xref: /inferno-os/libtk/label.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 #include <lib9.h>
2 #include <kernel.h>
3 #include "draw.h"
4 #include "tk.h"
5 #include "label.h"
6 
7 #define	O(t, e)		((long)(&((t*)0)->e))
8 
9 /* Layout constants */
10 enum {
11 	CheckSpace = CheckButton + 2*CheckButtonBW + 2*ButtonBorder,
12 };
13 
14 TkOption tklabelopts[] =
15 {
16 	"text",		OPTtext,	O(TkLabel, text),	nil,
17 	"label",	OPTtext,	O(TkLabel, text),	nil,
18 	"underline",	OPTdist,	O(TkLabel, ul),		nil,
19 	"justify",	OPTflag,	O(TkLabel, justify),	tkjustify,
20 	"anchor",	OPTflag,	O(TkLabel, anchor),	tkanchor,
21 	"bitmap",	OPTbmap,	O(TkLabel, bitmap),	nil,
22 	"image",	OPTimag,	O(TkLabel, img),	nil,
23 	nil
24 };
25 
26 char*
27 tklabel(TkTop *t, char *arg, char **ret)
28 {
29 	Tk *tk;
30 	char *e;
31 	TkLabel *tkl;
32 	TkName *names;
33 	TkOptab tko[3];
34 
35 	tk = tknewobj(t, TKlabel, sizeof(Tk)+sizeof(TkLabel));
36 	if(tk == nil)
37 		return TkNomem;
38 
39 	tkl = TKobj(TkLabel, tk);
40 	tkl->ul = -1;
41 	tkl->justify = Tkleft;
42 
43 	tko[0].ptr = tk;
44 	tko[0].optab = tkgeneric;
45 	tko[1].ptr = tkl;
46 	tko[1].optab = tklabelopts;
47 	tko[2].ptr = nil;
48 
49 	names = nil;
50 	e = tkparse(t, arg, tko, &names);
51 	if(e != nil) {
52 		tkfreeobj(tk);
53 		return e;
54 	}
55 
56 	tksizelabel(tk);
57 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
58 
59 	e = tkaddchild(t, tk, &names);
60 	tkfreename(names);
61 	if(e != nil) {
62 		tkfreeobj(tk);
63 		return e;
64 	}
65 	tk->name->link = nil;
66 
67 	return tkvalue(ret, "%s", tk->name->name);
68 }
69 
70 static char*
71 tklabelcget(Tk *tk, char *arg, char **val)
72 {
73 	TkOptab tko[3];
74 	TkLabel *tkl = TKobj(TkLabel, tk);
75 
76 	tko[0].ptr = tk;
77 	tko[0].optab = tkgeneric;
78 	tko[1].ptr = tkl;
79 	tko[1].optab = tklabelopts;
80 	tko[2].ptr = nil;
81 
82 	return tkgencget(tko, arg, val, tk->env->top);
83 }
84 
85 static char*
86 tklabelconf(Tk *tk, char *arg, char **val)
87 {
88 	char *e;
89 	TkGeom g;
90 	int bd;
91 	TkOptab tko[3];
92 	TkLabel *tkl = TKobj(TkLabel, tk);
93 
94 	tko[0].ptr = tk;
95 	tko[0].optab = tkgeneric;
96 	tko[1].ptr = tkl;
97 	tko[1].optab = tklabelopts;
98 	tko[2].ptr = nil;
99 
100 	if(*arg == '\0')
101 		return tkconflist(tko, val);
102 
103 	g = tk->req;
104 	bd = tk->borderwidth;
105 	e = tkparse(tk->env->top, arg, tko, nil);
106 	tksizelabel(tk);
107 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
108 	tkgeomchg(tk, &g, bd);
109 
110 	tk->dirty = tkrect(tk, 1);
111 	return e;
112 }
113 
114 void
115 tksizelabel(Tk *tk)
116 {
117 	Point p;
118 	int w, h;
119 	TkLabel *tkl;
120 
121 	tkl = TKobj(TkLabel, tk);
122 	if(tkl->anchor == 0)
123 		tkl->anchor = Tkcenter;
124 
125 	w = 0;
126 	h = 0;
127 	tkl->textheight = 0;
128 	if(tkl->img != nil) {
129 		w = tkl->img->w + 2*Bitpadx;
130 		h = tkl->img->h + 2*Bitpady;
131 	}
132 	else
133 	if(tkl->bitmap != nil) {
134 		w = Dx(tkl->bitmap->r) + 2*Bitpadx;
135 		h = Dy(tkl->bitmap->r) + 2*Bitpady;
136 	}
137 	else
138 	if(tkl->text != nil) {
139 		p = tkstringsize(tk, tkl->text);
140 		w = p.x + 2*Textpadx;
141 		h = p.y + 2*Textpady;
142 		if(tkl->ul != -1 && tkl->ul > strlen(tkl->text))
143 			tkl->ul = -1;
144 		tkl->textheight = p.y;
145 	}
146 
147 	if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF) {
148 		w += CheckSpace;
149 		if(h < CheckSpace)
150 			h = CheckSpace;
151 	} else if(tk->type == TKcascade) {
152 		w += CheckButton + 2*CheckButtonBW;
153 		if(h < CheckButton)
154 			h = CheckButton;
155 	}
156 	w += 2*tk->highlightwidth;
157 	h += 2*tk->highlightwidth;
158 	tkl->w = w;
159 	tkl->h = h;
160 	if((tk->flag & Tksetwidth) == 0)
161 		tk->req.width = w;
162 	if((tk->flag & Tksetheight) == 0)
163 		tk->req.height = h;
164 }
165 
166 int
167 tklabelmargin(Tk *tk)
168 {
169 	TkLabel *tkl;
170 	Image *img;
171 
172 	if (tk->type == TKseparator)
173 		return 0;
174 	if (tk->type == TKlabel || tk->type == TKcascade) {
175 		tkl = TKobj(TkLabel, tk);
176 		img = nil;
177 		if (tkl->img != nil)
178 			img = tkl->img->img;
179 		else if (tkl->bitmap != nil)
180 			img = tkl->bitmap;
181 		if (img != nil)
182 			return Bitpadx;
183 		return Textpadx;
184 	}
185 	return tkbuttonmargin(tk);
186 }
187 
188 void
189 tkfreelabel(Tk *tk)
190 {
191 	Image *i;
192 	int locked;
193 	Display *d;
194 	TkLabel *tkl;
195 
196 	tkl = TKobj(TkLabel, tk);
197 
198 	if(tkl->text != nil)
199 		free(tkl->text);
200 	if(tkl->command != nil)
201 		free(tkl->command);
202 	if(tkl->value != nil)
203 		free(tkl->value);
204 	if(tkl->variable != nil) {
205 		tkfreevar(tk->env->top, tkl->variable, tk->flag & Tkswept);
206 		free(tkl->variable);
207 	}
208 	if(tkl->img != nil)
209 		tkimgput(tkl->img);
210 	i = tkl->bitmap;
211 	if(i != nil) {
212 		d = i->display;
213 		locked = lockdisplay(d);
214 		freeimage(i);
215 		if(locked)
216 			unlockdisplay(d);
217 	}
218 	if(tkl->menu != nil)
219 		free(tkl->menu);
220 }
221 
222 static void
223 tktriangle(Point u, Image *i, TkEnv *e)
224 {
225 	int j;
226 	Point p[3];
227 
228 	u.y++;
229 	p[0].x = u.x + CheckButton;
230 	p[0].y = u.y + CheckButton/2;
231 	p[1].x = u.x;
232 	p[1].y = u.y + CheckButton;
233 	p[2].x = u.x;
234 	p[2].y = u.y;
235 	fillpoly(i, p, 3, ~0, tkgc(e, TkCbackgnddark), p[0]);
236 	for(j = 0; j < 3; j++)
237 		p[j].y -= 2;
238 
239 	fillpoly(i, p, 3, ~0, tkgc(e, TkCbackgndlght), p[0]);
240 }
241 
242 /*
243  * draw TKlabel, TKcheckbutton, TKradiobutton
244  */
245 char*
246 tkdrawlabel(Tk *tk, Point orig)
247 {
248  	TkEnv *e;
249 	TkLabel *tkl;
250 	Rectangle r, s, mainr, focusr;
251 	int dx, dy, h;
252 	Point p, u, v, *pp;
253 	Image *i, *dst, *cd, *cl, *ct, *img;
254 	char *o;
255 	int relief, bgnd, fgnd;
256 
257 	e = tk->env;
258 
259 	dst = tkimageof(tk);
260 	if(dst == nil)
261 		return nil;
262 
263 	v.x = tk->act.width + 2*tk->borderwidth;
264 	v.y = tk->act.height + 2*tk->borderwidth;
265 
266 	r.min = ZP;
267 	r.max.x = v.x;
268 	r.max.y = v.y;
269 	focusr = insetrect(r, tk->borderwidth);
270 	mainr = insetrect(focusr, tk->highlightwidth);
271 	relief = tk->relief;
272 
273 	tkl = TKobj(TkLabel, tk);
274 
275 	fgnd = TkCforegnd;
276 	bgnd = TkCbackgnd;
277 	if (tk->flag & Tkdisabled)
278 		fgnd = TkCdisablefgnd;
279 	else if ((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator == BoolF && tkl->check)
280 		bgnd = TkCselect;
281 	else if (tk->flag & Tkactive) {
282 		fgnd = TkCactivefgnd;
283 		bgnd = TkCactivebgnd;
284 	}
285 
286 	i = tkitmp(e, r.max, bgnd);
287 	if(i == nil)
288 		return nil;
289 
290 	if(tk->flag & Tkactive)
291 		draw(i, r, tkgc(e, bgnd), nil, ZP);
292 
293 	p = mainr.min;
294 	h = tkl->h - 2 * tk->highlightwidth;
295 
296 	dx = tk->act.width - tkl->w - tk->ipad.x;
297 	dy = tk->act.height - tkl->h - tk->ipad.y;
298 	if((tkl->anchor & (Tknorth|Tksouth)) == 0)
299 		p.y += dy/2;
300 	else
301 	if(tkl->anchor & Tksouth)
302 		p.y += dy;
303 
304 	if((tkl->anchor & (Tkeast|Tkwest)) == 0)
305 		p.x += dx/2;
306 	else
307 	if(tkl->anchor & Tkeast)
308 		p.x += dx;
309 
310 	switch(tk->type) {
311 	case TKcheckbutton:
312 		if (tkl->indicator == BoolF) {
313 			relief = tkl->check?TKsunken:TKraised;
314 			break;
315 		}
316 		u.x = p.x + ButtonBorder;
317 		u.y = p.y + ButtonBorder + (h - CheckSpace) / 2;
318 
319 		cl = tkgc(e, bgnd+TkLightshade);
320 		cd = tkgc(e, bgnd+TkDarkshade);
321 		if(tkl->check) {
322 			tkbevel(i, u, CheckButton, CheckButton, CheckButtonBW, cd, cl);
323 			u.x += CheckButtonBW;
324 			u.y += CheckButtonBW;
325 			s.min = u;
326 			s.max.x = u.x + CheckButton;
327 			s.max.y = u.y + CheckButton;
328 			draw(i, s, tkgc(e, TkCselect), nil, ZP);
329 		}
330 		else
331 			tkbevel(i, u, CheckButton, CheckButton, CheckButtonBW, cl, cd);
332 		break;
333 	case TKradiobutton:
334 		if (tkl->indicator == BoolF) {
335 			relief = tkl->check?TKsunken:TKraised;
336 			break;
337 		}
338 		u.x = p.x + ButtonBorder;
339 		u.y = p.y + ButtonBorder + (h - CheckSpace) / 2;
340 		pp = mallocz(4*sizeof(Point), 0);
341 		if(pp == nil)
342 			return TkNomem;
343 		pp[0].x = u.x + CheckButton/2;
344 		pp[0].y = u.y;
345 		pp[1].x = u.x + CheckButton;
346 		pp[1].y = u.y + CheckButton/2;
347 		pp[2].x = pp[0].x;
348 		pp[2].y = u.y + CheckButton;
349 		pp[3].x = u.x;
350 		pp[3].y = pp[1].y;
351 		cl = tkgc(e, bgnd+TkLightshade);
352 		cd = tkgc(e, bgnd+TkDarkshade);
353 		if(tkl->check)
354 			fillpoly(i, pp, 4, ~0, tkgc(e, TkCselect), pp[0]);
355 		else {
356 			ct = cl;
357 			cl = cd;
358 			cd = ct;
359 		}
360 		line(i, pp[0], pp[1], 0, Enddisc, CheckButtonBW/2, cd, pp[0]);
361 		line(i, pp[1], pp[2], 0, Enddisc, CheckButtonBW/2, cl, pp[1]);
362 		line(i, pp[2], pp[3], 0, Enddisc, CheckButtonBW/2, cl, pp[2]);
363 		line(i, pp[3], pp[0], 0, Enddisc, CheckButtonBW/2, cd, pp[3]);
364 		free(pp);
365 		break;
366 	case TKcascade:
367 		u.x = mainr.max.x - CheckButton - CheckButtonBW;
368 		u.y = p.y + ButtonBorder + (h-CheckSpace)/2;
369 		tktriangle(u, i, e);
370 		break;
371 	case TKbutton:
372 		if ((tk->flag & (Tkactivated|Tkactive)) == (Tkactivated|Tkactive))
373 			relief = TKsunken;
374 		break;
375 	}
376 
377 	p.x += tk->ipad.x/2;
378 	p.y += tk->ipad.y/2;
379 	u = ZP;
380 	if(tk->type == TKbutton && relief == TKsunken) {
381 		u.x++;
382 		u.y++;
383 	}
384 	if((tk->type == TKcheckbutton || tk->type == TKradiobutton) && tkl->indicator != BoolF)
385 		u.x += CheckSpace;
386 
387 	img = nil;
388 	if (tkl->img != nil && tkl->img->img != nil)
389 		img = tkl->img->img;
390 	else if (tkl->bitmap != nil)
391 		img = tkl->bitmap;
392 	if (img != nil) {
393 		s.min.x = p.x + Bitpadx;
394 		s.min.y = p.y + Bitpady;
395 		s.max.x = s.min.x + Dx(img->r);
396 		s.max.y = s.min.y + Dy(img->r);
397 		s = rectaddpt(s, u);
398 		if (tkchanhastype(img->chan, CGrey))
399 			draw(i, s, tkgc(e, fgnd), img, ZP);
400 		else
401 			draw(i, s, img, nil, ZP);
402 	} else if(tkl->text != nil) {
403 		u.x += Textpadx;
404 		u.y += Textpady;
405 		ct = tkgc(e, fgnd);
406 
407 		p.y += (h - tkl->textheight) / 2;
408 		o = tkdrawstring(tk, i, addpt(u, p), tkl->text, tkl->ul, ct, tkl->justify);
409 		if(o != nil)
410 			return o;
411 	}
412 
413 	if (tkhaskeyfocus(tk))
414 		tkbox(i, focusr, tk->highlightwidth, tkgc(e, TkChighlightfgnd));
415 	tkdrawrelief(i, tk, ZP, bgnd, relief);
416 
417 	p.x = tk->act.x + orig.x;
418 	p.y = tk->act.y + orig.y;
419 	r = rectaddpt(r, p);
420 	draw(dst, r, i, nil, ZP);
421 
422 	return nil;
423 }
424 
425 char*
426 tksetvar(TkTop *top, char *c, char *newval)
427 {
428 	TkVar *v;
429 	TkWin *tkw;
430 	Tk *f, *m;
431 	void (*vc)(Tk*, char*, char*);
432 
433 	if (c == nil || c[0] == '\0')
434 		return nil;
435 
436 	v = tkmkvar(top, c, TkVstring);
437 	if(v == nil)
438 		return TkNomem;
439 	if(v->type != TkVstring)
440 		return TkNotvt;
441 
442 	if(newval == nil)
443 		newval = "";
444 
445 	if(v->value != nil) {
446 		if (strcmp(v->value, newval) == 0)
447 			return nil;
448 		free(v->value);
449 	}
450 
451 	v->value = strdup(newval);
452 	if(v->value == nil)
453 		return TkNomem;
454 
455 	for(f = top->root; f; f = f->siblings) {
456 		if(f->type == TKmenu) {
457 			tkw = TKobj(TkWin, f);
458 			for(m = tkw->slave; m; m = m->next)
459 				if ((vc = tkmethod[m->type]->varchanged) != nil)
460 					(*vc)(m, c, newval);
461 		} else
462 			if ((vc = tkmethod[f->type]->varchanged) != nil)
463 				(*vc)(f, c, newval);
464 	}
465 
466 	return nil;
467 }
468 
469 char*
470 tkvariable(TkTop *t, char *arg, char **ret)
471 {
472 	TkVar *v;
473 	char *fmt, *e, *buf, *ebuf, *val;
474 	int l;
475 
476 	l = strlen(arg) + 2;
477 	buf = malloc(l);
478 	if(buf == nil)
479 		return TkNomem;
480 	ebuf = buf+l;
481 
482 	arg = tkword(t, arg, buf, ebuf, nil);
483 	arg = tkskip(arg, " \t");
484 	if (*arg == '\0') {
485 		if(strcmp(buf, "lasterror") == 0) {
486 			free(buf);
487 			if(t->err == nil)
488 				return nil;
489 			fmt = "%s: %s";
490 			if(strlen(t->errcmd) == sizeof(t->errcmd)-1)
491 				fmt = "%s...: %s";
492 			e = tkvalue(ret, fmt, t->errcmd, t->err);
493 			t->err = nil;
494 			return e;
495 		}
496 		v = tkmkvar(t, buf, 0);
497 		free(buf);
498 		if(v == nil || v->value == nil)
499 			return nil;
500 		if(v->type != TkVstring)
501 			return TkNotvt;
502 		return tkvalue(ret, "%s", v->value);
503 	}
504 	val = buf+strlen(buf)+1;
505 	tkword(t, arg, val, ebuf, nil);
506 	e = tksetvar(t, buf, val);
507 	free(buf);
508 	return e;
509 }
510 
511 void
512 tklabelgetimgs(Tk *tk, Image **image, Image **mask)
513 {
514 	TkLabel *tkl;
515 
516 	tkl = TKobj(TkLabel, tk);
517 	*mask = nil;
518 	if (tkl->img != nil)
519 		*image = tkl->img->img;
520 	else
521 		*image = tkl->bitmap;
522 }
523 
524 static
525 TkCmdtab tklabelcmd[] =
526 {
527 	"cget",			tklabelcget,
528 	"configure",		tklabelconf,
529 	nil
530 };
531 
532 TkMethod labelmethod = {
533 	"label",
534 	tklabelcmd,
535 	tkfreelabel,
536 	tkdrawlabel,
537 	nil,
538 	tklabelgetimgs
539 };
540