xref: /plan9-contrib/sys/src/libcontrol/tabs.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1*9a747e4fSDavid du Colombier #include <u.h>
2*9a747e4fSDavid du Colombier #include <libc.h>
3*9a747e4fSDavid du Colombier #include <thread.h>
4*9a747e4fSDavid du Colombier #include <draw.h>
5*9a747e4fSDavid du Colombier #include <mouse.h>
6*9a747e4fSDavid du Colombier #include <keyboard.h>
7*9a747e4fSDavid du Colombier #include <control.h>
8*9a747e4fSDavid du Colombier #include "group.h"
9*9a747e4fSDavid du Colombier 
10*9a747e4fSDavid du Colombier typedef struct Tab Tab;
11*9a747e4fSDavid du Colombier 
12*9a747e4fSDavid du Colombier struct Tab {
13*9a747e4fSDavid du Colombier 	Control;
14*9a747e4fSDavid du Colombier 	int		border;
15*9a747e4fSDavid du Colombier 	int		selected;
16*9a747e4fSDavid du Colombier 	int		separation;
17*9a747e4fSDavid du Colombier 	char		*format;
18*9a747e4fSDavid du Colombier 	CImage	*bordercolor;
19*9a747e4fSDavid du Colombier 	CImage	*image;
20*9a747e4fSDavid du Colombier 	Control	*tabrow;
21*9a747e4fSDavid du Colombier 	Control	*tabstack;
22*9a747e4fSDavid du Colombier 	Control	*tabcolumn;
23*9a747e4fSDavid du Colombier 	int		ntabs;
24*9a747e4fSDavid du Colombier 	int		nbuttons;
25*9a747e4fSDavid du Colombier 	Control	**buttons;
26*9a747e4fSDavid du Colombier };
27*9a747e4fSDavid du Colombier 
28*9a747e4fSDavid du Colombier enum{
29*9a747e4fSDavid du Colombier 	EAdd,
30*9a747e4fSDavid du Colombier 	EBorder,
31*9a747e4fSDavid du Colombier 	EBordercolor,
32*9a747e4fSDavid du Colombier 	EButton,
33*9a747e4fSDavid du Colombier 	EFocus,
34*9a747e4fSDavid du Colombier 	EFormat,
35*9a747e4fSDavid du Colombier 	EHide,
36*9a747e4fSDavid du Colombier 	EImage,
37*9a747e4fSDavid du Colombier 	ERect,
38*9a747e4fSDavid du Colombier 	EReveal,
39*9a747e4fSDavid du Colombier 	ESeparation,
40*9a747e4fSDavid du Colombier 	ESeparatorcolor,
41*9a747e4fSDavid du Colombier 	EShow,
42*9a747e4fSDavid du Colombier 	ESize,
43*9a747e4fSDavid du Colombier 	EValue,
44*9a747e4fSDavid du Colombier };
45*9a747e4fSDavid du Colombier 
46*9a747e4fSDavid du Colombier static char *cmds[] = {
47*9a747e4fSDavid du Colombier 	[EAdd] =			"add",
48*9a747e4fSDavid du Colombier 	[EBorder] =		"border",
49*9a747e4fSDavid du Colombier 	[EBordercolor] =	"bordercolor",
50*9a747e4fSDavid du Colombier 	[EButton] =		"button",
51*9a747e4fSDavid du Colombier 	[EFocus] = 		"focus",
52*9a747e4fSDavid du Colombier 	[EFormat] = 		"format",
53*9a747e4fSDavid du Colombier 	[EHide] =			"hide",
54*9a747e4fSDavid du Colombier 	[EImage] =		"image",
55*9a747e4fSDavid du Colombier 	[ERect] =			"rect",
56*9a747e4fSDavid du Colombier 	[EReveal] =		"reveal",
57*9a747e4fSDavid du Colombier 	[ESeparation] =		"separation",
58*9a747e4fSDavid du Colombier 	[ESeparatorcolor] =	"separatorcolor",
59*9a747e4fSDavid du Colombier 	[EShow] =			"show",
60*9a747e4fSDavid du Colombier 	[ESize] =			"size",
61*9a747e4fSDavid du Colombier 	[EValue] =			"value",
62*9a747e4fSDavid du Colombier };
63*9a747e4fSDavid du Colombier 
64*9a747e4fSDavid du Colombier static void
tabshow(Tab * t)65*9a747e4fSDavid du Colombier tabshow(Tab *t)
66*9a747e4fSDavid du Colombier {
67*9a747e4fSDavid du Colombier 	int i;
68*9a747e4fSDavid du Colombier 	Rectangle r;
69*9a747e4fSDavid du Colombier 	Group *g;
70*9a747e4fSDavid du Colombier 
71*9a747e4fSDavid du Colombier 	if (t->hidden)
72*9a747e4fSDavid du Colombier 		return;
73*9a747e4fSDavid du Colombier 	for(i=0; i<t->nbuttons; i++){
74*9a747e4fSDavid du Colombier 		_ctlprint(t->buttons[i], "value %d", (t->selected==i));
75*9a747e4fSDavid du Colombier 	}
76*9a747e4fSDavid du Colombier 	_ctlprint(t->tabstack, "reveal %d", t->selected);
77*9a747e4fSDavid du Colombier 	_ctlprint(t->tabcolumn, "show");
78*9a747e4fSDavid du Colombier 	if (t->selected < 0)
79*9a747e4fSDavid du Colombier 		return;
80*9a747e4fSDavid du Colombier 	g = (Group*)t->tabcolumn;
81*9a747e4fSDavid du Colombier 	if (g->nseparators == 0){
82*9a747e4fSDavid du Colombier 		return;
83*9a747e4fSDavid du Colombier 	}
84*9a747e4fSDavid du Colombier 	r = g->separators[0];
85*9a747e4fSDavid du Colombier 	r.min.x = t->buttons[t->selected]->rect.min.x;
86*9a747e4fSDavid du Colombier 	r.max.x = t->buttons[t->selected]->rect.max.x;
87*9a747e4fSDavid du Colombier 	draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
88*9a747e4fSDavid du Colombier 	flushimage(display, 1);
89*9a747e4fSDavid du Colombier }
90*9a747e4fSDavid du Colombier 
91*9a747e4fSDavid du Colombier static void
tabsize(Control * c)92*9a747e4fSDavid du Colombier tabsize(Control *c)
93*9a747e4fSDavid du Colombier {
94*9a747e4fSDavid du Colombier 	Tab *t = (Tab*)c;
95*9a747e4fSDavid du Colombier 	if (t->tabcolumn && t->tabcolumn->setsize)
96*9a747e4fSDavid du Colombier 		t->tabcolumn->setsize((Control*)t->tabcolumn);
97*9a747e4fSDavid du Colombier }
98*9a747e4fSDavid du Colombier 
99*9a747e4fSDavid du Colombier static void
tabctl(Control * c,CParse * cp)100*9a747e4fSDavid du Colombier tabctl(Control *c, CParse *cp)
101*9a747e4fSDavid du Colombier {
102*9a747e4fSDavid du Colombier 	int cmd, i;
103*9a747e4fSDavid du Colombier 	Control *cbut, *cwin;
104*9a747e4fSDavid du Colombier 	Tab *t;
105*9a747e4fSDavid du Colombier 	Rectangle r;
106*9a747e4fSDavid du Colombier 
107*9a747e4fSDavid du Colombier 	t = (Tab*)c;
108*9a747e4fSDavid du Colombier 	cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
109*9a747e4fSDavid du Colombier 	switch(cmd){
110*9a747e4fSDavid du Colombier 	case EAdd:
111*9a747e4fSDavid du Colombier 		if ((cp->nargs & 1) == 0)
112*9a747e4fSDavid du Colombier 			ctlerror("%q: arg count: %s", t->name, cp->args[1]);
113*9a747e4fSDavid du Colombier 		for (i = 1; i < cp->nargs; i += 2){
114*9a747e4fSDavid du Colombier 			cbut = controlcalled(cp->args[i]);
115*9a747e4fSDavid du Colombier 			if (cbut == nil)
116*9a747e4fSDavid du Colombier 				ctlerror("%q: no such control: %s", t->name, cp->args[i]);
117*9a747e4fSDavid du Colombier 			cwin = controlcalled(cp->args[i+1]);
118*9a747e4fSDavid du Colombier 			if (cwin == nil)
119*9a747e4fSDavid du Colombier 				ctlerror("%q: no such control: %s", t->name, cp->args[i+1]);
120*9a747e4fSDavid du Colombier 			_ctladdgroup(t->tabrow, cbut);
121*9a747e4fSDavid du Colombier 			_ctlprint(t->tabstack, "add %q", cp->args[i+1]);
122*9a747e4fSDavid du Colombier 			_ctlprint(cbut, "format '%%s: %q button %%d'", t->name);
123*9a747e4fSDavid du Colombier 			controlwire(cbut, "event", t->controlset->ctl);
124*9a747e4fSDavid du Colombier 			t->buttons = ctlrealloc(t->buttons, (t->nbuttons+1)*sizeof(Control*));
125*9a747e4fSDavid du Colombier 			t->buttons[t->nbuttons] = cbut;
126*9a747e4fSDavid du Colombier 			t->nbuttons++;
127*9a747e4fSDavid du Colombier 			t->selected = -1;
128*9a747e4fSDavid du Colombier 		}
129*9a747e4fSDavid du Colombier 		_ctlprint(t->tabcolumn, "size");
130*9a747e4fSDavid du Colombier 		t->size = t->tabcolumn->size;
131*9a747e4fSDavid du Colombier 		break;
132*9a747e4fSDavid du Colombier 	case EBorder:
133*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
134*9a747e4fSDavid du Colombier 		if(cp->iargs[1] < 0)
135*9a747e4fSDavid du Colombier 			ctlerror("%q: bad border: %c", t->name, cp->str);
136*9a747e4fSDavid du Colombier 		t->border = cp->iargs[1];
137*9a747e4fSDavid du Colombier 		break;
138*9a747e4fSDavid du Colombier 	case EBordercolor:
139*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
140*9a747e4fSDavid du Colombier 		_setctlimage(t, &t->bordercolor, cp->args[1]);
141*9a747e4fSDavid du Colombier 		break;
142*9a747e4fSDavid du Colombier 	case EButton:
143*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
144*9a747e4fSDavid du Colombier 		if (cp->sender == nil)
145*9a747e4fSDavid du Colombier 			ctlerror("%q: senderless buttonevent: %q", t->name, cp->str);
146*9a747e4fSDavid du Colombier 		cbut = controlcalled(cp->sender);
147*9a747e4fSDavid du Colombier 		for(i=0; i<t->nbuttons; i++)
148*9a747e4fSDavid du Colombier 			if (cbut == t->buttons[i])
149*9a747e4fSDavid du Colombier 				break;
150*9a747e4fSDavid du Colombier 		if (i == t->nbuttons)
151*9a747e4fSDavid du Colombier 			ctlerror("%q: not my event: %q", t->name, cp->str);
152*9a747e4fSDavid du Colombier 		if(cp->iargs[1] == 0){
153*9a747e4fSDavid du Colombier 			/* button was turned off; turn it back on */
154*9a747e4fSDavid du Colombier 			_ctlprint(cbut, "value 1");
155*9a747e4fSDavid du Colombier 		}else{
156*9a747e4fSDavid du Colombier 			t->selected = i;
157*9a747e4fSDavid du Colombier 			if (t->format)
158*9a747e4fSDavid du Colombier 				chanprint(t->event, t->format, t->name, i);
159*9a747e4fSDavid du Colombier 			tabshow(t);
160*9a747e4fSDavid du Colombier 		}
161*9a747e4fSDavid du Colombier 		break;
162*9a747e4fSDavid du Colombier 	case EFocus:
163*9a747e4fSDavid du Colombier 		/* ignore focus change */
164*9a747e4fSDavid du Colombier 		break;
165*9a747e4fSDavid du Colombier 	case EFormat:
166*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
167*9a747e4fSDavid du Colombier 		t->format = ctlstrdup(cp->args[1]);
168*9a747e4fSDavid du Colombier 		break;
169*9a747e4fSDavid du Colombier 	case EImage:
170*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
171*9a747e4fSDavid du Colombier 		_setctlimage(t, &t->image, cp->args[1]);
172*9a747e4fSDavid du Colombier 		break;
173*9a747e4fSDavid du Colombier 	case ESeparation:
174*9a747e4fSDavid du Colombier 		t->tabrow->ctl(t->tabrow, cp);
175*9a747e4fSDavid du Colombier 		t->tabcolumn->ctl(t->tabcolumn, cp);
176*9a747e4fSDavid du Colombier 		break;
177*9a747e4fSDavid du Colombier 	case ERect:
178*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 5);
179*9a747e4fSDavid du Colombier 		r.min.x = cp->iargs[1];
180*9a747e4fSDavid du Colombier 		r.min.y = cp->iargs[2];
181*9a747e4fSDavid du Colombier 		r.max.x = cp->iargs[3];
182*9a747e4fSDavid du Colombier 		r.max.y = cp->iargs[4];
183*9a747e4fSDavid du Colombier 		if(Dx(r)<=0 || Dy(r)<=0)
184*9a747e4fSDavid du Colombier 			ctlerror("%q: bad rectangle: %s", t->name, cp->str);
185*9a747e4fSDavid du Colombier 		t->rect = r;
186*9a747e4fSDavid du Colombier 		r = insetrect(r, t->border);
187*9a747e4fSDavid du Colombier 		_ctlprint(t->tabcolumn, "rect %R", r);
188*9a747e4fSDavid du Colombier 		break;
189*9a747e4fSDavid du Colombier 	case EReveal:
190*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 1);
191*9a747e4fSDavid du Colombier 	case EHide:
192*9a747e4fSDavid du Colombier 	case ESize:
193*9a747e4fSDavid du Colombier 		t->tabcolumn->ctl(t->tabcolumn, cp);
194*9a747e4fSDavid du Colombier 		break;
195*9a747e4fSDavid du Colombier 	case EShow:
196*9a747e4fSDavid du Colombier 		tabshow(t);
197*9a747e4fSDavid du Colombier 		break;
198*9a747e4fSDavid du Colombier 	case EValue:
199*9a747e4fSDavid du Colombier 		_ctlargcount(t, cp, 2);
200*9a747e4fSDavid du Colombier 		if (cp->iargs[1] < 0 || cp->iargs[1] >= t->nbuttons)
201*9a747e4fSDavid du Colombier 			ctlerror("%q: illegal value '%s'", t->name, cp->str);
202*9a747e4fSDavid du Colombier 		t->selected = cp->iargs[1];
203*9a747e4fSDavid du Colombier 		tabshow(t);
204*9a747e4fSDavid du Colombier 		break;
205*9a747e4fSDavid du Colombier 	default:
206*9a747e4fSDavid du Colombier 		ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
207*9a747e4fSDavid du Colombier 		break;
208*9a747e4fSDavid du Colombier 	}
209*9a747e4fSDavid du Colombier }
210*9a747e4fSDavid du Colombier 
211*9a747e4fSDavid du Colombier static void
tabfree(Control * c)212*9a747e4fSDavid du Colombier tabfree(Control*c)
213*9a747e4fSDavid du Colombier {
214*9a747e4fSDavid du Colombier 	Tab *t = (Tab*)c;
215*9a747e4fSDavid du Colombier 	t->nbuttons = 0;
216*9a747e4fSDavid du Colombier 	free(t->buttons);
217*9a747e4fSDavid du Colombier 	t->buttons = 0;
218*9a747e4fSDavid du Colombier }
219*9a747e4fSDavid du Colombier 
220*9a747e4fSDavid du Colombier static void
activatetab(Control * c,int act)221*9a747e4fSDavid du Colombier activatetab(Control *c, int act)
222*9a747e4fSDavid du Colombier {
223*9a747e4fSDavid du Colombier 	Tab *t;
224*9a747e4fSDavid du Colombier 
225*9a747e4fSDavid du Colombier 	t = (Tab*)c;
226*9a747e4fSDavid du Colombier 	if (act)
227*9a747e4fSDavid du Colombier 		activate(t->tabcolumn);
228*9a747e4fSDavid du Colombier 	else
229*9a747e4fSDavid du Colombier 		deactivate(t->tabcolumn);
230*9a747e4fSDavid du Colombier }
231*9a747e4fSDavid du Colombier 
232*9a747e4fSDavid du Colombier Control *
createtab(Controlset * cs,char * name)233*9a747e4fSDavid du Colombier createtab(Controlset *cs, char *name)
234*9a747e4fSDavid du Colombier {
235*9a747e4fSDavid du Colombier 	char s[128];
236*9a747e4fSDavid du Colombier 
237*9a747e4fSDavid du Colombier 	Tab *t;
238*9a747e4fSDavid du Colombier 	t = (Tab*)_createctl(cs, "tab", sizeof(Tab), name);
239*9a747e4fSDavid du Colombier 	t->selected = -1;
240*9a747e4fSDavid du Colombier 	t->nbuttons = 0;
241*9a747e4fSDavid du Colombier 	t->ctl = tabctl;
242*9a747e4fSDavid du Colombier 	t->mouse = nil;
243*9a747e4fSDavid du Colombier 	t->exit = tabfree;
244*9a747e4fSDavid du Colombier 	snprint(s, sizeof s, "_%s-row", name);
245*9a747e4fSDavid du Colombier 	t->tabrow = createrow(cs, s);
246*9a747e4fSDavid du Colombier 	snprint(s, sizeof s, "_%s-stack", name);
247*9a747e4fSDavid du Colombier 	t->tabstack = createstack(cs, s);
248*9a747e4fSDavid du Colombier 	snprint(s, sizeof s, "_%s-column", name);
249*9a747e4fSDavid du Colombier 	t->tabcolumn = createcolumn(cs, s);
250*9a747e4fSDavid du Colombier 	ctlprint(t->tabcolumn, "add %q %q", t->tabrow->name, t->tabstack->name);
251*9a747e4fSDavid du Colombier 	t->bordercolor = _getctlimage("black");
252*9a747e4fSDavid du Colombier 	t->image = _getctlimage("white");
253*9a747e4fSDavid du Colombier 	t->setsize = tabsize;
254*9a747e4fSDavid du Colombier 	t->activate = activatetab;
255*9a747e4fSDavid du Colombier 	return (Control*)t;
256*9a747e4fSDavid du Colombier }
257