xref: /inferno-os/libtk/twind.c (revision 06155dbb53eb0585800b239acd6e45b946c6e0cf)
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 void
105 tktxtforgetsub(Tk *sub, Tk *tk)
106 {
107 	TkTwind *w;
108 	TkTindex ix;
109 
110 	if(!tktfindsubitem(sub, &ix))
111 		return;
112 	w = ix.item->iwin;
113 	if(w->focus == tk) {
114 if(0)print("tktxtforget sub %p %q focus %p %q\n", sub, tkname(sub), tk, tkname(tk));
115 		w->focus = nil;
116 	}
117 }
118 
119 static void
120 tktwingeom(Tk *sub, int x, int y, int w, int h)
121 {
122 	TkTindex ix;
123 	Tk *tk;
124 	TkTwind *win;
125 
126 	USED(x);
127 	USED(y);
128 
129 	tk = sub->parent;
130 	if(!tktfindsubitem(sub, &ix)) {
131 		print("tktwingeom: %s not found\n", sub->name->name);
132 		return;
133 	}
134 
135 	win = ix.item->iwin;
136 
137 	win->width = w;
138 	win->height = h;
139 
140 	sub->req.width = w;
141 	sub->req.height = h;
142 	tktwindsize(tk, &ix);
143 }
144 
145 static void
146 tktdestroyed(Tk *sub)
147 {
148 	TkTindex ix;
149 	Tk *tk;
150 
151 	if(tktfindsubitem(sub, &ix)) {
152 		ix.item->iwin->sub = nil;
153 		ix.item->iwin->focus = nil;
154 		if((tk = sub->parent) != nil) {
155 			tktfixgeom(tk, tktprevwrapline(tk, ix.line), ix.line, 0);
156 			tktextsize(tk, 1);
157 			sub->parent = nil;
158 		}
159 	}
160 }
161 
162 void
163 tktdirty(Tk *sub)
164 {
165 	Tk *tk, *parent, *isub;
166 	TkText *tkt;
167 	TkTindex ix;
168 
169 	parent = nil;
170 	for(tk = sub; tk && parent == nil; tk = tk->master)
171 		parent = tk->parent;
172 	if(tk == nil)
173 		return;
174 
175 	tkt = TKobj(TkText, parent);
176 	tktstartind(tkt, &ix);
177 	do {
178 		if(ix.item->kind == TkTwin) {
179 			isub = ix.item->iwin->sub;
180 			if(isub != nil) {
181 				tktfixgeom(parent, tktprevwrapline(parent, ix.line), ix.line, 0);
182 				if (sub->flag & Tktransparent)
183 					parent->flag |= Tkrefresh;	/* XXX could be more efficient, by drawing the background locally? */
184 				return;
185 			}
186 		}
187 	} while(tktadjustind(tkt, TkTbyitem, &ix));
188 	tktextsize(parent, 1);
189 }
190 
191 static char*
192 tktwinchk(Tk *tk, TkTwind *w, Tk *oldsub)
193 {
194 	Tk *sub;
195 
196 	sub = w->sub;
197 	if (sub != oldsub) {
198 		w->sub = oldsub;
199 		if(sub == nil)
200 			return nil;
201 
202 		if(sub->flag & Tkwindow)
203 			return TkIstop;
204 
205 		if(sub->master != nil || sub->parent != nil)
206 			return TkWpack;
207 
208 		if (oldsub != nil) {
209 			oldsub->parent = nil;
210 			oldsub->geom = nil;
211 			oldsub->destroyed = nil;
212 		}
213 		w->sub = sub;
214 		w->focus = nil;
215 
216 		sub->parent = tk;
217 		tksetbits(sub, Tksubsub);
218 		sub->geom = tktwingeom;
219 		sub->destroyed = tktdestroyed;
220 
221 		w->width = sub->req.width;
222 		w->height = sub->req.height;
223 		w->owned = winowned(tk, sub);
224 	}
225 
226 	return nil;
227 }
228 
229 
230 /* Text Window Command (+ means implemented)
231 	+cget
232 	+configure
233 	+create
234 	+names
235 */
236 
237 static char*
238 tktwincget(Tk *tk, char *arg, char **val)
239 {
240 	char *e;
241 	TkTindex ix;
242 	TkOptab tko[2];
243 
244 	e = tktindparse(tk, &arg, &ix);
245 	if(e != nil)
246 		return e;
247 	if(ix.item->kind != TkTwin)
248 		return TkBadwp;
249 
250 	tko[0].ptr = ix.item->iwin;
251 	tko[0].optab = twinopts;
252 	tko[1].ptr = nil;
253 
254 	return tkgencget(tko, arg, val, tk->env->top);
255 }
256 
257 static char*
258 tktwinconfigure(Tk *tk, char *arg, char **val)
259 {
260 	char *e;
261 	TkTindex ix;
262 	TkOptab tko[2];
263 	Tk *oldsub;
264 
265 	USED(val);
266 
267 	e = tktindparse(tk, &arg, &ix);
268 	if(e != nil)
269 		return e;
270 	if(ix.item->kind != TkTwin)
271 		return TkBadwp;
272 
273 	oldsub = ix.item->iwin->sub;
274 
275 	tko[0].ptr = ix.item->iwin;
276 	tko[0].optab = twinopts;
277 	tko[1].ptr = nil;
278 
279 	e = tkparse(tk->env->top, arg, tko, nil);
280 	if(e != nil)
281 		return e;
282 
283 	e = tktwinchk(tk, ix.item->iwin, oldsub);
284 	if(e != nil)
285 		return e;
286 
287 	tktwindsize(tk, &ix);
288 	return nil;
289 }
290 
291 /*
292  * return true if tk is an ancestor of sub
293  */
294 static int
295 winowned(Tk *tk, Tk *sub)
296 {
297 	int len;
298 	if (tk->name == nil || sub->name == nil)
299 		return 0;
300 	len = strlen(tk->name->name);
301 	if (strncmp(tk->name->name, sub->name->name, len) == 0 &&
302 			sub->name->name[len] == '.')
303 		return 1;
304 	return 0;
305 }
306 
307 static char*
308 tktwincreate(Tk *tk, char *arg, char **val)
309 {
310 	char *e;
311 	TkTindex ix;
312 	TkTitem *i;
313 	TkText *tkt;
314 	TkOptab tko[2];
315 
316 	USED(val);
317 
318 	tkt = TKobj(TkText, tk);
319 
320 	e = tktindparse(tk, &arg, &ix);
321 	if(e != nil)
322 		return e;
323 
324 	e = tktnewitem(TkTwin, 0, &i);
325 	if(e != nil)
326 		return e;
327 
328 	i->iwin = malloc(sizeof(TkTwind));
329 	if(i->iwin == nil) {
330 		tktfreeitems(tkt, i, 1);
331 		return TkNomem;
332 	}
333 
334 	memset(i->iwin, 0, sizeof(TkTwind));
335 	i->iwin->align = Tkcenter;
336 	i->iwin->ascent = -1;
337 
338 	tko[0].ptr = i->iwin;
339 	tko[0].optab = twinopts;
340 	tko[1].ptr = nil;
341 
342 	e = tkparse(tk->env->top, arg, tko, nil);
343 	if(e != nil) {
344     err1:
345 		tktfreeitems(tkt, i, 1);
346 		return e;
347 	}
348 
349 	e = tktwinchk(tk, i->iwin, nil);
350 	if(e != nil)
351 		goto err1;
352 
353 	e = tktsplititem(&ix);
354 	if(e != nil)
355 		goto err1;
356 
357 	tktiteminsert(tkt, &ix, i);
358 	if(e != nil)
359 		goto err1;
360 
361 	tktadjustind(tkt, TkTbyitemback, &ix);
362 	tktwindsize(tk, &ix);
363 
364 	return nil;
365 }
366 
367 static char*
368 tktwinnames(Tk *tk, char *arg, char **val)
369 {
370 	char *e, *fmt;
371 	TkTindex ix;
372 	TkText *tkt = TKobj(TkText, tk);
373 
374 	USED(arg);
375 
376 	tktstartind(tkt, &ix);
377 	fmt = "%s";
378 	do {
379 		if(ix.item->kind == TkTwin &&
380 		   ix.item->iwin->sub != nil &&
381                      ix.item->iwin->sub->name != nil) {
382 			e = tkvalue(val, fmt, ix.item->iwin->sub->name->name);
383 			if(e != nil)
384 				return e;
385 			fmt = " %s";
386 		}
387 	} while(tktadjustind(tkt, TkTbyitem, &ix));
388 	return nil;
389 }
390