#include #include #include #include #include #include #include typedef struct Menu0 Menu0; /* Menu is taken by mouse.h */ struct Menu0 { Control; CImage *image; CImage *bordercolor; CImage *textcolor; CImage *selectcolor; CImage *selecttextcolor; CFont *font; char **line; int nline; int border; int align; Image *window; int visible; /* state of menu */ int selection; /* currently selected line; -1 == none */ int prevsel; /* previous selection */ int lastbut; /* previous state of mouse button */ }; enum{ EAdd, EAlign, EBorder, EBordercolor, EFocus, EFont, EFormat, EImage, ERect, ESelectcolor, ESelecttextcolor, EShow, ETextcolor, EWindow, }; static char *cmds[] = { [EAdd] = "add", [EAlign] = "align", [EBorder] = "border", [EBordercolor] = "bordercolor", [EFocus] = "focus", [EFont] = "font", [EFormat] = "format", [EImage] = "image", [ERect] = "rect", [ESelectcolor] ="selectcolor", [ESelecttextcolor] ="selecttextcolor", [EShow] = "show", [ETextcolor] = "textcolor", [EWindow] = "window", nil }; static void menuctl(Control*, char*); static void menushow(Menu0*); static void menufree(Menu0*); static void menutrack(Menu0*); static void menuhide(Menu0*); static void menuthread(void *v) { char buf[32]; Menu0 *m; m = v; snprint(buf, sizeof buf, "menu-%s-0x%p", m->name, m); threadsetname(buf); m->font = _getctlfont("font"); m->image = _getctlimage("white"); m->textcolor = _getctlimage("black"); m->selectcolor = _getctlimage("yellow"); m->selecttextcolor = _getctlimage("black"); m->bordercolor = _getctlimage("black"); m->format = ctlstrdup("%q: value %d"); m->border = 0; m->align = Aupperleft; m->visible = 0; m->window = nil; m->lastbut = 0; m->selection = -1; for(;;){ switch(alt(m->alts)){ default: ctlerror("%q: unknown message", m->name); case AKey: /* ignore keystrokes */ break; case AMouse: menutrack(m); break; case ACtl: _ctlcontrol(m, m->str, menuctl); free(m->str); break; case AWire: _ctlrewire(m); break; case AExit: menufree(m); sendul(m->exit, 0); return; } } } Control* createmenu(Controlset *cs, char *name) { return _createctl(cs, "menu", sizeof(Menu0), name, menuthread, 0); } static void menufree(Menu0 *m) { _putctlfont(m->font); _putctlimage(m->image); _putctlimage(m->textcolor); _putctlimage(m->bordercolor); _putctlimage(m->selectcolor); _putctlimage(m->selecttextcolor); } static void menushow(Menu0 *m) { Rectangle r, clipr; int i, dx, dy, w; Font *f; Point p, q; Image *im, *c; if(m->window == nil) return; m->visible = 1; f = m->font->font; draw(m->window, m->rect, m->image->image, nil, m->image->image->r.min); if(m->border > 0) border(m->window, m->rect, m->border, m->bordercolor->image, ZP); /* text goes here */ dx = 0; for(i=0; inline; i++){ w = stringwidth(f, m->line[i]); if(dx < w) dx = w; } dy = m->nline*f->height; clipr = insetrect(m->rect, m->border); p = _ctlalignpoint(clipr, dx, dy, m->align); im = m->textcolor->image; // if(m->pressed) // im = m->pressedtextcolor->image; for(i=0; inline; i++){ r.min = p; r.max.x = p.x+dx; r.max.y = p.y+f->height; c = im; if(i == m->selection){ draw(m->window, r, m->selectcolor->image, nil, ZP); c = m->selecttextcolor->image; } q = _ctlalignpoint(r, stringwidth(f, m->line[i]), f->height, m->align%3); _string(m->window, q, c, ZP, f, m->line[i], nil, strlen(m->line[i]), clipr, nil, ZP); p.y += f->height; } // if(m->pressed) // draw(m->screen, m->rect, m->lighm->image, m->mask->image, m->mask->image->r.min); flushimage(display, 1); } static Point menusize(Menu0 *m) { int x, y; int i; Point p; Font *f; x = 0; y = 0; f = m->font->font; for(i=0; inline; i++){ p = stringsize(f, m->line[i]); if(p.x > x) x = p.x; y += f->height; } return Pt(x+2*m->border, y+2*m->border); } static void menuhide(Menu0 *m) { freeimage(m->window); m->window = nil; m->rect.max.y = m->rect.min.y; /* go to zero size */ m->lastbut = 0; m->visible = 0; if(m->selection >= 0) m->prevsel = m->selection; m->selection = -1; _ctlfocus(m, 0); } static void menutrack(Menu0 *m) { Rectangle r; int s; if(m->window == nil) return; if(m->lastbut && m->m.buttons==0){ /* menu was released */ printctl(m->event, "%q: value %d", m->name, m->selection); menuhide(m); return; } m->lastbut = m->m.buttons; r = insetrect(m->rect, m->border); if(!ptinrect(m->m.xy, r)) s = -1; else{ s = (m->m.xy.y - r.min.y)/m->font->font->height; if(s < 0 || s >= m->nline) s = -1; } if(m->visible== 0 || s!=m->selection){ m->selection = s; menushow(m); } } static void menuctl(Control *c, char *str) { int up, cmd, h; CParse cp; Rectangle r; Menu0 *m; Point diag; m = (Menu0*)c; cmd = _ctlparse(&cp, str, cmds); switch(cmd){ default: ctlerror("%q: unrecognized message '%s'", m->name, cp.str); break; case EAdd: _ctlargcount(m, &cp, 2); m->line = ctlrealloc(m->line, (m->nline+1)*sizeof(char*)); m->line[m->nline++] = ctlstrdup(cp.args[1]); menushow(m); break; case EAlign: _ctlargcount(m, &cp, 2); m->align = _ctlalignment(cp.args[1]); menushow(m); break; case EBorder: _ctlargcount(m, &cp, 2); m->border = cp.iargs[1]; menushow(m); break; case EBordercolor: _ctlargcount(m, &cp, 2); _setctlimage(m, &m->bordercolor, cp.args[1]); menushow(m); break; case EFocus: _ctlargcount(m, &cp, 2); if(atoi(cp.args[1]) == 0) menuhide(m); break; case EFont: _ctlargcount(m, &cp, 2); _setctlfont(m, &m->font, cp.args[1]); break; case EFormat: _ctlargcount(m, &cp, 2); m->format = ctlstrdup(cp.args[1]); break; case EImage: _ctlargcount(m, &cp, 2); _setctlimage(m, &m->image, cp.args[1]); menushow(m); break; case ERect: _ctlargcount(m, &cp, 5); r.min.x = cp.iargs[1]; r.min.y = cp.iargs[2]; r.max.x = cp.iargs[3]; r.max.y = cp.iargs[4]; if(Dx(r)<0 || Dy(r)<0) ctlerror("%q: bad rectangle: %s", m->name, cp.str); m->rect = r; menushow(m); break; case ESelectcolor: _ctlargcount(m, &cp, 2); _setctlimage(m, &m->selectcolor, cp.args[1]); menushow(m); break; case ESelecttextcolor: _ctlargcount(m, &cp, 2); _setctlimage(m, &m->selecttextcolor, cp.args[1]); menushow(m); break; case EShow: _ctlargcount(m, &cp, 1); menushow(m); break; case ETextcolor: _ctlargcount(m, &cp, 2); _setctlimage(m, &m->textcolor, cp.args[1]); menushow(m); break; case EWindow: /* no args == toggle; otherwise 0 or 1 for state of window */ if(cp.nargs >= 2) up = cp.iargs[1]; else up = (m->window == nil); if(!up){ /* take window down */ if(m->window) menuhide(m); break; } if(m->window != nil) break; diag = menusize(m); if(Dx(m->rect) <= 0) m->rect.max.x = m->rect.min.x + diag.x; if(Dy(m->rect) <= 0) m->rect.max.y = m->rect.min.y + diag.y; m->window = allocwindow(_screen, m->rect, Refbackup, DWhite); if(m->window == nil) m->window = m->screen; up = m->prevsel; if(up<0 || up>=m->nline) up = 0; m->selection = up; menushow(m); h = m->font->font->height; moveto(m->controlset->mousectl, Pt(m->rect.min.x+Dx(m->rect)/2, m->rect.min.y+up*h+h/2)); // _ctlfocus(m, 1); break; } }