xref: /inferno-os/libtk/twind.c (revision 5849851a19380dbb62a47d9c4d868a81e42fa79b)
1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 #include "textw.h"
5 
6 #define istring u.string
7 #define iwin u.win
8 #define imark u.mark
9 #define iline u.line
10 
11 #define	O(t, e)		((long)(&((t*)0)->e))
12 
13 static char* tktwincget(Tk*, char*, char**);
14 static char* tktwinconfigure(Tk*, char*, char**);
15 static char* tktwincreate(Tk*, char*, char**);
16 static char* tktwinnames(Tk*, char*, char**);
17 static int winowned(Tk *tk, Tk *sub);
18 
19 static
20 TkStab tkalign[] =
21 {
22 	"top",		Tktop,
23 	"bottom",	Tkbottom,
24 	"center",	Tkcenter,
25 	"baseline",	Tkbaseline,
26 	nil
27 };
28 
29 static
30 TkOption twinopts[] =
31 {
32 	"align",	OPTstab,	O(TkTwind, align),	tkalign,
33 	"create",	OPTtext,	O(TkTwind, create),	nil,
34 	"padx",		OPTnndist,	O(TkTwind, padx),	nil,
35 	"pady",		OPTnndist,	O(TkTwind, pady),	nil,
36 	"stretch",	OPTstab,	O(TkTwind, stretch),	tkbool,
37 	"window",	OPTwinp,	O(TkTwind, sub),	nil,
38 	"ascent",	OPTdist,	O(TkTwind, ascent), nil,
39 	nil
40 };
41 
42 TkCmdtab
43 tktwincmd[] =
44 {
45 	"cget",		tktwincget,
46 	"configure",	tktwinconfigure,
47 	"create",	tktwincreate,
48 	"names",	tktwinnames,
49 	nil
50 };
51 
52 int
53 tktfindsubitem(Tk *sub, TkTindex *ix)
54 {
55 	Tk *tk, *isub;
56 	TkText *tkt;
57 
58 	tk = sub->parent;
59 	if(tk != nil) {
60 		tkt = TKobj(TkText, tk);
61 		tktstartind(tkt, ix);
62 		do {
63 			if(ix->item->kind == TkTwin) {
64 				isub = ix->item->iwin->sub;
65 				if(isub != nil &&
66 				   isub->name != nil &&
67 				   strcmp(isub->name->name, sub->name->name) == 0)
68 				return 1;
69 			}
70 		} while(tktadjustind(tkt, TkTbyitem, ix));
71 	}
72 	return 0;
73 }
74 
75 static void
76 tktwindsize(Tk *tk, TkTindex *ix)
77 {
78 	Tk *s;
79 	TkTitem *i;
80 	TkTwind *w;
81 
82 
83 	i = ix->item;
84 	/* assert(i->kind == TkTwin); */
85 
86 	w = i->iwin;
87 	s = w->sub;
88 	if(s == nil)
89 		return;
90 
91 	if(w->width != s->act.width || w->height != s->act.height) {
92 		s->act.width = w->width;
93 		s->act.height = w->height;
94 		if(s->slave) {
95 			tkpackqit(s);
96 			tkrunpack(tk->env->top);
97 		}
98 	}
99 
100 	tktfixgeom(tk, tktprevwrapline(tk, ix->line), ix->line, 0);
101 	tktextsize(tk, 1);
102 }
103 
104 /*
105  * check that w->focus is a window packed under tk.
106  * XXX couldn't this be done more simply by traversing
107  * directly upwards from w->focus and seeing whether
108  * it hits tk? (same applies to tkcvschkwfocus in cwind.c)
109  */
110 static int
111 tktchkwfocus(TkTwind *w, Tk *tk)
112 {
113 	if(w->focus == tk)
114 		return 1;
115 	for(tk = tk->slave; tk; tk = tk->next)
116 		if(tktchkwfocus(w, tk))
117 			return 1;
118 	return 0;
119 }
120 
121 static void
122 tktwingeom(Tk *sub, int x, int y, int w, int h)
123 {
124 	TkTindex ix;
125 	Tk *tk;
126 	TkTwind *win;
127 
128 	USED(x);
129 	USED(y);
130 
131 	tk = sub->parent;
132 	if(!tktfindsubitem(sub, &ix)) {
133 		print("tktwingeom: %s not found\n", sub->name->name);
134 		return;
135 	}
136 
137 	win = ix.item->iwin;
138 
139 	if(win->focus != nil) {
140 		if(tktchkwfocus(win, sub) == 0)
141 			win->focus = nil;
142 	}
143 
144 	win->width = w;
145 	win->height = h;
146 
147 	sub->req.width = w;
148 	sub->req.height = h;
149 	tktwindsize(tk, &ix);
150 }
151 
152 static void
153 tktdestroyed(Tk *sub)
154 {
155 	TkTindex ix;
156 	Tk *tk;
157 
158 	if(tktfindsubitem(sub, &ix)) {
159 		ix.item->iwin->sub = nil;
160 		ix.item->iwin->focus = nil;
161 		if((tk = sub->parent) != nil) {
162 			tktfixgeom(tk, tktprevwrapline(tk, ix.line), ix.line, 0);
163 			tktextsize(tk, 1);
164 			sub->parent = nil;
165 		}
166 	}
167 }
168 
169 void
170 tktdirty(Tk *sub)
171 {
172 	Tk *tk, *parent, *isub;
173 	TkText *tkt;
174 	TkTindex ix;
175 
176 	parent = nil;
177 	for(tk = sub; tk && parent == nil; tk = tk->master)
178 		parent = tk->parent;
179 	if(tk == nil)
180 		return;
181 
182 	tkt = TKobj(TkText, parent);
183 	tktstartind(tkt, &ix);
184 	do {
185 		if(ix.item->kind == TkTwin) {
186 			isub = ix.item->iwin->sub;
187 			if(isub != nil) {
188 				tktfixgeom(parent, tktprevwrapline(parent, ix.line), ix.line, 0);
189 				if (sub->flag & Tktransparent)
190 					parent->flag |= Tkrefresh;	/* XXX could be more efficient, by drawing the background locally? */
191 				return;
192 			}
193 		}
194 	} while(tktadjustind(tkt, TkTbyitem, &ix));
195 	tktextsize(parent, 1);
196 }
197 
198 static char*
199 tktwinchk(Tk *tk, TkTwind *w, Tk *oldsub)
200 {
201 	Tk *sub;
202 
203 	sub = w->sub;
204 	if (sub != oldsub) {
205 		w->sub = oldsub;
206 		if(sub == nil)
207 			return nil;
208 
209 		if(sub->flag & Tkwindow)
210 			return TkIstop;
211 
212 		if(sub->master != nil || sub->parent != nil)
213 			return TkWpack;
214 
215 		if (oldsub != nil) {
216 			oldsub->parent = nil;
217 			oldsub->geom = nil;
218 			oldsub->destroyed = nil;
219 		}
220 		w->sub = sub;
221 		w->focus = nil;
222 
223 		sub->parent = tk;
224 		tksetbits(sub, Tksubsub);
225 		sub->geom = tktwingeom;
226 		sub->destroyed = tktdestroyed;
227 
228 		w->width = sub->req.width;
229 		w->height = sub->req.height;
230 		w->owned = winowned(tk, sub);
231 	}
232 
233 	return nil;
234 }
235 
236 
237 /* Text Window Command (+ means implemented)
238 	+cget
239 	+configure
240 	+create
241 	+names
242 */
243 
244 static char*
245 tktwincget(Tk *tk, char *arg, char **val)
246 {
247 	char *e;
248 	TkTindex ix;
249 	TkOptab tko[2];
250 
251 	e = tktindparse(tk, &arg, &ix);
252 	if(e != nil)
253 		return e;
254 	if(ix.item->kind != TkTwin)
255 		return TkBadwp;
256 
257 	tko[0].ptr = ix.item->iwin;
258 	tko[0].optab = twinopts;
259 	tko[1].ptr = nil;
260 
261 	return tkgencget(tko, arg, val, tk->env->top);
262 }
263 
264 static char*
265 tktwinconfigure(Tk *tk, char *arg, char **val)
266 {
267 	char *e;
268 	TkTindex ix;
269 	TkOptab tko[2];
270 	Tk *oldsub;
271 
272 	USED(val);
273 
274 	e = tktindparse(tk, &arg, &ix);
275 	if(e != nil)
276 		return e;
277 	if(ix.item->kind != TkTwin)
278 		return TkBadwp;
279 
280 	oldsub = ix.item->iwin->sub;
281 
282 	tko[0].ptr = ix.item->iwin;
283 	tko[0].optab = twinopts;
284 	tko[1].ptr = nil;
285 
286 	e = tkparse(tk->env->top, arg, tko, nil);
287 	if(e != nil)
288 		return e;
289 
290 	e = tktwinchk(tk, ix.item->iwin, oldsub);
291 	if(e != nil)
292 		return e;
293 
294 	tktwindsize(tk, &ix);
295 	return nil;
296 }
297 
298 /*
299  * return true if tk is an ancestor of sub
300  */
301 static int
302 winowned(Tk *tk, Tk *sub)
303 {
304 	int len;
305 	if (tk->name == nil || sub->name == nil)
306 		return 0;
307 	len = strlen(tk->name->name);
308 	if (strncmp(tk->name->name, sub->name->name, len) == 0 &&
309 			sub->name->name[len] == '.')
310 		return 1;
311 	return 0;
312 }
313 
314 static char*
315 tktwincreate(Tk *tk, char *arg, char **val)
316 {
317 	char *e;
318 	TkTindex ix;
319 	TkTitem *i;
320 	TkText *tkt;
321 	TkOptab tko[2];
322 
323 	USED(val);
324 
325 	tkt = TKobj(TkText, tk);
326 
327 	e = tktindparse(tk, &arg, &ix);
328 	if(e != nil)
329 		return e;
330 
331 	e = tktnewitem(TkTwin, 0, &i);
332 	if(e != nil)
333 		return e;
334 
335 	i->iwin = malloc(sizeof(TkTwind));
336 	if(i->iwin == nil) {
337 		tktfreeitems(tkt, i, 1);
338 		return TkNomem;
339 	}
340 
341 	memset(i->iwin, 0, sizeof(TkTwind));
342 	i->iwin->align = Tkcenter;
343 	i->iwin->ascent = -1;
344 
345 	tko[0].ptr = i->iwin;
346 	tko[0].optab = twinopts;
347 	tko[1].ptr = nil;
348 
349 	e = tkparse(tk->env->top, arg, tko, nil);
350 	if(e != nil) {
351     err1:
352 		tktfreeitems(tkt, i, 1);
353 		return e;
354 	}
355 
356 	e = tktwinchk(tk, i->iwin, nil);
357 	if(e != nil)
358 		goto err1;
359 
360 	e = tktsplititem(&ix);
361 	if(e != nil)
362 		goto err1;
363 
364 	tktiteminsert(tkt, &ix, i);
365 	if(e != nil)
366 		goto err1;
367 
368 	tktadjustind(tkt, TkTbyitemback, &ix);
369 	tktwindsize(tk, &ix);
370 
371 	return nil;
372 }
373 
374 static char*
375 tktwinnames(Tk *tk, char *arg, char **val)
376 {
377 	char *e, *fmt;
378 	TkTindex ix;
379 	TkText *tkt = TKobj(TkText, tk);
380 
381 	USED(arg);
382 
383 	tktstartind(tkt, &ix);
384 	fmt = "%s";
385 	do {
386 		if(ix.item->kind == TkTwin &&
387 		   ix.item->iwin->sub != nil &&
388                      ix.item->iwin->sub->name != nil) {
389 			e = tkvalue(val, fmt, ix.item->iwin->sub->name->name);
390 			if(e != nil)
391 				return e;
392 			fmt = " %s";
393 		}
394 	} while(tktadjustind(tkt, TkTbyitem, &ix));
395 	return nil;
396 }
397