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