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