#include #include /************************************************************* * parameters */ displayitem *items = (displayitem *)[]; int defaultchoice = -1; name choicemode = /ExclusiveVariation; int *selection = (int *)[]; int topitem = 0; persist(items); persist(defaultchoice); persist(choicemode); persist(scrolllinedelay); persist(scrollthreshold); persist(scrollautorepeat); /************************************************************* * dimensions */ #define SCROLLGAP 4 #define SCROLLMAR (OLScrollW + SCROLLGAP) #define MAR 8 #define IMAR 4 int itemtopy() { return h - MAR; } float itemheight() { return fontheight(textfont) + 8; } int viewsize() { return max(0,(itemtopy - MAR) / itemheight); } int itemareawidth() { return w - SCROLLMAR; } int itemrectwidth() { return w - (SCROLLMAR + MAR*2); } int itemrectx() { return MAR; } int itemrecty(int n) { return itemtopy - ((n + 1 - topitem) * itemheight); } int itemx() { return MAR + IMAR; } int itemwidth() { return w - (SCROLLMAR + MAR*2 + IMAR*2); } float minwidth() { return SCROLLMAR + MAR*2 + IMAR*2 + 10; } float minheight() { return itemheight + MAR*2; } float preferredheight() { return (nritems * itemheight) + MAR*2; } float preferredwidth() { int i, W = 40; gsave(); setfont(textfont); for (i = 0 ; i < nritems ; i++) W = max(W,DIWidth(itemlabel(i))+10); grestore(); return W + SCROLLMAR + MAR*2 + IMAR*2; } /************************************************************* * access */ int nritems() { return length(items); } boolean itemselected(int n) { return (n < nritems) ? arraycontains(selection,n) : false; } displayitem itemlabel(int n) { return (n < nritems) ? items[n] : null; } /************************************************************* * painting */ void PaintBackground() { /*OLBox(true,0,0,itemareawidth,h);*/ OLRect(false,0,0,itemareawidth,h); rectpath(itemareawidth,0,SCROLLGAP,h); fill(BG); } void PaintItem(int n) { int Y; if ((n < topitem) || ((n - topitem) >= viewsize)) return; Y = itemrecty(n); if (n >= nritems) { rectpath(itemrectx,Y,itemrectwidth,itemheight); fill(BG); return; } if (itemselected(n)) OLRect(true,itemrectx,Y,itemrectwidth,itemheight); else { rectpath(itemrectx,Y,itemrectwidth,itemheight); fill(BG); if (n == defaultchoice) { rectpath(insetstroke(1,[itemrectx,Y,itemrectwidth,itemheight])); stroke(BG2); } } setfont(textfont); setcolor(FG); OLShow(itemlabel(n),itemx,Y+2,itemwidth,itemheight-4); } void PaintItems() { int i, n; for (i = topitem, n = viewsize ; n-- ;) PaintItem(i++); } void Paint() { PaintBackground(); PaintItems(); } /************************************************************* * preview/action */ void preview() { Send Preview(selected_items()); } void action() { Send Action(selected_strings()); } void action2() { Send Action2(selected_strings()); } /************************************************************* * selection */ void setselection0(int *newsel) { int *oldsel, i; oldsel = selection; unpromote(/selection); forall (; i ; newsel) if ((i >= 0) && (i < nritems) && (!arraycontains(selection,i))) selection = (int *)append(selection,[i]); if (nritems) { switch (choicemode) { case /Exclusive: if (length(selection) == 0) selection = [defaultchoice]; else if (length(selection) > 1) selection = [selection[0]]; break; case /ExclusiveVariation: if (length(selection) > 1) selection = [selection[0]]; break; default: break; } } if (cmp(oldsel,selection)) return; forall( ; i ; oldsel) if (!arraycontains(selection,i)) PaintItem(i); forall( ; i ; selection) if (!arraycontains(oldsel,i)) PaintItem(i); preview(); } void setselection(int *newsel) { gexeca(/setselection0,[newsel]); } /************************************************************* * scrolling */ void Validate() { Send setparameters(topitem,viewsize,nritems-1) to /@scroll; } void scrollitems(int newtop) { int H = viewsize * itemheight; int t, f; rectpath(itemrectx,itemtopy - H,itemrectwidth,H); clip(); copyarea(0,(newtop - topitem) * itemheight); if (newtop > topitem) { f = max(newtop,topitem + viewsize); t = newtop + viewsize(); topitem = newtop; } else { f = newtop; t = min(f + viewsize,topitem); topitem = newtop; } for (; f < t ; f++) PaintItem(f); Send setvalue(newtop) to /@scroll; } void settop(int n) { n = max(0,min(n,nritems-1)); if (n != topitem) gexeca(/scrollitems,[n]); } /************************************************************* * selection */ int *tracksel = (int *)[]; void track_exclusive() { int i; if (track_is_timed && scrollautorepeat) { if (tracky > h) settop(topitem - 1); else if ((tracky < 0) && ((topitem + viewsize) < nritems)) settop(topitem + 1); } else if ((trackx < itemrectx) || (trackx > (itemrectx + itemrectwidth))) setselection(tracksel); else { i = min(nritems-1,max(0,topitem + ((itemtopy - tracky) / itemheight))); setselection([i]); } } void track_nonexclusive() { int i, j; if (track_is_timed && scrollautorepeat) { if (tracky > h) settop(topitem - 1); else if ((tracky < 0) && ((topitem + viewsize) < nritems)) settop(topitem + 1); } else if ((trackx < itemrectx) || (trackx > (itemrectx + itemrectwidth))) setselection(tracksel); else { i = min(nritems-1,max(0,topitem + ((itemtopy - tracky) / itemheight))); j = arrayindex(tracksel,i); if (j >= 0) setselection((int *)arraydelete(tracksel,j)); else setselection((int *)append([i],tracksel)); } } void OnSelect() { int i; tracksel = selection; switch (choicemode) { case /Exclusive: if ((mouselevel == 2) && (track_state() == /mouse_click)) { i = topitem + ((itemtopy - mouseevent.YLocation) / itemheight); setselection([i]); action2(); } else track_time(scrollthreshold,scrolllinedelay,/track_exclusive); break; default: track_time(scrollthreshold,scrolllinedelay,/track_nonexclusive); break; } if (!cmp(tracksel,selection)) action(); unpromote(/tracksel); } /************************************************************* * access */ void setdefaultchoice(int n) { int olddefaultchoice; if (choicemode == /Exclusive) n = max(0,min(n,nritems-1)); else n = max(-1,min(n,nritems-1)); if (defaultchoice != n) { olddefaultchoice = defaultchoice; softpromote(/defaultchoice,n); selection = [defaultchoice]; gexeca(/PaintItem,[olddefaultchoice]); gexeca(/PaintItem,[defaultchoice]); } } void setitems(displayitem *list) { topitem = 0; items = list; if (choicemode == /Exclusive) { softpromote(/defaultchoice,max(0,min(defaultchoice,nritems-1))); selection = [defaultchoice]; } else unpromote(/selection); gexec(/PaintItems); Validate(); preview(); } void setchoicemode(name mode) { if (mode != choicemode) { choicemode = mode; if (choicemode == /Exclusive) setdefaultchoice(defaultchoice); setselection(selection); } } char **selected_strings() { int i; char **s = (char **)[]; forall(; i ; selection) s = (char **)append(s,[DIString(itemlabel(i))]); return s; } int *selected_items() { return (int *)copy(selection,array(length(selection))); } /************************************************************* * scrolling */ void OnInit() { olscrollbar s; if (!promoted(/@scroll)) { s = (olscrollbar)newmember(/olscrollbar); s.rename(/@scroll); s.setscript("","/Preview {[exch] /settop @parent Send} def\n/Action /pop load def"); s.setposition(null,0,0,0); s.map(); } }