plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

emenuhit.c (7160B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <draw.h>
      4 #include <event.h>
      5 
      6 enum
      7 {
      8 	Margin = 4,		/* outside to text */
      9 	Border = 2,		/* outside to selection boxes */
     10 	Blackborder = 2,	/* width of outlining border */
     11 	Vspacing = 2,		/* extra spacing between lines of text */
     12 	Maxunscroll = 25,	/* maximum #entries before scrolling turns on */
     13 	Nscroll = 20,		/* number entries in scrolling part */
     14 	Scrollwid = 14,		/* width of scroll bar */
     15 	Gap = 4			/* between text and scroll bar */
     16 };
     17 
     18 static	Image	*menutxt;
     19 static	Image	*back;
     20 static	Image	*high;
     21 static	Image	*bord;
     22 static	Image	*text;
     23 static	Image	*htext;
     24 
     25 static
     26 void
     27 menucolors(void)
     28 {
     29 	/* Main tone is greenish, with negative selection */
     30 	back = allocimagemix(display, DPalegreen, DWhite);
     31 	high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen);	/* dark green */
     32 	bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedgreen);	/* not as dark green */
     33 	if(back==nil || high==nil || bord==nil)
     34 		goto Error;
     35 	text = display->black;
     36 	htext = back;
     37 	return;
     38 
     39     Error:
     40 	freeimage(back);
     41 	freeimage(high);
     42 	freeimage(bord);
     43 	back = display->white;
     44 	high = display->black;
     45 	bord = display->black;
     46 	text = display->black;
     47 	htext = display->white;
     48 }
     49 
     50 /*
     51  * r is a rectangle holding the text elements.
     52  * return the rectangle, including its black edge, holding element i.
     53  */
     54 static Rectangle
     55 menurect(Rectangle r, int i)
     56 {
     57 	if(i < 0)
     58 		return Rect(0, 0, 0, 0);
     59 	r.min.y += (font->height+Vspacing)*i;
     60 	r.max.y = r.min.y+font->height+Vspacing;
     61 	return insetrect(r, Border-Margin);
     62 }
     63 
     64 /*
     65  * r is a rectangle holding the text elements.
     66  * return the element number containing p.
     67  */
     68 static int
     69 menusel(Rectangle r, Point p)
     70 {
     71 	r = insetrect(r, Margin);
     72 	if(!ptinrect(p, r))
     73 		return -1;
     74 	return (p.y-r.min.y)/(font->height+Vspacing);
     75 }
     76 
     77 static
     78 void
     79 paintitem(Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
     80 {
     81 	char *item;
     82 	Rectangle r;
     83 	Point pt;
     84 
     85 	if(i < 0)
     86 		return;
     87 	r = menurect(textr, i);
     88 	if(restore){
     89 		draw(screen, r, restore, nil, restore->r.min);
     90 		return;
     91 	}
     92 	if(save)
     93 		draw(save, save->r, screen, nil, r.min);
     94 	item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
     95 	pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
     96 	pt.y = textr.min.y+i*(font->height+Vspacing);
     97 	draw(screen, r, highlight? high : back, nil, pt);
     98 	string(screen, pt, highlight? htext : text, pt, font, item);
     99 }
    100 
    101 /*
    102  * menur is a rectangle holding all the highlightable text elements.
    103  * track mouse while inside the box, return what's selected when button
    104  * is raised, -1 as soon as it leaves box.
    105  * invariant: nothing is highlighted on entry or exit.
    106  */
    107 static int
    108 menuscan(Menu *menu, int but, Mouse *m, Rectangle textr, int off, int lasti, Image *save)
    109 {
    110 	int i;
    111 
    112 	paintitem(menu, textr, off, lasti, 1, save, nil);
    113 	flushimage(display, 1);	/* in case display->locking is set */
    114 	*m = emouse();
    115 	while(m->buttons & (1<<(but-1))){
    116 		flushimage(display, 1);	/* in case display->locking is set */
    117 		*m = emouse();
    118 		i = menusel(textr, m->xy);
    119 		if(i != -1 && i == lasti)
    120 			continue;
    121 		paintitem(menu, textr, off, lasti, 0, nil, save);
    122 		if(i == -1)
    123 			return i;
    124 		lasti = i;
    125 		paintitem(menu, textr, off, lasti, 1, save, nil);
    126 	}
    127 	return lasti;
    128 }
    129 
    130 static void
    131 menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn)
    132 {
    133 	int i;
    134 
    135 	draw(screen, insetrect(textr, Border-Margin), back, nil, ZP);
    136 	for(i = 0; i<nitemdrawn; i++)
    137 		paintitem(menu, textr, off, i, 0, nil, nil);
    138 }
    139 
    140 static void
    141 menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn)
    142 {
    143 	Rectangle r;
    144 
    145 	draw(screen, scrollr, back, nil, ZP);
    146 	r.min.x = scrollr.min.x;
    147 	r.max.x = scrollr.max.x;
    148 	r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
    149 	r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
    150 	if(r.max.y < r.min.y+2)
    151 		r.max.y = r.min.y+2;
    152 	border(screen, r, 1, bord, ZP);
    153 	if(menutxt == 0)
    154 		menutxt = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DDarkgreen);
    155 	if(menutxt)
    156 		draw(screen, insetrect(r, 1), menutxt, nil, ZP);
    157 }
    158 
    159 int
    160 emenuhit(int but, Mouse *m, Menu *menu)
    161 {
    162 	int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
    163 	int scrolling;
    164 	Rectangle r, menur, sc, textr, scrollr;
    165 	Image *b, *save;
    166 	Point pt;
    167 	char *item;
    168 
    169 	if(back == nil)
    170 		menucolors();
    171 	sc = screen->clipr;
    172 	replclipr(screen, 0, screen->r);
    173 	maxwid = 0;
    174 	for(nitem = 0;
    175 	    item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
    176 	    nitem++){
    177 		i = stringwidth(font, item);
    178 		if(i > maxwid)
    179 			maxwid = i;
    180 	}
    181 	if(menu->lasthit<0 || menu->lasthit>=nitem)
    182 		menu->lasthit = 0;
    183 	screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
    184 	if(nitem>Maxunscroll || nitem>screenitem){
    185 		scrolling = 1;
    186 		nitemdrawn = Nscroll;
    187 		if(nitemdrawn > screenitem)
    188 			nitemdrawn = screenitem;
    189 		wid = maxwid + Gap + Scrollwid;
    190 		off = menu->lasthit - nitemdrawn/2;
    191 		if(off < 0)
    192 			off = 0;
    193 		if(off > nitem-nitemdrawn)
    194 			off = nitem-nitemdrawn;
    195 		lasti = menu->lasthit-off;
    196 	}else{
    197 		scrolling = 0;
    198 		nitemdrawn = nitem;
    199 		wid = maxwid;
    200 		off = 0;
    201 		lasti = menu->lasthit;
    202 	}
    203 	r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
    204 	r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
    205 	r = rectaddpt(r, m->xy);
    206 	pt = ZP;
    207 	if(r.max.x>screen->r.max.x)
    208 		pt.x = screen->r.max.x-r.max.x;
    209 	if(r.max.y>screen->r.max.y)
    210 		pt.y = screen->r.max.y-r.max.y;
    211 	if(r.min.x<screen->r.min.x)
    212 		pt.x = screen->r.min.x-r.min.x;
    213 	if(r.min.y<screen->r.min.y)
    214 		pt.y = screen->r.min.y-r.min.y;
    215 	menur = rectaddpt(r, pt);
    216 	textr.max.x = menur.max.x-Margin;
    217 	textr.min.x = textr.max.x-maxwid;
    218 	textr.min.y = menur.min.y+Margin;
    219 	textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
    220 	if(scrolling){
    221 		scrollr = insetrect(menur, Border);
    222 		scrollr.max.x = scrollr.min.x+Scrollwid;
    223 	}else
    224 		scrollr = Rect(0, 0, 0, 0);
    225 
    226 	b = allocimage(display, menur, screen->chan, 0, 0);
    227 	if(b == 0)
    228 		b = screen;
    229 	draw(b, menur, screen, nil, menur.min);
    230 	draw(screen, menur, back, nil, ZP);
    231 	border(screen, menur, Blackborder, bord, ZP);
    232 	save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
    233 	r = menurect(textr, lasti);
    234 	emoveto(divpt(addpt(r.min, r.max), 2));
    235 	menupaint(menu, textr, off, nitemdrawn);
    236 	if(scrolling)
    237 		menuscrollpaint(scrollr, off, nitem, nitemdrawn);
    238 	while(m->buttons & (1<<(but-1))){
    239 		lasti = menuscan(menu, but, m, textr, off, lasti, save);
    240 		if(lasti >= 0)
    241 			break;
    242 		while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
    243 			if(scrolling && ptinrect(m->xy, scrollr)){
    244 				noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
    245 				noff -= nitemdrawn/2;
    246 				if(noff < 0)
    247 					noff = 0;
    248 				if(noff > nitem-nitemdrawn)
    249 					noff = nitem-nitemdrawn;
    250 				if(noff != off){
    251 					off = noff;
    252 					menupaint(menu, textr, off, nitemdrawn);
    253 					menuscrollpaint(scrollr, off, nitem, nitemdrawn);
    254 				}
    255 			}
    256 			flushimage(display, 1);	/* in case display->locking is set */
    257 			*m = emouse();
    258 		}
    259 	}
    260 	draw(screen, menur, b, nil, menur.min);
    261 	if(b != screen)
    262 		freeimage(b);
    263 	freeimage(save);
    264 	replclipr(screen, 0, sc);
    265 	flushimage(display, 1);
    266 	if(lasti >= 0){
    267 		menu->lasthit = lasti+off;
    268 		return menu->lasthit;
    269 	}
    270 	return -1;
    271 }