#include "lib9.h"#include "draw.h"#include "keyboard.h"#include "tk.h"#include "frame.h"#include "label.h" /*arrow annotation for choicebutton: how do we make surethe menu items come up the same size? - set menu items to same req.width & height as button itself. autorepeat:when we get mouse event at the edge of the screenand the menu overlaps that edge,start autorepeat timer to slide the menu the opposite direction. variable setting + command invocation:is the value of the variable the text or the index?same for the value appended to the command, text or index? if it's reimplemented as a custom widget, how does the custom widgetget notified of variable changes?*/ /* Widget Commands (+ means implemented) +activate +add +cget +configure +delete +entrycget +entryconfigure +index +insert +invoke +post +postcascade +type +unpost +yposition*/ #define O(t, e) ((long)(&((t*)0)->e)) /* Layout constants */enum { Sepheight = 6, /* Height of menu separator */}; #define NOCHOICE "-----" enum { Startspeed = TKI2F(1),}; staticTkOption mbopts[] ={ "text", OPTtext, O(TkLabel, text), nil, "anchor", OPTflag, O(TkLabel, anchor), tkanchor, "underline", OPTdist, O(TkLabel, ul), nil, "justify", OPTstab, O(TkLabel, justify), tkjustify, "menu", OPTtext, O(TkLabel, menu), nil, "bitmap", OPTbmap, O(TkLabel, bitmap), nil, "image", OPTimag, O(TkLabel, img), nil, nil}; staticTkOption choiceopts[] ={ "variable", OPTtext, O(TkLabel, variable), nil, "values", OPTlist, O(TkLabel, values), nil, "command", OPTtext, O(TkLabel, command), nil, nil}; staticTkEbind mbbindings[] = { {TkEnter, "%W tkMBenter %s"}, {TkLeave, "%W tkMBleave"}, {TkButton1P, "%W tkMBpress 1"}, {TkKey, "%W tkMBkey 0x%K"}, {TkButton1P|TkMotion, "%W tkMBpress 0"},}; extern Rectangle bbnil;static char* tkmpost(Tk*, int, int, int, int, int);static void menuclr(Tk*);static void freemenu(Tk*);static void appenditem(Tk*, Tk*, int);static void layout(Tk*);static Tk* tkmenuindex2ptr(Tk*, char**);static void activateitem(Tk*); /* * unmap menu cascade upto (but not including) tk */static voidtkunmapmenus(TkTop *top, Tk *tk){ TkTop *t; Tk *menu; TkWin *tkw; menu = top->ctxt->tkmenu; if (menu == nil) return; t = menu->env->top; /* if something went wrong, clear down all menus */ if (tk != nil && tk->env->top != t) tk = nil; while (menu != nil && menu != tk) { menuclr(menu); tkunmap(menu); tkcancelrepeat(menu); tkw = TKobj(TkWin, menu); if (tkw->cascade != nil) { menu = tklook(t, tkw->cascade, 0); free(tkw->cascade); tkw->cascade = nil; } else menu = nil; } top->ctxt->tkmenu = menu; tksetmgrab(top, menu);} static voidtkunmapmenu(Tk *tk){ TkTop *t; TkWin *tkw; Tk *parent; parent = nil; tkw = TKobj(TkWin, tk); t = tk->env->top; if (tkw->cascade != nil) parent = tklook(t, tkw->cascade, 0); tkunmapmenus(t, parent); if (tkw->freeonunmap) freemenu(tk);} static voidtksizemenubutton(Tk *tk){ int w, h; char **v, *cur; TkLabel *tkl = TKobj(TkLabel, tk); tksizelabel(tk); if(tk->type != TKchoicebutton) return; w = tk->req.width; h = tk->req.height; v = tkl->values; if (v == nil || *v == nil) return; cur = tkl->text; for (; *v; v++) { tkl->text = *v; tksizelabel(tk); if (tk->req.width > w) w = tk->req.width; if (tk->req.height > h) h = tk->req.height; } tkl->text = cur; tksizelabel(tk); tk->req.width = w; tk->req.height = h;} static char*tkmkmenubutton(TkTop *t, char *arg, char **ret, int type, TkOption *opts){ Tk *tk; char *e, **v; TkName *names; TkLabel *tkl; TkOptab tko[3]; /* need to get the label from elsewhere */ tk = tknewobj(t, type, sizeof(Tk)+sizeof(TkLabel)); if(tk == nil) return TkNomem; tk->borderwidth = 2; tk->flag |= Tknograb; tkl = TKobj(TkLabel, tk); tkl->ul = -1; if(type == TKchoicebutton) tkl->anchor = Tknorth|Tkwest; tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkl; tko[1].optab = opts; tko[2].ptr = nil; names = nil; e = tkparse(t, arg, tko, &names); if(e != nil) { tkfreeobj(tk); return e; } tkl->nvalues = 0; if (tkl->values != nil) { for (v = tkl->values; *v; v++) ; tkl->nvalues = v - tkl->values; } if(type == TKchoicebutton){ if(tkl->nvalues > 0) tkl->text = strdup(tkl->values[0]); else tkl->text = strdup(NOCHOICE); } tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd) || tkhasalpha(tk->env, TkCselectbgnd) || tkhasalpha(tk->env, TkCactivebgnd)); e = tkbindings(t, tk, mbbindings, nelem(mbbindings)); if(e != nil) { tkfreeobj(tk); return e; } tksizemenubutton(tk); e = tkaddchild(t, tk, &names); tkfreename(names); if(e != nil) { tkfreeobj(tk); return e; } tk->name->link = nil; return tkvalue(ret, "%s", tk->name->name);} char*tkchoicebutton(TkTop *t, char *arg, char **ret){ return tkmkmenubutton(t, arg, ret, TKchoicebutton, choiceopts);} char*tkmenubutton(TkTop *t, char *arg, char **ret){ return tkmkmenubutton(t, arg, ret, TKmenubutton, mbopts);} static char*tkmenubutcget(Tk *tk, char *arg, char **val){ TkOptab tko[3]; TkLabel *tkl = TKobj(TkLabel, tk); tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkl; tko[1].optab = (tk->type == TKchoicebutton ? choiceopts : mbopts); tko[2].ptr = nil; return tkgencget(tko, arg, val, tk->env->top);} static char*tkmenubutconf(Tk *tk, char *arg, char **val){ char *e, **v; TkGeom g; int bd; TkOptab tko[3]; TkLabel *tkl = TKobj(TkLabel, tk); tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkl; tko[1].optab = (tk->type == TKchoicebutton ? choiceopts : mbopts); tko[2].ptr = nil; if(*arg == '\0') return tkconflist(tko, val); g = tk->req; bd = tk->borderwidth; e = tkparse(tk->env->top, arg, tko, nil); if (tk->type == TKchoicebutton) { tkl->nvalues = 0; if (tkl->values != nil) { for (v = tkl->values; *v; v++) ; tkl->nvalues = v - tkl->values; } if (tkl->check >= tkl->nvalues || strcmp(tkl->text, tkl->values[tkl->check])) { /* * try to keep selected value the same if possible */ for (v = tkl->values; v && *v; v++) if (!strcmp(*v, tkl->text)) break; free(tkl->text); if (v == nil || *v == nil) { tkl->text = strdup(tkl->nvalues > 0 ? tkl->values[0] : NOCHOICE); tkl->check = 0; } else { tkl->check = v - tkl->values; tkl->text = strdup(*v); } } } tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd) || tkhasalpha(tk->env, TkCselectbgnd) || tkhasalpha(tk->env, TkCactivebgnd)); tksizemenubutton(tk); tkgeomchg(tk, &g, bd); tk->dirty = tkrect(tk, 1); return e;} static char*tkMBleave(Tk *tk, char *arg, char **val){ USED(arg); USED(val); tk->flag &= ~Tkactive; tk->dirty = tkrect(tk, 1); return nil;} static Tk*mkchoicemenu(Tk *tkb){ Tk *menu, *tkc; int i; TkLabel *tkl, *tkcl; TkWin *tkw; TkTop *t; tkl = TKobj(TkLabel, tkb); t = tkb->env->top; menu = tknewobj(t, TKmenu, sizeof(Tk)+sizeof(TkWin)); if(menu == nil) return nil; menu->relief = TKraised; menu->flag |= Tknograb; menu->borderwidth = 1; tkputenv(menu->env); menu->env = tkb->env; menu->env->ref++; menu->flag |= Tkwindow; menu->geom = tkmoveresize; tkw = TKobj(TkWin, menu); tkw->cbname = strdup(tkb->name->name); tkw->di = (void*)-1; // XXX for(i = tkl->nvalues - 1; i >= 0; i--){ tkc = tknewobj(t, TKlabel, sizeof(Tk)+sizeof(TkLabel)); /* XXX recover from malloc failure */ tkc->flag = Tkwest|Tkfillx|Tktop; tkc->highlightwidth = 0; tkc->borderwidth = 1; tkc->relief = TKflat; tkputenv(tkc->env); tkc->env = tkb->env; tkc->env->ref++; tkcl = TKobj(TkLabel, tkc); tkcl->anchor = Tkwest; tkcl->ul = -1; tkcl->justify = Tkleft; tkcl->text = strdup(tkl->values[i]); tkcl->command = smprint("%s invoke %d", tkb->name->name, i); /* XXX recover from malloc failure */ tksizelabel(tkc); tkc->req.height = tkb->req.height; appenditem(menu, tkc, 0); } layout(menu); tkw->next = t->windows; tkw->freeonunmap = 1; t->windows = menu; return menu;} static char*tkMBpress(Tk *tk, char *arg, char **val){ Tk *menu, *item; TkLabel *tkl = TKobj(TkLabel, tk); Point g; char buf[12], *bufp, *e; USED(arg); USED(val); g = tkposn(tk); if (tk->type == TKchoicebutton) { menu = mkchoicemenu(tk); if (menu == nil) return TkNomem; sprint(buf, "%d", tkl->check); bufp = buf; item = tkmenuindex2ptr(menu, &bufp); if(item == nil) return nil; g.y -= item->act.y; e = tkmpost(menu, g.x, g.y, 0, 0, 0); activateitem(item); return e; } else { if (tkl->menu == nil) return nil; menu = tklook(tk->env->top, tkl->menu, 0); if(menu == nil || menu->type != TKmenu) return TkBadwp; if(menu->flag & Tkmapped) { if(atoi(arg)) tkunmapmenu(menu); return nil; } return tkmpost(menu, g.x, g.y, 0, tk->act.height + 2*tk->borderwidth, 1); }} static char*tkMBkey(Tk *tk, char *arg, char **val){ int key; USED(val); if(tk->flag & Tkdisabled) return nil; key = strtol(arg, nil, 0); if (key == '\n' || key == ' ') return tkMBpress(tk, "1", nil); return nil;} static char*tkMBenter(Tk *tk, char *arg, char **val){ USED(arg); USED(val); tk->flag |= Tkactive; tk->dirty = tkrect(tk, 1); return nil;} static char*tkchoicebutset(Tk *tk, char *arg, char **val){ char buf[12], *e; int v; TkLabel *tkl = TKobj(TkLabel, tk); USED(val); tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); if (*buf == '\0') return TkBadvl; v = atoi(buf); if (v < 0 || v >= tkl->nvalues) return TkBadvl; if (v == tkl->check) return nil; free(tkl->text); tkl->text = strdup(tkl->values[v]); /* XXX recover from malloc error */ tkl->check = v; sprint(buf, "%d", v); e = tksetvar(tk->env->top, tkl->variable, buf); if(e != nil) return e; tk->dirty = tkrect(tk, 1); return nil;} static char*Lines 1–500 of 1840. Load more
#include "lib9.h" #include "draw.h" #include "keyboard.h" #include "tk.h" #include "frame.h" #include "label.h" /* arrow annotation for choicebutton: how do we make sure the menu items come up the same size? - set menu items to same req.width & height as button itself. autorepeat: when we get mouse event at the edge of the screen and the menu overlaps that edge, start autorepeat timer to slide the menu the opposite direction. variable setting + command invocation: is the value of the variable the text or the index? same for the value appended to the command, text or index? if it's reimplemented as a custom widget, how does the custom widget get notified of variable changes? */ /* Widget Commands (+ means implemented) +activate +add +cget +configure +delete +entrycget +entryconfigure +index +insert +invoke +post +postcascade +type +unpost +yposition */ #define O(t, e) ((long)(&((t*)0)->e)) /* Layout constants */ enum { Sepheight = 6, /* Height of menu separator */ }; #define NOCHOICE "-----" enum { Startspeed = TKI2F(1), }; static TkOption mbopts[] = { "text", OPTtext, O(TkLabel, text), nil, "anchor", OPTflag, O(TkLabel, anchor), tkanchor, "underline", OPTdist, O(TkLabel, ul), nil, "justify", OPTstab, O(TkLabel, justify), tkjustify, "menu", OPTtext, O(TkLabel, menu), nil, "bitmap", OPTbmap, O(TkLabel, bitmap), nil, "image", OPTimag, O(TkLabel, img), nil, nil }; static TkOption choiceopts[] = { "variable", OPTtext, O(TkLabel, variable), nil, "values", OPTlist, O(TkLabel, values), nil, "command", OPTtext, O(TkLabel, command), nil, nil }; static TkEbind mbbindings[] = { {TkEnter, "%W tkMBenter %s"}, {TkLeave, "%W tkMBleave"}, {TkButton1P, "%W tkMBpress 1"}, {TkKey, "%W tkMBkey 0x%K"}, {TkButton1P|TkMotion, "%W tkMBpress 0"}, }; extern Rectangle bbnil; static char* tkmpost(Tk*, int, int, int, int, int); static void menuclr(Tk*); static void freemenu(Tk*); static void appenditem(Tk*, Tk*, int); static void layout(Tk*); static Tk* tkmenuindex2ptr(Tk*, char**); static void activateitem(Tk*); /* * unmap menu cascade upto (but not including) tk */ static void tkunmapmenus(TkTop *top, Tk *tk) { TkTop *t; Tk *menu; TkWin *tkw; menu = top->ctxt->tkmenu; if (menu == nil) return; t = menu->env->top; /* if something went wrong, clear down all menus */ if (tk != nil && tk->env->top != t) tk = nil; while (menu != nil && menu != tk) { menuclr(menu); tkunmap(menu); tkcancelrepeat(menu); tkw = TKobj(TkWin, menu); if (tkw->cascade != nil) { menu = tklook(t, tkw->cascade, 0); free(tkw->cascade); tkw->cascade = nil; } else menu = nil; } top->ctxt->tkmenu = menu; tksetmgrab(top, menu); } static void tkunmapmenu(Tk *tk) { TkTop *t; TkWin *tkw; Tk *parent; parent = nil; tkw = TKobj(TkWin, tk); t = tk->env->top; if (tkw->cascade != nil) parent = tklook(t, tkw->cascade, 0); tkunmapmenus(t, parent); if (tkw->freeonunmap) freemenu(tk); } static void tksizemenubutton(Tk *tk) { int w, h; char **v, *cur; TkLabel *tkl = TKobj(TkLabel, tk); tksizelabel(tk); if(tk->type != TKchoicebutton) return; w = tk->req.width; h = tk->req.height; v = tkl->values; if (v == nil || *v == nil) return; cur = tkl->text; for (; *v; v++) { tkl->text = *v; tksizelabel(tk); if (tk->req.width > w) w = tk->req.width; if (tk->req.height > h) h = tk->req.height; } tkl->text = cur; tksizelabel(tk); tk->req.width = w; tk->req.height = h; } static char* tkmkmenubutton(TkTop *t, char *arg, char **ret, int type, TkOption *opts) { Tk *tk; char *e, **v; TkName *names; TkLabel *tkl; TkOptab tko[3]; /* need to get the label from elsewhere */ tk = tknewobj(t, type, sizeof(Tk)+sizeof(TkLabel)); if(tk == nil) return TkNomem; tk->borderwidth = 2; tk->flag |= Tknograb; tkl = TKobj(TkLabel, tk); tkl->ul = -1; if(type == TKchoicebutton) tkl->anchor = Tknorth|Tkwest; tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkl; tko[1].optab = opts; tko[2].ptr = nil; names = nil; e = tkparse(t, arg, tko, &names); if(e != nil) { tkfreeobj(tk); return e; } tkl->nvalues = 0; if (tkl->values != nil) { for (v = tkl->values; *v; v++) ; tkl->nvalues = v - tkl->values; } if(type == TKchoicebutton){ if(tkl->nvalues > 0) tkl->text = strdup(tkl->values[0]); else tkl->text = strdup(NOCHOICE); } tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd) || tkhasalpha(tk->env, TkCselectbgnd) || tkhasalpha(tk->env, TkCactivebgnd)); e = tkbindings(t, tk, mbbindings, nelem(mbbindings)); if(e != nil) { tkfreeobj(tk); return e; } tksizemenubutton(tk); e = tkaddchild(t, tk, &names); tkfreename(names); if(e != nil) { tkfreeobj(tk); return e; } tk->name->link = nil; return tkvalue(ret, "%s", tk->name->name); } char* tkchoicebutton(TkTop *t, char *arg, char **ret) { return tkmkmenubutton(t, arg, ret, TKchoicebutton, choiceopts); } char* tkmenubutton(TkTop *t, char *arg, char **ret) { return tkmkmenubutton(t, arg, ret, TKmenubutton, mbopts); } static char* tkmenubutcget(Tk *tk, char *arg, char **val) { TkOptab tko[3]; TkLabel *tkl = TKobj(TkLabel, tk); tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkl; tko[1].optab = (tk->type == TKchoicebutton ? choiceopts : mbopts); tko[2].ptr = nil; return tkgencget(tko, arg, val, tk->env->top); } static char* tkmenubutconf(Tk *tk, char *arg, char **val) { char *e, **v; TkGeom g; int bd; TkOptab tko[3]; TkLabel *tkl = TKobj(TkLabel, tk); tko[0].ptr = tk; tko[0].optab = tkgeneric; tko[1].ptr = tkl; tko[1].optab = (tk->type == TKchoicebutton ? choiceopts : mbopts); tko[2].ptr = nil; if(*arg == '\0') return tkconflist(tko, val); g = tk->req; bd = tk->borderwidth; e = tkparse(tk->env->top, arg, tko, nil); if (tk->type == TKchoicebutton) { tkl->nvalues = 0; if (tkl->values != nil) { for (v = tkl->values; *v; v++) ; tkl->nvalues = v - tkl->values; } if (tkl->check >= tkl->nvalues || strcmp(tkl->text, tkl->values[tkl->check])) { /* * try to keep selected value the same if possible */ for (v = tkl->values; v && *v; v++) if (!strcmp(*v, tkl->text)) break; free(tkl->text); if (v == nil || *v == nil) { tkl->text = strdup(tkl->nvalues > 0 ? tkl->values[0] : NOCHOICE); tkl->check = 0; } else { tkl->check = v - tkl->values; tkl->text = strdup(*v); } } } tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd) || tkhasalpha(tk->env, TkCselectbgnd) || tkhasalpha(tk->env, TkCactivebgnd)); tksizemenubutton(tk); tkgeomchg(tk, &g, bd); tk->dirty = tkrect(tk, 1); return e; } static char* tkMBleave(Tk *tk, char *arg, char **val) { USED(arg); USED(val); tk->flag &= ~Tkactive; tk->dirty = tkrect(tk, 1); return nil; } static Tk* mkchoicemenu(Tk *tkb) { Tk *menu, *tkc; int i; TkLabel *tkl, *tkcl; TkWin *tkw; TkTop *t; tkl = TKobj(TkLabel, tkb); t = tkb->env->top; menu = tknewobj(t, TKmenu, sizeof(Tk)+sizeof(TkWin)); if(menu == nil) return nil; menu->relief = TKraised; menu->flag |= Tknograb; menu->borderwidth = 1; tkputenv(menu->env); menu->env = tkb->env; menu->env->ref++; menu->flag |= Tkwindow; menu->geom = tkmoveresize; tkw = TKobj(TkWin, menu); tkw->cbname = strdup(tkb->name->name); tkw->di = (void*)-1; // XXX for(i = tkl->nvalues - 1; i >= 0; i--){ tkc = tknewobj(t, TKlabel, sizeof(Tk)+sizeof(TkLabel)); /* XXX recover from malloc failure */ tkc->flag = Tkwest|Tkfillx|Tktop; tkc->highlightwidth = 0; tkc->borderwidth = 1; tkc->relief = TKflat; tkputenv(tkc->env); tkc->env = tkb->env; tkc->env->ref++; tkcl = TKobj(TkLabel, tkc); tkcl->anchor = Tkwest; tkcl->ul = -1; tkcl->justify = Tkleft; tkcl->text = strdup(tkl->values[i]); tkcl->command = smprint("%s invoke %d", tkb->name->name, i); /* XXX recover from malloc failure */ tksizelabel(tkc); tkc->req.height = tkb->req.height; appenditem(menu, tkc, 0); } layout(menu); tkw->next = t->windows; tkw->freeonunmap = 1; t->windows = menu; return menu; } static char* tkMBpress(Tk *tk, char *arg, char **val) { Tk *menu, *item; TkLabel *tkl = TKobj(TkLabel, tk); Point g; char buf[12], *bufp, *e; USED(arg); USED(val); g = tkposn(tk); if (tk->type == TKchoicebutton) { menu = mkchoicemenu(tk); if (menu == nil) return TkNomem; sprint(buf, "%d", tkl->check); bufp = buf; item = tkmenuindex2ptr(menu, &bufp); if(item == nil) return nil; g.y -= item->act.y; e = tkmpost(menu, g.x, g.y, 0, 0, 0); activateitem(item); return e; } else { if (tkl->menu == nil) return nil; menu = tklook(tk->env->top, tkl->menu, 0); if(menu == nil || menu->type != TKmenu) return TkBadwp; if(menu->flag & Tkmapped) { if(atoi(arg)) tkunmapmenu(menu); return nil; } return tkmpost(menu, g.x, g.y, 0, tk->act.height + 2*tk->borderwidth, 1); } } static char* tkMBkey(Tk *tk, char *arg, char **val) { int key; USED(val); if(tk->flag & Tkdisabled) return nil; key = strtol(arg, nil, 0); if (key == '\n' || key == ' ') return tkMBpress(tk, "1", nil); return nil; } static char* tkMBenter(Tk *tk, char *arg, char **val) { USED(arg); USED(val); tk->flag |= Tkactive; tk->dirty = tkrect(tk, 1); return nil; } static char* tkchoicebutset(Tk *tk, char *arg, char **val) { char buf[12], *e; int v; TkLabel *tkl = TKobj(TkLabel, tk); USED(val); tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil); if (*buf == '\0') return TkBadvl; v = atoi(buf); if (v < 0 || v >= tkl->nvalues) return TkBadvl; if (v == tkl->check) return nil; free(tkl->text); tkl->text = strdup(tkl->values[v]); /* XXX recover from malloc error */ tkl->check = v; sprint(buf, "%d", v); e = tksetvar(tk->env->top, tkl->variable, buf); if(e != nil) return e; tk->dirty = tkrect(tk, 1); return nil; } static char*