xref: /inferno-os/libtk/panel.c (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 
5 #define	O(t, e)		((long)(&((t*)0)->e))
6 
7 typedef struct TkPanel TkPanel;
8 struct TkPanel
9 {
10 	Image*	image;
11 	Image*	matte;
12 	Point		view;	/* vector from image origin to widget origin */
13 	Rectangle		r;		/* drawn rectangle (in image coords) */
14 	int		anchor;
15 	int		hasalpha;	/* does the image include an alpha channel? */
16 };
17 
18 static TkOption tkpanelopts[] =
19 {
20 	"anchor",	OPTflag,	O(TkPanel, anchor),	tkanchor,
21 	nil
22 };
23 
24 static int
tkdrawnrect(Image * image,Image * matte,Rectangle * r)25 tkdrawnrect(Image *image, Image *matte, Rectangle *r)
26 {
27 	*r = image->clipr;
28 	if (matte != nil) {
29 		if (!rectclip(r, matte->clipr))
30 			return 0;
31 		if (!matte->repl && !rectclip(r, matte->r))
32 			return 0;
33 	}
34 	if (!image->repl && !rectclip(r, image->r))
35 		return 0;
36 	return 1;
37 }
38 
39 char*
tkpanel(TkTop * t,char * arg,char ** ret)40 tkpanel(TkTop *t, char *arg, char **ret)
41 {
42 	TkOptab tko[3];
43 	Tk *tk;
44 	TkPanel *tkp;
45 	TkName *names;
46 	char *e;
47 
48 	tk = tknewobj(t, TKpanel, sizeof(Tk)+sizeof(TkPanel));
49 	if(tk == nil)
50 		return TkNomem;
51 
52 	tkp = TKobj(TkPanel, tk);
53 	tkp->anchor = Tkcenter;
54 
55 	tko[0].ptr = tk;
56 	tko[0].optab = tkgeneric;
57 	tko[1].ptr = tkp;
58 	tko[1].optab = tkpanelopts;
59 	tko[2].ptr = nil;
60 	names = nil;
61 
62 	e = tkparse(t, arg, tko, &names);
63 	if(e != nil) {
64 		tkfreeobj(tk);
65 		return e;
66 	}
67 
68 	tksettransparent(tk, tkhasalpha(tk->env, TkCbackgnd));
69 
70 	e = tkaddchild(t, tk, &names);
71 
72 	tkfreename(names);
73 	if (e != nil) {
74 		tkfreeobj(tk);
75 		return e;
76 	}
77 
78 	tk->name->link = nil;
79 	return tkvalue(ret, "%s", tk->name->name);
80 }
81 
82 void
tkgetpanelimage(Tk * tk,Image ** i,Image ** m)83 tkgetpanelimage(Tk *tk, Image **i, Image **m)
84 {
85 	TkPanel *tkp = TKobj(TkPanel, tk);
86 	*i = tkp->image;
87 	*m = tkp->matte;
88 }
89 
90 void
tksetpanelimage(Tk * tk,Image * image,Image * matte)91 tksetpanelimage(Tk *tk, Image *image, Image *matte)
92 {
93 	TkPanel *tkp = TKobj(TkPanel, tk);
94 	int ishuge;
95 	TkGeom g;
96 
97 	g = tk->req;
98 
99 	tkp->image = image;
100 	tkp->matte = matte;
101 
102 	if (!tkdrawnrect(image, matte, &tkp->r)) {
103 		tkp->r.min = image->r.min;
104 		tkp->r.max = image->r.min;
105 	}
106 
107 	tkp->view = tkp->r.min;		/* XXX do we actually want to keep the old one? */
108 	/*
109 	 * if both image and matte are replicated, then we've got no idea what
110 	 * the rectangle should be, so request zero size, and set origin to (0, 0).
111 	 */
112 	ishuge = (Dx(tkp->r) >= 10000000);
113 	if((tk->flag & Tksetwidth) == 0){
114 		if(ishuge)
115 			tk->req.width = 0;
116 		else
117 			tk->req.width = Dx(tkp->r);
118 	}
119 	if(ishuge)
120 		tkp->view.x = 0;
121 
122 	ishuge = (Dy(tkp->r) >= 10000000);
123 	if((tk->flag & Tksetheight) == 0){
124 		if(ishuge)
125 			tk->req.height = 0;
126 		else
127 			tk->req.height = Dy(tkp->r);
128 	}
129 	if(ishuge)
130 		tkp->view.y = 0;
131 
132 	tkp->hasalpha = tkchanhastype(image->chan, CAlpha);
133 	tkgeomchg(tk, &g, tk->borderwidth);
134 	tksettransparent(tk, tkp->hasalpha || tkhasalpha(tk->env, TkCbackgnd));
135 	tk->dirty = tkrect(tk, 0);
136 }
137 
138 static void
tkfreepanel(Tk * tk)139 tkfreepanel(Tk *tk)
140 {
141 	TkPanel *tkp = TKobj(TkPanel, tk);
142 	tkdelpanelimage(tk->env->top, tkp->image);
143 	tkdelpanelimage(tk->env->top, tkp->matte);
144 }
145 
146 static Point
tkpanelview(Tk * tk)147 tkpanelview(Tk *tk)
148 {
149 	int dx, dy;
150 	Point view;
151 	TkPanel *tkp = TKobj(TkPanel, tk);
152 
153 	dx = tk->act.width - Dx(tkp->r);
154 	dy = tk->act.height - Dy(tkp->r);
155 
156 	view = tkp->view;
157 
158 	if (dx > 0) {
159 		if((tkp->anchor & (Tkeast|Tkwest)) == 0)
160 			view.x -= dx/2;
161 		else
162 		if(tkp->anchor & Tkeast)
163 			view.x -= dx;
164 	}
165 	if (dy > 0) {
166 		if((tkp->anchor & (Tknorth|Tksouth)) == 0)
167 			view.y -= dy/2;
168 		else
169 		if(tkp->anchor & Tksouth)
170 			view.y -= dy;
171 	}
172 	return view;
173 }
174 
175 static char*
tkdrawpanel(Tk * tk,Point orig)176 tkdrawpanel(Tk *tk, Point orig)
177 {
178 	Rectangle r, pr;
179 	TkPanel *tkp = TKobj(TkPanel, tk);
180 	Image *i;
181 	int any;
182 	Point view, p;
183 
184 	i = tkimageof(tk);
185 	if (i == nil)
186 		return nil;
187 
188 	p.x = orig.x + tk->act.x + tk->borderwidth;
189 	p.y = orig.y + tk->act.y + tk->borderwidth;
190 
191 	view = tkpanelview(tk);
192 
193 	/*
194 	 * if the image doesn't fully cover the dirty rectangle, then
195 	 * paint some background in there
196 	 */
197 	r = rectsubpt(tkp->r, view);		/* convert to widget coords */
198 	pr = tkrect(tk, 0);
199 	any = rectclip(&r, pr);				/* clip to inside widget borders */
200 
201 	if (!any || tkp->hasalpha || !rectinrect(tk->dirty, r))
202 		draw(i, rectaddpt(tk->dirty, p), tkgc(tk->env, TkCbackgnd), nil, ZP);
203 
204 	if (any && rectclip(&r, tk->dirty))
205 		draw(i, rectaddpt(r, p), tkp->image, tkp->matte, addpt(r.min, view));
206 
207 	if (!rectinrect(tk->dirty, pr)) {
208 		p.x -= tk->borderwidth;
209 		p.y -= tk->borderwidth;
210 		tkdrawrelief(i, tk, p, TkCbackgnd, tk->relief);
211 	}
212 	return nil;
213 }
214 
215 static char*
tkpanelcget(Tk * tk,char * arg,char ** val)216 tkpanelcget(Tk *tk, char *arg, char **val)
217 {
218 	TkOptab tko[3];
219 	TkPanel *tkp = TKobj(TkPanel, tk);
220 
221 	tko[0].ptr = tk;
222 	tko[0].optab = tkgeneric;
223 	tko[1].ptr = tkp;
224 	tko[1].optab = tkpanelopts;
225 	tko[2].ptr = nil;
226 
227 	return tkgencget(tko, arg, val, tk->env->top);
228 }
229 
230 static char*
tkpanelcvt(Tk * tk,char * arg,int rel,int * p)231 tkpanelcvt(Tk *tk, char *arg, int rel, int *p)
232 {
233 	char buf[Tkmaxitem];
234 
235 	tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
236 	if(buf[0] == '\0')
237 		return TkBadvl;
238 	*p = atoi(buf) + rel;
239 	return nil;
240 }
241 
242 /*
243  * screen to image
244  */
245 static char*
tkpanelpanelx(Tk * tk,char * arg,char ** val)246 tkpanelpanelx(Tk *tk, char *arg, char **val)
247 {
248 	Point p;
249 	char *e;
250 
251 	USED(val);
252 	p = subpt(tkposn(tk), tkpanelview(tk));
253 	e = tkpanelcvt(tk, arg, -p.x, &p.x);
254 	if (e != nil)
255 		return e;
256 	return tkvalue(val, "%d", p.x);
257 }
258 
259 static char*
tkpanelpanely(Tk * tk,char * arg,char ** val)260 tkpanelpanely(Tk *tk, char *arg, char **val)
261 {
262 	Point p;
263 	char *e;
264 
265 	USED(val);
266 	p = subpt(tkposn(tk), tkpanelview(tk));
267 	e = tkpanelcvt(tk, arg, -p.y, &p.y);
268 	if (e != nil)
269 		return e;
270 	return tkvalue(val, "%d", p.y);
271 }
272 
273 /*
274  * image to screen
275  */
276 static char*
tkpanelscreenx(Tk * tk,char * arg,char ** val)277 tkpanelscreenx(Tk *tk, char *arg, char **val)
278 {
279 	Point p;
280 	char *e;
281 
282 	USED(val);
283 	p = subpt(tkposn(tk), tkpanelview(tk));
284 	e = tkpanelcvt(tk, arg, p.x, &p.x);
285 	if (e != nil)
286 		return e;
287 	return tkvalue(val, "%d", p.x);
288 }
289 
290 static char*
tkpanelscreeny(Tk * tk,char * arg,char ** val)291 tkpanelscreeny(Tk *tk, char *arg, char **val)
292 {
293 	Point p;
294 	char *e;
295 
296 	USED(val);
297 	p = subpt(tkposn(tk), tkpanelview(tk));
298 	e = tkpanelcvt(tk, arg, p.y, &p.y);
299 	if (e != nil)
300 		return e;
301 	return tkvalue(val, "%d", p.y);
302 }
303 
304 static char*
tkpanelconf(Tk * tk,char * arg,char ** val)305 tkpanelconf(Tk *tk, char *arg, char **val)
306 {
307 	char *e;
308 	TkGeom g;
309 	int bd;
310 	TkOptab tko[3];
311 	TkPanel *tkp = TKobj(TkPanel, tk);
312 
313 	tko[0].ptr = tk;
314 	tko[0].optab = tkgeneric;
315 	tko[1].ptr = tkp;
316 	tko[1].optab = tkpanelopts;
317 	tko[2].ptr = nil;
318 
319 	if(*arg == '\0')
320 		return tkconflist(tko, val);
321 
322 	g = tk->req;
323 	bd = tk->borderwidth;
324 	e = tkparse(tk->env->top, arg, tko, nil);
325 	tkgeomchg(tk, &g, bd);
326 	tksettransparent(tk, tkp->hasalpha || tkhasalpha(tk->env, TkCbackgnd));
327 
328 	tk->dirty = tkrect(tk, 1);
329 
330 	return e;
331 }
332 
333 static char*
tkpaneldirty(Tk * tk,char * arg,char ** val)334 tkpaneldirty(Tk *tk, char *arg, char **val)
335 {
336 	char buf[Tkmaxitem];
337 	int n, coords[4];
338 	Rectangle r;
339 	char *e, *p;
340 	TkPanel *tkp = TKobj(TkPanel, tk);
341 
342 	USED(val);
343 	n = 0;
344 	while (n < 4) {
345 		arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
346 		if (buf[0] == 0)
347 			break;
348 		p = buf;
349 		e = tkfrac(&p, &coords[n++], nil);
350 		if (e != nil)
351 			return TkBadvl;
352 	}
353 	if (n == 0)
354 		r = tkp->r;
355 	else {
356 		 if (n != 4)
357 			return TkBadvl;
358 		r.min.x = TKF2I(coords[0]);
359 		r.min.y = TKF2I(coords[1]);
360 		r.max.x = TKF2I(coords[2]);
361 		r.max.y = TKF2I(coords[3]);
362 	}
363 	if (rectclip(&r, tkp->r)) {
364 		r = rectsubpt(r, tkpanelview(tk));		/* convert to widget coords */
365 		if (rectclip(&r, tkrect(tk, 0)))			/* clip to visible area */
366 			combinerect(&tk->dirty, r);
367 	}
368 	return nil;
369 }
370 
371 static char*
tkpanelorigin(Tk * tk,char * arg,char ** val)372 tkpanelorigin(Tk *tk, char *arg, char **val)
373 {
374 	char *e;
375 	Point view;
376 	TkPanel *tkp = TKobj(TkPanel, tk);
377 
378 	e = tkxyparse(tk, &arg, &view);
379 	if (e != nil) {
380 		if (e == TkOparg)
381 			return tkvalue(val, "%d %d", tkp->view.x, tkp->view.y);
382 		return e;
383 	}
384 	tkp->view = view;
385 	tk->dirty = tkrect(tk, 0);
386 	return nil;
387 }
388 
389 static
390 TkCmdtab tkpanelcmd[] =
391 {
392 	"cget",			tkpanelcget,
393 	"configure",		tkpanelconf,
394 	"dirty",			tkpaneldirty,
395 	"origin",			tkpanelorigin,
396 	"panelx",			tkpanelpanelx,
397 	"panely",			tkpanelpanely,
398 	"screenx",			tkpanelscreenx,
399 	"screeny",			tkpanelscreeny,
400 	nil
401 };
402 
403 TkMethod panelmethod = {
404 	"panel",
405 	tkpanelcmd,
406 	tkfreepanel,
407 	tkdrawpanel
408 };
409