menuhit.c - sam - An updated version of the sam text editor.
 (HTM) git clone git://vernunftzentrum.de/sam.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) LICENSE
       ---
       menuhit.c (6868B)
       ---
            1 /* Copyright (c) 1998 Lucent Technologies - All rights reserved. */
            2 #include <u.h>
            3 #include <libg.h>
            4 #include "libgint.h"
            5 
            6 enum
            7 {
            8     Margin = 3,     /* outside to text */
            9     Border = 2,     /* outside to selection boxes */
           10     Blackborder = 1,    /* width of outlining border */
           11     Vspacing = 1,       /* 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 extern Bitmap *darkgrey;
           19 
           20 static int
           21 fontheight() {
           22     return font->ascent + font->descent;
           23 }
           24 
           25 /*
           26  * r is a rectangle holding the text elements.
           27  * return the rectangle, including its black edge, holding element i.
           28  */
           29 static Rectangle
           30 menurect(Rectangle r, int i)
           31 {
           32     if(i < 0)
           33         return Rect(0, 0, 0, 0);
           34     r.min.y += (fontheight()+Vspacing)*i;
           35     r.max.y = r.min.y+fontheight()+Vspacing;
           36     return inset(r, Border-Margin);
           37 }
           38 
           39 /*
           40  * r is a rectangle holding the text elements.
           41  * return the element number containing p.
           42  */
           43 static int
           44 menusel(Rectangle r, Point p)
           45 {
           46     if(!ptinrect(p, r))
           47         return -1;
           48     return (p.y-r.min.y)/(fontheight()+Vspacing);
           49 }
           50 
           51 /*
           52  * menur is a rectangle holding all the highlightable text elements.
           53  * track mouse while inside the box, return what's selected when button
           54  * is raised, -1 as soon as it leaves box.
           55  * invariant: nothing is highlighted on entry or exit.
           56  */
           57 static int
           58 menuscan(int but, Mouse *m, Rectangle menur, int lasti)
           59 {
           60     int i;
           61     Rectangle r;
           62 
           63     r = menurect(menur, lasti);
           64     bitblt(&screen, r.min, &screen, r, F&~D);
           65     *m = emouse();
           66     while(m->buttons & (1<<(but-1))){
           67         *m = emouse();
           68         i = menusel(menur, m->xy);
           69         if(i == lasti)
           70             continue;
           71         bitblt(&screen, r.min, &screen, r, F&~D);
           72         if(i == -1)
           73             return i;
           74         r = menurect(menur, i);
           75         bitblt(&screen, r.min, &screen, r, F&~D);
           76         lasti = i;
           77     }
           78     return lasti;
           79 }
           80 
           81 void
           82 menupaint(Menu *menu, Rectangle textr, int off, int nitemdrawn)
           83 {
           84     int i;
           85     Point pt;
           86     Rectangle r;
           87     char *item;
           88 
           89     r = inset(textr, Border-Margin);
           90     bitblt(&screen, r.min, &screen, r, 0);
           91     pt = Pt(textr.min.x+textr.max.x, textr.min.y);
           92     for(i = 0; i<nitemdrawn; i++, pt.y += fontheight()+Vspacing){
           93         item = menu->item? menu->item[i+off] : (*menu->gen)(i+off);
           94         string(&screen,
           95             Pt((pt.x-strwidth(font, item))/2, pt.y),
           96             font, item, S);
           97     }
           98 }
           99 
          100 static void
          101 menuscrollpaint(Rectangle scrollr, int off, int nitem, int nitemdrawn)
          102 {
          103     Rectangle r;
          104 
          105     bitblt(&screen, scrollr.min, &screen, scrollr, 0);
          106     r.min.x = scrollr.min.x;
          107     r.max.x = scrollr.max.x;
          108     r.min.y = scrollr.min.y + (Dy(scrollr)*off)/nitem;
          109     r.max.y = scrollr.min.y + (Dy(scrollr)*(off+nitemdrawn))/nitem;
          110     if(r.max.y < r.min.y+2)
          111         r.max.y = r.min.y+2;
          112     border(&screen, r, 1, F, _bgpixel);
          113     if(darkgrey)
          114         texture(&screen, inset(r, 1), darkgrey, S);
          115 }
          116 
          117 int
          118 menuhit(int but, Mouse *m, Menu *menu)
          119 {
          120     int i, nitem, nitemdrawn, maxwid, lasti, off, noff, wid, screenitem;
          121     bool scrolling;
          122     Rectangle r, menur, sc, textr, scrollr;
          123     Bitmap *b;
          124     Point pt;
          125     char *item;
          126     extern unsigned int cursor;
          127     unsigned int oldcursor = cursor;
          128 
          129     cursorswitch(ArrowCursor);
          130     sc = screen.clipr;
          131     clipr(&screen, screen.r);
          132     maxwid = 0;
          133     for(nitem = 0;
          134         (item = menu->item? menu->item[nitem] : (*menu->gen)(nitem));
          135         nitem++){
          136         i = strwidth(font, item);
          137         if(i > maxwid)
          138             maxwid = i;
          139     }
          140     if(menu->lasthit<0 || menu->lasthit>=nitem)
          141         menu->lasthit = 0;
          142     screenitem = (Dy(screen.r)-10)/(fontheight()+Vspacing);
          143     if(nitem>Maxunscroll || nitem>screenitem){
          144         scrolling = true;
          145         nitemdrawn = Nscroll;
          146         if(nitemdrawn > screenitem)
          147             nitemdrawn = screenitem;
          148         wid = maxwid + Gap + Scrollwid;
          149         off = menu->lasthit - nitemdrawn/2;
          150         if(off < 0)
          151             off = 0;
          152         if(off > nitem-nitemdrawn)
          153             off = nitem-nitemdrawn;
          154         lasti = menu->lasthit-off;
          155     }else{
          156         scrolling = false;
          157         nitemdrawn = nitem;
          158         wid = maxwid;
          159         off = 0;
          160         lasti = menu->lasthit;
          161     }
          162     r = inset(Rect(0, 0, wid, nitemdrawn*(fontheight()+Vspacing)), -Margin);
          163     r = rsubp(r, Pt(wid/2, lasti*(fontheight()+Vspacing)+fontheight()/2));
          164     r = raddp(r, m->xy);
          165     pt = Pt(0, 0);
          166     if(r.max.x>screen.r.max.x)
          167         pt.x = screen.r.max.x-r.max.x;
          168     if(r.max.y>screen.r.max.y)
          169         pt.y = screen.r.max.y-r.max.y;
          170     if(r.min.x<screen.r.min.x)
          171         pt.x = screen.r.min.x-r.min.x;
          172     if(r.min.y<screen.r.min.y)
          173         pt.y = screen.r.min.y-r.min.y;
          174     menur = raddp(r, pt);
          175     textr.max.x = menur.max.x-Margin;
          176     textr.min.x = textr.max.x-maxwid;
          177     textr.min.y = menur.min.y+Margin;
          178     textr.max.y = textr.min.y + nitemdrawn*(fontheight()+Vspacing);
          179     if(scrolling){
          180         scrollr = inset(menur, Border);
          181         scrollr.max.x = scrollr.min.x+Scrollwid;
          182     }else
          183         scrollr = Rect(0, 0, 0, 0);
          184 
          185     b = balloc(menur, screen.ldepth);
          186     if(b == 0)
          187         b = &screen;
          188     bitblt(b, menur.min, &screen, menur, S);
          189     bitblt(&screen, menur.min, &screen, menur, 0);
          190     border(&screen, menur, Blackborder, F, _bgpixel);
          191     r = menurect(textr, lasti);
          192     cursorset(divpt(add(r.min, r.max), 2));
          193     menupaint(menu, textr, off, nitemdrawn);
          194     if(scrolling)
          195         menuscrollpaint(scrollr, off, nitem, nitemdrawn);
          196     r = menurect(textr, lasti);
          197     cursorset(divpt(add(r.min, r.max), 2));
          198     menupaint(menu, textr, off, nitemdrawn);
          199     if(scrolling)
          200         menuscrollpaint(scrollr, off, nitem, nitemdrawn);
          201     while(m->buttons & (1<<(but-1))){
          202         lasti = menuscan(but, m, textr, lasti);
          203         if(lasti >= 0)
          204             break;
          205         while(!ptinrect(m->xy, textr) && (m->buttons & (1<<(but-1)))){
          206             if(scrolling && ptinrect(m->xy, scrollr)){
          207                 noff = ((m->xy.y-scrollr.min.y)*nitem)/Dy(scrollr);
          208                 noff -= nitemdrawn/2;
          209                 if(noff < 0)
          210                     noff = 0;
          211                 if(noff > nitem-nitemdrawn)
          212                     noff = nitem-nitemdrawn;
          213                 if(noff != off){
          214                     off = noff;
          215                     menupaint(menu, textr, off, nitemdrawn);
          216                     menuscrollpaint(scrollr, off, nitem, nitemdrawn);
          217                 }
          218             }
          219             *m = emouse();
          220         }
          221     }
          222     bitblt(&screen, menur.min, b, menur, S);
          223     if(b != &screen)
          224         bfree(b);
          225     clipr(&screen, sc);
          226     if(lasti >= 0){
          227         menu->lasthit = lasti+off;
          228         return cursorswitch(oldcursor), menu->lasthit;
          229     }
          230     cursorswitch(oldcursor);
          231     return -1;
          232 }