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