Charles.Forsyth20060303a828770d19 years agoHistory
1840 lines · 33.9 KB · C
RawDownload
#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 = 2;	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 = atoi(arg);	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.