19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <draw.h>
49a747e4fSDavid du Colombier #include <thread.h>
59a747e4fSDavid du Colombier #include <mouse.h>
69a747e4fSDavid du Colombier #include <keyboard.h>
79a747e4fSDavid du Colombier #include <control.h>
89a747e4fSDavid du Colombier
99a747e4fSDavid du Colombier typedef struct Menu0 Menu0; /* Menu is taken by mouse.h */
109a747e4fSDavid du Colombier
119a747e4fSDavid du Colombier struct Menu0
129a747e4fSDavid du Colombier {
139a747e4fSDavid du Colombier Control;
149a747e4fSDavid du Colombier CImage *image;
159a747e4fSDavid du Colombier CImage *bordercolor;
169a747e4fSDavid du Colombier CImage *textcolor;
179a747e4fSDavid du Colombier CImage *selectcolor;
189a747e4fSDavid du Colombier CImage *selecttextcolor;
199a747e4fSDavid du Colombier CFont *font;
209a747e4fSDavid du Colombier char **line;
219a747e4fSDavid du Colombier int nline;
229a747e4fSDavid du Colombier int border;
239a747e4fSDavid du Colombier int align;
249a747e4fSDavid du Colombier Image *window;
259a747e4fSDavid du Colombier int visible; /* state of menu */
269a747e4fSDavid du Colombier int selection; /* currently selected line; -1 == none */
279a747e4fSDavid du Colombier int prevsel; /* previous selection */
289a747e4fSDavid du Colombier int lastbut; /* previous state of mouse button */
299a747e4fSDavid du Colombier };
309a747e4fSDavid du Colombier
319a747e4fSDavid du Colombier enum{
329a747e4fSDavid du Colombier EAdd,
339a747e4fSDavid du Colombier EAlign,
349a747e4fSDavid du Colombier EBorder,
359a747e4fSDavid du Colombier EBordercolor,
369a747e4fSDavid du Colombier EFocus,
379a747e4fSDavid du Colombier EFont,
389a747e4fSDavid du Colombier EFormat,
399a747e4fSDavid du Colombier EHide,
409a747e4fSDavid du Colombier EImage,
419a747e4fSDavid du Colombier ERect,
429a747e4fSDavid du Colombier EReveal,
439a747e4fSDavid du Colombier ESelectcolor,
449a747e4fSDavid du Colombier ESelecttextcolor,
459a747e4fSDavid du Colombier EShow,
469a747e4fSDavid du Colombier ESize,
479a747e4fSDavid du Colombier ETextcolor,
489a747e4fSDavid du Colombier EWindow,
499a747e4fSDavid du Colombier };
509a747e4fSDavid du Colombier
519a747e4fSDavid du Colombier static char *cmds[] = {
529a747e4fSDavid du Colombier [EAdd] = "add",
539a747e4fSDavid du Colombier [EAlign] = "align",
549a747e4fSDavid du Colombier [EBorder] = "border",
559a747e4fSDavid du Colombier [EBordercolor] = "bordercolor",
569a747e4fSDavid du Colombier [EFocus] = "focus",
579a747e4fSDavid du Colombier [EFont] = "font",
589a747e4fSDavid du Colombier [EFormat] = "format",
599a747e4fSDavid du Colombier [EHide] = "hide",
609a747e4fSDavid du Colombier [EImage] = "image",
619a747e4fSDavid du Colombier [ERect] = "rect",
629a747e4fSDavid du Colombier [EReveal] = "reveal",
639a747e4fSDavid du Colombier [ESelectcolor] = "selectcolor",
649a747e4fSDavid du Colombier [ESelecttextcolor] = "selecttextcolor",
659a747e4fSDavid du Colombier [EShow] = "show",
669a747e4fSDavid du Colombier [ESize] = "size",
679a747e4fSDavid du Colombier [ETextcolor] = "textcolor",
689a747e4fSDavid du Colombier [EWindow] = "window",
699a747e4fSDavid du Colombier nil
709a747e4fSDavid du Colombier };
719a747e4fSDavid du Colombier
729a747e4fSDavid du Colombier static void menushow(Menu0*);
739a747e4fSDavid du Colombier static void menuhide(Menu0*);
749a747e4fSDavid du Colombier
759a747e4fSDavid du Colombier static void
menufree(Control * c)769a747e4fSDavid du Colombier menufree(Control *c)
779a747e4fSDavid du Colombier {
789a747e4fSDavid du Colombier Menu0 *m;
799a747e4fSDavid du Colombier
809a747e4fSDavid du Colombier m = (Menu0*)c;
819a747e4fSDavid du Colombier _putctlfont(m->font);
829a747e4fSDavid du Colombier _putctlimage(m->image);
839a747e4fSDavid du Colombier _putctlimage(m->textcolor);
849a747e4fSDavid du Colombier _putctlimage(m->bordercolor);
859a747e4fSDavid du Colombier _putctlimage(m->selectcolor);
869a747e4fSDavid du Colombier _putctlimage(m->selecttextcolor);
879a747e4fSDavid du Colombier }
889a747e4fSDavid du Colombier
899a747e4fSDavid du Colombier static void
menushow(Menu0 * m)909a747e4fSDavid du Colombier menushow(Menu0 *m)
919a747e4fSDavid du Colombier {
929a747e4fSDavid du Colombier Rectangle r, clipr;
939a747e4fSDavid du Colombier int i, dx, dy, w;
949a747e4fSDavid du Colombier Font *f;
959a747e4fSDavid du Colombier Point p, q;
969a747e4fSDavid du Colombier Image *im, *c;
979a747e4fSDavid du Colombier
989a747e4fSDavid du Colombier if(m->hidden || m->window == nil)
999a747e4fSDavid du Colombier return;
1009a747e4fSDavid du Colombier
1019a747e4fSDavid du Colombier m->visible = 1;
1029a747e4fSDavid du Colombier f = m->font->font;
1039a747e4fSDavid du Colombier draw(m->window, m->rect, m->image->image, nil, m->image->image->r.min);
1049a747e4fSDavid du Colombier if(m->border > 0)
1059a747e4fSDavid du Colombier border(m->window, m->rect, m->border, m->bordercolor->image, ZP);
1069a747e4fSDavid du Colombier /* text goes here */
1079a747e4fSDavid du Colombier dx = 0;
1089a747e4fSDavid du Colombier for(i=0; i<m->nline; i++){
1099a747e4fSDavid du Colombier w = stringwidth(f, m->line[i]);
1109a747e4fSDavid du Colombier if(dx < w)
1119a747e4fSDavid du Colombier dx = w;
1129a747e4fSDavid du Colombier }
1139a747e4fSDavid du Colombier dy = m->nline*f->height;
1149a747e4fSDavid du Colombier clipr = insetrect(m->rect, m->border);
1159a747e4fSDavid du Colombier p = _ctlalignpoint(clipr, dx, dy, m->align);
1169a747e4fSDavid du Colombier im = m->textcolor->image;
1179a747e4fSDavid du Colombier // if(m->pressed)
1189a747e4fSDavid du Colombier // im = m->pressedtextcolor->image;
1199a747e4fSDavid du Colombier for(i=0; i<m->nline; i++){
1209a747e4fSDavid du Colombier r.min = p;
1219a747e4fSDavid du Colombier r.max.x = p.x+dx;
1229a747e4fSDavid du Colombier r.max.y = p.y+f->height;
1239a747e4fSDavid du Colombier c = im;
1249a747e4fSDavid du Colombier if(i == m->selection){
1259a747e4fSDavid du Colombier draw(m->window, r, m->selectcolor->image, nil, ZP);
1269a747e4fSDavid du Colombier c = m->selecttextcolor->image;
1279a747e4fSDavid du Colombier }
1289a747e4fSDavid du Colombier q = _ctlalignpoint(r, stringwidth(f, m->line[i]), f->height, m->align%3);
1299a747e4fSDavid du Colombier _string(m->window, q, c,
1309a747e4fSDavid du Colombier ZP, f, m->line[i], nil, strlen(m->line[i]),
131*ac57dd0bSDavid du Colombier clipr, nil, ZP, SoverD);
1329a747e4fSDavid du Colombier p.y += f->height;
1339a747e4fSDavid du Colombier }
1349a747e4fSDavid du Colombier // if(m->pressed)
1359a747e4fSDavid du Colombier // draw(m->screen, m->rect, m->lighm->image, m->mask->image, m->mask->image->r.min);
1369a747e4fSDavid du Colombier flushimage(display, 1);
1379a747e4fSDavid du Colombier }
1389a747e4fSDavid du Colombier
1399a747e4fSDavid du Colombier static Point
menusize(Menu0 * m)1409a747e4fSDavid du Colombier menusize(Menu0 *m)
1419a747e4fSDavid du Colombier {
1429a747e4fSDavid du Colombier int x, y;
1439a747e4fSDavid du Colombier int i;
1449a747e4fSDavid du Colombier Point p;
1459a747e4fSDavid du Colombier Font *f;
1469a747e4fSDavid du Colombier
1479a747e4fSDavid du Colombier x = 0;
1489a747e4fSDavid du Colombier y = 0;
1499a747e4fSDavid du Colombier f = m->font->font;
1509a747e4fSDavid du Colombier for(i=0; i<m->nline; i++){
1519a747e4fSDavid du Colombier p = stringsize(f, m->line[i]);
1529a747e4fSDavid du Colombier if(p.x > x)
1539a747e4fSDavid du Colombier x = p.x;
1549a747e4fSDavid du Colombier y += f->height;
1559a747e4fSDavid du Colombier }
1569a747e4fSDavid du Colombier
1579a747e4fSDavid du Colombier return Pt(x+2*m->border, y+2*m->border);
1589a747e4fSDavid du Colombier }
1599a747e4fSDavid du Colombier
1609a747e4fSDavid du Colombier static void
menuhide(Menu0 * m)1619a747e4fSDavid du Colombier menuhide(Menu0 *m)
1629a747e4fSDavid du Colombier {
1639a747e4fSDavid du Colombier freeimage(m->window);
1649a747e4fSDavid du Colombier m->window = nil;
1659a747e4fSDavid du Colombier m->rect.max.y = m->rect.min.y; /* go to zero size */
1669a747e4fSDavid du Colombier m->lastbut = 0;
1679a747e4fSDavid du Colombier m->visible = 0;
1689a747e4fSDavid du Colombier if(m->selection >= 0)
1699a747e4fSDavid du Colombier m->prevsel = m->selection;
1709a747e4fSDavid du Colombier m->selection = -1;
1719a747e4fSDavid du Colombier _ctlfocus(m, 0);
1729a747e4fSDavid du Colombier }
1739a747e4fSDavid du Colombier
1749a747e4fSDavid du Colombier static void
menutrack(Control * c,Mouse * ms)1759a747e4fSDavid du Colombier menutrack(Control *c, Mouse *ms)
1769a747e4fSDavid du Colombier {
1779a747e4fSDavid du Colombier Rectangle r;
1789a747e4fSDavid du Colombier int s;
1799a747e4fSDavid du Colombier Menu0 *m;
1809a747e4fSDavid du Colombier
1819a747e4fSDavid du Colombier m = (Menu0*)c;
1829a747e4fSDavid du Colombier if(m->window == nil)
1839a747e4fSDavid du Colombier return;
1849a747e4fSDavid du Colombier if(m->lastbut && ms->buttons==0){ /* menu was released */
1859a747e4fSDavid du Colombier chanprint(m->event, "%q: value %d", m->name, m->selection);
1869a747e4fSDavid du Colombier menuhide(m);
1879a747e4fSDavid du Colombier return;
1889a747e4fSDavid du Colombier }
1899a747e4fSDavid du Colombier m->lastbut = ms->buttons;
1909a747e4fSDavid du Colombier r = insetrect(m->rect, m->border);
1919a747e4fSDavid du Colombier if(!ptinrect(ms->xy, r))
1929a747e4fSDavid du Colombier s = -1;
1939a747e4fSDavid du Colombier else{
1949a747e4fSDavid du Colombier s = (ms->xy.y - r.min.y)/m->font->font->height;
1959a747e4fSDavid du Colombier if(s < 0 || s >= m->nline)
1969a747e4fSDavid du Colombier s = -1;
1979a747e4fSDavid du Colombier }
1989a747e4fSDavid du Colombier if(m->visible== 0 || s!=m->selection){
1999a747e4fSDavid du Colombier m->selection = s;
2009a747e4fSDavid du Colombier menushow(m);
2019a747e4fSDavid du Colombier }
2029a747e4fSDavid du Colombier }
2039a747e4fSDavid du Colombier
2049a747e4fSDavid du Colombier static void
menuctl(Control * c,CParse * cp)2059a747e4fSDavid du Colombier menuctl(Control *c, CParse *cp)
2069a747e4fSDavid du Colombier {
2079a747e4fSDavid du Colombier int up, cmd, h;
2089a747e4fSDavid du Colombier Rectangle r;
2099a747e4fSDavid du Colombier Menu0 *m;
2109a747e4fSDavid du Colombier Point diag;
2119a747e4fSDavid du Colombier
2129a747e4fSDavid du Colombier m = (Menu0*)c;
2139a747e4fSDavid du Colombier cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
2149a747e4fSDavid du Colombier switch(cmd){
2159a747e4fSDavid du Colombier default:
2169a747e4fSDavid du Colombier ctlerror("%q: unrecognized message '%s'", m->name, cp->str);
2179a747e4fSDavid du Colombier break;
2189a747e4fSDavid du Colombier case EAdd:
2199a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2209a747e4fSDavid du Colombier m->line = ctlrealloc(m->line, (m->nline+1)*sizeof(char*));
2219a747e4fSDavid du Colombier m->line[m->nline++] = ctlstrdup(cp->args[1]);
2229a747e4fSDavid du Colombier menushow(m);
2239a747e4fSDavid du Colombier break;
2249a747e4fSDavid du Colombier case EAlign:
2259a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2269a747e4fSDavid du Colombier m->align = _ctlalignment(cp->args[1]);
2279a747e4fSDavid du Colombier menushow(m);
2289a747e4fSDavid du Colombier break;
2299a747e4fSDavid du Colombier case EBorder:
2309a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2319a747e4fSDavid du Colombier m->border = cp->iargs[1];
2329a747e4fSDavid du Colombier menushow(m);
2339a747e4fSDavid du Colombier break;
2349a747e4fSDavid du Colombier case EBordercolor:
2359a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2369a747e4fSDavid du Colombier _setctlimage(m, &m->bordercolor, cp->args[1]);
2379a747e4fSDavid du Colombier menushow(m);
2389a747e4fSDavid du Colombier break;
2399a747e4fSDavid du Colombier case EFocus:
2409a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2419a747e4fSDavid du Colombier if(atoi(cp->args[1]) == 0)
2429a747e4fSDavid du Colombier menuhide(m);
2439a747e4fSDavid du Colombier break;
2449a747e4fSDavid du Colombier case EFont:
2459a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2469a747e4fSDavid du Colombier _setctlfont(m, &m->font, cp->args[1]);
2479a747e4fSDavid du Colombier break;
2489a747e4fSDavid du Colombier case EFormat:
2499a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2509a747e4fSDavid du Colombier m->format = ctlstrdup(cp->args[1]);
2519a747e4fSDavid du Colombier break;
2529a747e4fSDavid du Colombier case EHide:
2539a747e4fSDavid du Colombier _ctlargcount(m, cp, 1);
2549a747e4fSDavid du Colombier m->hidden = 1;
2559a747e4fSDavid du Colombier break;
2569a747e4fSDavid du Colombier case EImage:
2579a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2589a747e4fSDavid du Colombier _setctlimage(m, &m->image, cp->args[1]);
2599a747e4fSDavid du Colombier menushow(m);
2609a747e4fSDavid du Colombier break;
2619a747e4fSDavid du Colombier case ERect:
2629a747e4fSDavid du Colombier _ctlargcount(m, cp, 5);
2639a747e4fSDavid du Colombier r.min.x = cp->iargs[1];
2649a747e4fSDavid du Colombier r.min.y = cp->iargs[2];
2659a747e4fSDavid du Colombier r.max.x = cp->iargs[3];
2669a747e4fSDavid du Colombier r.max.y = cp->iargs[4];
2679a747e4fSDavid du Colombier if(Dx(r)<0 || Dy(r)<0)
2689a747e4fSDavid du Colombier ctlerror("%q: bad rectangle: %s", m->name, cp->str);
2699a747e4fSDavid du Colombier m->rect = r;
2709a747e4fSDavid du Colombier menushow(m);
2719a747e4fSDavid du Colombier break;
2729a747e4fSDavid du Colombier case EReveal:
2739a747e4fSDavid du Colombier _ctlargcount(m, cp, 1);
2749a747e4fSDavid du Colombier m->hidden = 0;
2759a747e4fSDavid du Colombier menushow(m);
2769a747e4fSDavid du Colombier break;
2779a747e4fSDavid du Colombier case ESelectcolor:
2789a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2799a747e4fSDavid du Colombier _setctlimage(m, &m->selectcolor, cp->args[1]);
2809a747e4fSDavid du Colombier menushow(m);
2819a747e4fSDavid du Colombier break;
2829a747e4fSDavid du Colombier case ESelecttextcolor:
2839a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
2849a747e4fSDavid du Colombier _setctlimage(m, &m->selecttextcolor, cp->args[1]);
2859a747e4fSDavid du Colombier menushow(m);
2869a747e4fSDavid du Colombier break;
2879a747e4fSDavid du Colombier case EShow:
2889a747e4fSDavid du Colombier _ctlargcount(m, cp, 1);
2899a747e4fSDavid du Colombier menushow(m);
2909a747e4fSDavid du Colombier break;
2919a747e4fSDavid du Colombier case ESize:
2929a747e4fSDavid du Colombier if (cp->nargs == 3)
2939a747e4fSDavid du Colombier r.max = Pt(0x7fffffff, 0x7fffffff);
2949a747e4fSDavid du Colombier else{
2959a747e4fSDavid du Colombier _ctlargcount(m, cp, 5);
2969a747e4fSDavid du Colombier r.max.x = cp->iargs[3];
2979a747e4fSDavid du Colombier r.max.y = cp->iargs[4];
2989a747e4fSDavid du Colombier }
2999a747e4fSDavid du Colombier r.min.x = cp->iargs[1];
3009a747e4fSDavid du Colombier r.min.y = cp->iargs[2];
3019a747e4fSDavid du Colombier if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
3029a747e4fSDavid du Colombier ctlerror("%q: bad sizes: %s", m->name, cp->str);
3039a747e4fSDavid du Colombier m->size.min = r.min;
3049a747e4fSDavid du Colombier m->size.max = r.max;
3059a747e4fSDavid du Colombier break;
3069a747e4fSDavid du Colombier case ETextcolor:
3079a747e4fSDavid du Colombier _ctlargcount(m, cp, 2);
3089a747e4fSDavid du Colombier _setctlimage(m, &m->textcolor, cp->args[1]);
3099a747e4fSDavid du Colombier menushow(m);
3109a747e4fSDavid du Colombier break;
3119a747e4fSDavid du Colombier case EWindow:
3129a747e4fSDavid du Colombier /* no args == toggle; otherwise 0 or 1 for state of window */
3139a747e4fSDavid du Colombier if(cp->nargs >= 2)
3149a747e4fSDavid du Colombier up = cp->iargs[1];
3159a747e4fSDavid du Colombier else
3169a747e4fSDavid du Colombier up = (m->window == nil);
3179a747e4fSDavid du Colombier if(!up){ /* take window down */
3189a747e4fSDavid du Colombier if(m->window)
3199a747e4fSDavid du Colombier menuhide(m);
3209a747e4fSDavid du Colombier break;
3219a747e4fSDavid du Colombier }
3229a747e4fSDavid du Colombier if(m->window != nil)
3239a747e4fSDavid du Colombier break;
3249a747e4fSDavid du Colombier diag = menusize(m);
3259a747e4fSDavid du Colombier m->rect.max.x = m->rect.min.x + diag.x;
3269a747e4fSDavid du Colombier m->rect.max.y = m->rect.min.y + diag.y;
3279a747e4fSDavid du Colombier m->window = allocwindow(_screen, m->rect, Refbackup, DWhite);
3289a747e4fSDavid du Colombier if(m->window == nil)
3299a747e4fSDavid du Colombier m->window = m->screen;
3309a747e4fSDavid du Colombier up = m->prevsel;
3319a747e4fSDavid du Colombier if(up<0 || up>=m->nline)
3329a747e4fSDavid du Colombier up = 0;
3339a747e4fSDavid du Colombier m->selection = up;
3349a747e4fSDavid du Colombier menushow(m);
3359a747e4fSDavid du Colombier h = m->font->font->height;
3369a747e4fSDavid du Colombier moveto(m->controlset->mousectl,
3379a747e4fSDavid du Colombier Pt(m->rect.min.x+Dx(m->rect)/2, m->rect.min.y+up*h+h/2));
3389a747e4fSDavid du Colombier // _ctlfocus(m, 1);
3399a747e4fSDavid du Colombier break;
3409a747e4fSDavid du Colombier }
3419a747e4fSDavid du Colombier }
3429a747e4fSDavid du Colombier
3439a747e4fSDavid du Colombier Control*
createmenu(Controlset * cs,char * name)3449a747e4fSDavid du Colombier createmenu(Controlset *cs, char *name)
3459a747e4fSDavid du Colombier {
3469a747e4fSDavid du Colombier Menu0 *m;
3479a747e4fSDavid du Colombier
3489a747e4fSDavid du Colombier m = (Menu0*)_createctl(cs, "menu", sizeof(Menu0), name);
3499a747e4fSDavid du Colombier m->font = _getctlfont("font");
3509a747e4fSDavid du Colombier m->image = _getctlimage("white");
3519a747e4fSDavid du Colombier m->textcolor = _getctlimage("black");
3529a747e4fSDavid du Colombier m->selectcolor = _getctlimage("yellow");
3539a747e4fSDavid du Colombier m->selecttextcolor = _getctlimage("black");
3549a747e4fSDavid du Colombier m->bordercolor = _getctlimage("black");
3559a747e4fSDavid du Colombier m->format = ctlstrdup("%q: value %d");
3569a747e4fSDavid du Colombier m->border = 0;
3579a747e4fSDavid du Colombier m->align = Aupperleft;
3589a747e4fSDavid du Colombier m->visible = 0;
3599a747e4fSDavid du Colombier m->window = nil;
3609a747e4fSDavid du Colombier m->lastbut = 0;
3619a747e4fSDavid du Colombier m->selection = -1;
3629a747e4fSDavid du Colombier m->mouse = menutrack;
3639a747e4fSDavid du Colombier m->ctl = menuctl;
3649a747e4fSDavid du Colombier m->exit = menufree;
3659a747e4fSDavid du Colombier return (Control *)m;
3669a747e4fSDavid du Colombier }
367