17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <draw.h>
47dd7cddfSDavid du Colombier #include <event.h>
57dd7cddfSDavid du Colombier
67dd7cddfSDavid du Colombier enum
77dd7cddfSDavid du Colombier {
87dd7cddfSDavid du Colombier Margin = 4, /* outside to text */
97dd7cddfSDavid du Colombier Border = 2, /* outside to selection boxes */
107dd7cddfSDavid du Colombier Blackborder = 2, /* width of outlining border */
117dd7cddfSDavid du Colombier Vspacing = 2, /* extra spacing between lines of text */
127dd7cddfSDavid du Colombier Maxunscroll = 25, /* maximum #entries before scrolling turns on */
137dd7cddfSDavid du Colombier Nscroll = 20, /* number entries in scrolling part */
147dd7cddfSDavid du Colombier Scrollwid = 14, /* width of scroll bar */
157dd7cddfSDavid du Colombier Gap = 4, /* between text and scroll bar */
167dd7cddfSDavid du Colombier };
177dd7cddfSDavid du Colombier
187dd7cddfSDavid du Colombier static Image *menutxt;
197dd7cddfSDavid du Colombier static Image *back;
207dd7cddfSDavid du Colombier static Image *high;
217dd7cddfSDavid du Colombier static Image *bord;
227dd7cddfSDavid du Colombier static Image *text;
237dd7cddfSDavid du Colombier static Image *htext;
247dd7cddfSDavid du Colombier
257dd7cddfSDavid du Colombier static
267dd7cddfSDavid du Colombier void
menucolors(void)277dd7cddfSDavid du Colombier menucolors(void)
287dd7cddfSDavid du Colombier {
297dd7cddfSDavid du Colombier /* Main tone is greenish, with negative selection */
307dd7cddfSDavid du Colombier back = allocimagemix(display, DPalegreen, DWhite);
317dd7cddfSDavid du Colombier high = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DDarkgreen); /* dark green */
327dd7cddfSDavid du Colombier bord = allocimage(display, Rect(0,0,1,1), CMAP8, 1, DMedgreen); /* not as dark green */
337dd7cddfSDavid du Colombier if(back==nil || high==nil || bord==nil)
347dd7cddfSDavid du Colombier goto Error;
357dd7cddfSDavid du Colombier text = display->black;
367dd7cddfSDavid du Colombier htext = back;
377dd7cddfSDavid du Colombier return;
387dd7cddfSDavid du Colombier
397dd7cddfSDavid du Colombier Error:
407dd7cddfSDavid du Colombier freeimage(back);
417dd7cddfSDavid du Colombier freeimage(high);
427dd7cddfSDavid du Colombier freeimage(bord);
437dd7cddfSDavid du Colombier back = display->white;
447dd7cddfSDavid du Colombier high = display->black;
457dd7cddfSDavid du Colombier bord = display->black;
467dd7cddfSDavid du Colombier text = display->black;
477dd7cddfSDavid du Colombier htext = display->white;
487dd7cddfSDavid du Colombier }
497dd7cddfSDavid du Colombier
507dd7cddfSDavid du Colombier /*
517dd7cddfSDavid du Colombier * r is a rectangle holding the text elements.
527dd7cddfSDavid du Colombier * return the rectangle, including its black edge, holding element i.
537dd7cddfSDavid du Colombier */
547dd7cddfSDavid du Colombier static Rectangle
menurect(Rectangle r,int i)557dd7cddfSDavid du Colombier menurect(Rectangle r, int i)
567dd7cddfSDavid du Colombier {
577dd7cddfSDavid du Colombier if(i < 0)
587dd7cddfSDavid du Colombier return Rect(0, 0, 0, 0);
597dd7cddfSDavid du Colombier r.min.y += (font->height+Vspacing)*i;
607dd7cddfSDavid du Colombier r.max.y = r.min.y+font->height+Vspacing;
617dd7cddfSDavid du Colombier return insetrect(r, Border-Margin);
627dd7cddfSDavid du Colombier }
637dd7cddfSDavid du Colombier
647dd7cddfSDavid du Colombier /*
657dd7cddfSDavid du Colombier * r is a rectangle holding the text elements.
667dd7cddfSDavid du Colombier * return the element number containing p.
677dd7cddfSDavid du Colombier */
687dd7cddfSDavid du Colombier static int
menusel(Rectangle r,Point p)697dd7cddfSDavid du Colombier menusel(Rectangle r, Point p)
707dd7cddfSDavid du Colombier {
717dd7cddfSDavid du Colombier r = insetrect(r, Margin);
727dd7cddfSDavid du Colombier if(!ptinrect(p, r))
737dd7cddfSDavid du Colombier return -1;
747dd7cddfSDavid du Colombier return (p.y-r.min.y)/(font->height+Vspacing);
757dd7cddfSDavid du Colombier }
767dd7cddfSDavid du Colombier
777dd7cddfSDavid du Colombier static
787dd7cddfSDavid du Colombier void
paintitem(Menu * menu,Rectangle textr,int off,int i,int highlight,Image * save,Image * restore)797dd7cddfSDavid du Colombier paintitem(Menu *menu, Rectangle textr, int off, int i, int highlight, Image *save, Image *restore)
807dd7cddfSDavid du Colombier {
817dd7cddfSDavid du Colombier char *item;
827dd7cddfSDavid du Colombier Rectangle r;
837dd7cddfSDavid du Colombier Point pt;
847dd7cddfSDavid du Colombier
857dd7cddfSDavid du Colombier if(i < 0)
867dd7cddfSDavid du Colombier return;
877dd7cddfSDavid du Colombier r = menurect(textr, i);
887dd7cddfSDavid du Colombier if(restore){
897dd7cddfSDavid du Colombier draw(screen, r, restore, nil, restore->r.min);
907dd7cddfSDavid du Colombier return;
917dd7cddfSDavid du Colombier }
927dd7cddfSDavid du Colombier if(save)
937dd7cddfSDavid du Colombier draw(save, save->r, screen, nil, r.min);
947dd7cddfSDavid du Colombier item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
957dd7cddfSDavid du Colombier pt.x = (textr.min.x+textr.max.x-stringwidth(font, item))/2;
967dd7cddfSDavid du Colombier pt.y = textr.min.y+i*(font->height+Vspacing);
977dd7cddfSDavid du Colombier draw(screen, r, highlight? high : back, nil, pt);
987dd7cddfSDavid du Colombier string(screen, pt, highlight? htext : text, pt, font, item);
997dd7cddfSDavid du Colombier }
1007dd7cddfSDavid du Colombier
1017dd7cddfSDavid du Colombier /*
1027dd7cddfSDavid du Colombier * menur is a rectangle holding all the highlightable text elements.
1037dd7cddfSDavid du Colombier * track mouse while inside the box, return what's selected when button
1047dd7cddfSDavid du Colombier * is raised, -1 as soon as it leaves box.
1057dd7cddfSDavid du Colombier * invariant: nothing is highlighted on entry or exit.
1067dd7cddfSDavid du Colombier */
1077dd7cddfSDavid du Colombier static int
menuscan(Menu * menu,int but,Mouse * m,Rectangle textr,int off,int lasti,Image * save)1087dd7cddfSDavid du Colombier menuscan(Menu *menu, int but, Mouse *m, Rectangle textr, int off, int lasti, Image *save)
1097dd7cddfSDavid du Colombier {
1107dd7cddfSDavid du Colombier int i;
1117dd7cddfSDavid du Colombier
1127dd7cddfSDavid du Colombier paintitem(menu, textr, off, lasti, 1, save, nil);
1137dd7cddfSDavid du Colombier flushimage(display, 1); /* in case display->locking is set */
1147dd7cddfSDavid du Colombier *m = emouse();
1157dd7cddfSDavid du Colombier while(m->buttons & (1<<(but-1))){
1167dd7cddfSDavid du Colombier flushimage(display, 1); /* in case display->locking is set */
1177dd7cddfSDavid du Colombier *m = emouse();
1187dd7cddfSDavid du Colombier i = menusel(textr, m->xy);
1197dd7cddfSDavid du Colombier if(i != -1 && i == lasti)
1207dd7cddfSDavid du Colombier continue;
1217dd7cddfSDavid du Colombier paintitem(menu, textr, off, lasti, 0, nil, save);
1227dd7cddfSDavid du Colombier if(i == -1)
1237dd7cddfSDavid du Colombier return i;
1247dd7cddfSDavid du Colombier lasti = i;
1257dd7cddfSDavid du Colombier paintitem(menu, textr, off, lasti, 1, save, nil);
1267dd7cddfSDavid du Colombier }
1277dd7cddfSDavid du Colombier return lasti;
1287dd7cddfSDavid du Colombier }
1297dd7cddfSDavid du Colombier
1307dd7cddfSDavid du Colombier static void
menupaint(Menu * menu,Rectangle textr,int off,int nitemdrawn)1317dd7cddfSDavid du Colombier menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn)
1327dd7cddfSDavid du Colombier {
1337dd7cddfSDavid du Colombier int i;
1347dd7cddfSDavid du Colombier
1357dd7cddfSDavid du Colombier draw(screen, insetrect(textr, Border-Margin), back, nil, ZP);
1367dd7cddfSDavid du Colombier for(i = 0; i<nitemdrawn; i++)
1377dd7cddfSDavid du Colombier paintitem(menu, textr, off, i, 0, nil, nil);
1387dd7cddfSDavid du Colombier }
1397dd7cddfSDavid du Colombier
1407dd7cddfSDavid du Colombier static void
menuscrollpaint(Rectangle scrollr,int off,int nitem,int nitemdrawn)1417dd7cddfSDavid du Colombier menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn)
1427dd7cddfSDavid du Colombier {
1437dd7cddfSDavid du Colombier Rectangle r;
1447dd7cddfSDavid du Colombier
1457dd7cddfSDavid du Colombier draw(screen, scrollr, back, nil, ZP);
1467dd7cddfSDavid du Colombier r.min.x = scrollr.min.x;
1477dd7cddfSDavid du Colombier r.max.x = scrollr.max.x;
1487dd7cddfSDavid du Colombier r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
1497dd7cddfSDavid du Colombier r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
1507dd7cddfSDavid du Colombier if(r.max.y < r.min.y+2)
1517dd7cddfSDavid du Colombier r.max.y = r.min.y+2;
1527dd7cddfSDavid du Colombier border(screen, r, 1, bord, ZP);
1537dd7cddfSDavid du Colombier if(menutxt == 0)
1547dd7cddfSDavid du Colombier menutxt = allocimage(display, Rect(0, 0, 1, 1), CMAP8, 1, DDarkgreen);
1557dd7cddfSDavid du Colombier if(menutxt)
1567dd7cddfSDavid du Colombier draw(screen, insetrect(r, 1), menutxt, nil, ZP);
1577dd7cddfSDavid du Colombier }
1587dd7cddfSDavid du Colombier
1597dd7cddfSDavid du Colombier int
emenuhit(int but,Mouse * m,Menu * menu)1607dd7cddfSDavid du Colombier emenuhit(int but, Mouse *m, Menu *menu)
1617dd7cddfSDavid du Colombier {
1627dd7cddfSDavid du Colombier int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
1637dd7cddfSDavid du Colombier int scrolling;
1647dd7cddfSDavid du Colombier Rectangle r, menur, sc, textr, scrollr;
1657dd7cddfSDavid du Colombier Image *b, *save;
1667dd7cddfSDavid du Colombier Point pt;
1677dd7cddfSDavid du Colombier char *item;
1687dd7cddfSDavid du Colombier
1697dd7cddfSDavid du Colombier if(back == nil)
1707dd7cddfSDavid du Colombier menucolors();
1717dd7cddfSDavid du Colombier sc = screen->clipr;
1727dd7cddfSDavid du Colombier replclipr(screen, 0, screen->r);
1737dd7cddfSDavid du Colombier maxwid = 0;
1747dd7cddfSDavid du Colombier for(nitem = 0;
1757dd7cddfSDavid du Colombier item = menu->item? menu->item[nitem] : (*menu->gen)(nitem);
1767dd7cddfSDavid du Colombier nitem++){
1777dd7cddfSDavid du Colombier i = stringwidth(font, item);
1787dd7cddfSDavid du Colombier if(i > maxwid)
1797dd7cddfSDavid du Colombier maxwid = i;
1807dd7cddfSDavid du Colombier }
1817dd7cddfSDavid du Colombier if(menu->lasthit<0 || menu->lasthit>=nitem)
1827dd7cddfSDavid du Colombier menu->lasthit = 0;
1837dd7cddfSDavid du Colombier screenitem = (Dy(screen->r)-10)/(font->height+Vspacing);
1847dd7cddfSDavid du Colombier if(nitem>Maxunscroll || nitem>screenitem){
1857dd7cddfSDavid du Colombier scrolling = 1;
1867dd7cddfSDavid du Colombier nitemdrawn = Nscroll;
1877dd7cddfSDavid du Colombier if(nitemdrawn > screenitem)
1887dd7cddfSDavid du Colombier nitemdrawn = screenitem;
1897dd7cddfSDavid du Colombier wid = maxwid + Gap + Scrollwid;
1907dd7cddfSDavid du Colombier off = menu->lasthit - nitemdrawn/2;
1917dd7cddfSDavid du Colombier if(off < 0)
1927dd7cddfSDavid du Colombier off = 0;
1937dd7cddfSDavid du Colombier if(off > nitem-nitemdrawn)
1947dd7cddfSDavid du Colombier off = nitem-nitemdrawn;
1957dd7cddfSDavid du Colombier lasti = menu->lasthit-off;
1967dd7cddfSDavid du Colombier }else{
1977dd7cddfSDavid du Colombier scrolling = 0;
1987dd7cddfSDavid du Colombier nitemdrawn = nitem;
1997dd7cddfSDavid du Colombier wid = maxwid;
2007dd7cddfSDavid du Colombier off = 0;
2017dd7cddfSDavid du Colombier lasti = menu->lasthit;
2027dd7cddfSDavid du Colombier }
2037dd7cddfSDavid du Colombier r = insetrect(Rect(0, 0, wid, nitemdrawn*(font->height+Vspacing)), -Margin);
2047dd7cddfSDavid du Colombier r = rectsubpt(r, Pt(wid/2, lasti*(font->height+Vspacing)+font->height/2));
2057dd7cddfSDavid du Colombier r = rectaddpt(r, m->xy);
2067dd7cddfSDavid du Colombier pt = ZP;
2077dd7cddfSDavid du Colombier if(r.max.x>screen->r.max.x)
2087dd7cddfSDavid du Colombier pt.x = screen->r.max.x-r.max.x;
2097dd7cddfSDavid du Colombier if(r.max.y>screen->r.max.y)
2107dd7cddfSDavid du Colombier pt.y = screen->r.max.y-r.max.y;
2117dd7cddfSDavid du Colombier if(r.min.x<screen->r.min.x)
2127dd7cddfSDavid du Colombier pt.x = screen->r.min.x-r.min.x;
2137dd7cddfSDavid du Colombier if(r.min.y<screen->r.min.y)
2147dd7cddfSDavid du Colombier pt.y = screen->r.min.y-r.min.y;
2157dd7cddfSDavid du Colombier menur = rectaddpt(r, pt);
2167dd7cddfSDavid du Colombier textr.max.x = menur.max.x-Margin;
2177dd7cddfSDavid du Colombier textr.min.x = textr.max.x-maxwid;
2187dd7cddfSDavid du Colombier textr.min.y = menur.min.y+Margin;
2197dd7cddfSDavid du Colombier textr.max.y = textr.min.y + nitemdrawn*(font->height+Vspacing);
2207dd7cddfSDavid du Colombier if(scrolling){
2217dd7cddfSDavid du Colombier scrollr = insetrect(menur, Border);
2227dd7cddfSDavid du Colombier scrollr.max.x = scrollr.min.x+Scrollwid;
2237dd7cddfSDavid du Colombier }else
2247dd7cddfSDavid du Colombier scrollr = Rect(0, 0, 0, 0);
2257dd7cddfSDavid du Colombier
2267dd7cddfSDavid du Colombier b = allocimage(display, menur, screen->chan, 0, 0);
2277dd7cddfSDavid du Colombier if(b == 0)
2287dd7cddfSDavid du Colombier b = screen;
2297dd7cddfSDavid du Colombier draw(b, menur, screen, nil, menur.min);
2307dd7cddfSDavid du Colombier draw(screen, menur, back, nil, ZP);
2317dd7cddfSDavid du Colombier border(screen, menur, Blackborder, bord, ZP);
232*9a747e4fSDavid du Colombier save = allocimage(display, menurect(textr, 0), screen->chan, 0, -1);
2337dd7cddfSDavid du Colombier r = menurect(textr, lasti);
2347dd7cddfSDavid du Colombier emoveto(divpt(addpt(r.min, r.max), 2));
2357dd7cddfSDavid du Colombier menupaint(menu, textr, off, nitemdrawn);
2367dd7cddfSDavid du Colombier if(scrolling)
2377dd7cddfSDavid du Colombier menuscrollpaint(scrollr, off, nitem, nitemdrawn);
2387dd7cddfSDavid du Colombier while(m->buttons & (1<<(but-1))){
2397dd7cddfSDavid du Colombier lasti = menuscan(menu, but, m, textr, off, lasti, save);
2407dd7cddfSDavid du Colombier if(lasti >= 0)
2417dd7cddfSDavid du Colombier break;
2427dd7cddfSDavid du Colombier while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
2437dd7cddfSDavid du Colombier if(scrolling && ptinrect(m->xy, scrollr)){
2447dd7cddfSDavid du Colombier noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
2457dd7cddfSDavid du Colombier noff -= nitemdrawn/2;
2467dd7cddfSDavid du Colombier if(noff < 0)
2477dd7cddfSDavid du Colombier noff = 0;
2487dd7cddfSDavid du Colombier if(noff > nitem-nitemdrawn)
2497dd7cddfSDavid du Colombier noff = nitem-nitemdrawn;
2507dd7cddfSDavid du Colombier if(noff != off){
2517dd7cddfSDavid du Colombier off = noff;
2527dd7cddfSDavid du Colombier menupaint(menu, textr, off, nitemdrawn);
2537dd7cddfSDavid du Colombier menuscrollpaint(scrollr, off, nitem, nitemdrawn);
2547dd7cddfSDavid du Colombier }
2557dd7cddfSDavid du Colombier }
2567dd7cddfSDavid du Colombier flushimage(display, 1); /* in case display->locking is set */
2577dd7cddfSDavid du Colombier *m = emouse();
2587dd7cddfSDavid du Colombier }
2597dd7cddfSDavid du Colombier }
2607dd7cddfSDavid du Colombier draw(screen, menur, b, nil, menur.min);
2617dd7cddfSDavid du Colombier if(b != screen)
2627dd7cddfSDavid du Colombier freeimage(b);
2637dd7cddfSDavid du Colombier freeimage(save);
2647dd7cddfSDavid du Colombier replclipr(screen, 0, sc);
2657dd7cddfSDavid du Colombier if(lasti >= 0){
2667dd7cddfSDavid du Colombier menu->lasthit = lasti+off;
2677dd7cddfSDavid du Colombier return menu->lasthit;
2687dd7cddfSDavid du Colombier }
2697dd7cddfSDavid du Colombier return -1;
2707dd7cddfSDavid du Colombier }
271