#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();
}
}