xref: /inferno-os/libtk/grids.c (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
1 #include "lib9.h"
2 #include "draw.h"
3 #include "tk.h"
4 
5 /*
6  * XXX TODO
7  * - grid rowcget|columncget
8  * - grid columnconfigure/rowconfigure accepts a list of indexes?
9  */
10 
11 #define	O(t, e)		((long)(&((t*)0)->e))
12 
13 typedef struct TkGridparam TkGridparam;
14 typedef struct TkBeamparam TkBeamparam;
15 
16 struct TkGridparam{
17 	Point	span;
18 	Tk*	in;
19 	Point	pad;
20 	Point ipad;
21 	char	*row;
22 	char *col;
23 	int	sticky;
24 };
25 
26 struct TkBeamparam{
27 	int	minsize;
28 	int	maxsize;
29 	int	weight;
30 	int	pad;
31 	char	*name;
32 	int	equalise;
33 };
34 
35 static
36 TkOption opts[] =
37 {
38 	"padx",		OPTnndist,	O(TkGridparam, pad.x),	nil,
39 	"pady",		OPTnndist,	O(TkGridparam, pad.y),	nil,
40 	"ipadx",	OPTnndist,	O(TkGridparam, ipad.x),	nil,
41 	"ipady",	OPTnndist,	O(TkGridparam, ipad.y),	nil,
42 	"in",		OPTwinp,		O(TkGridparam, in),		nil,
43 	"row",	OPTtext,		O(TkGridparam, row), nil,
44 	"column",	OPTtext,		O(TkGridparam, col), nil,
45 	"rowspan",	OPTnndist,	O(TkGridparam, span.y), nil,
46 	"columnspan",	OPTnndist,	O(TkGridparam, span.x), nil,
47 	"sticky",	OPTsticky,		O(TkGridparam, sticky), nil,
48 	nil
49 };
50 
51 static
52 TkOption beamopts[] =
53 {
54 	"minsize",		OPTnndist,	O(TkBeamparam, minsize),	nil,
55 	"maxsize",	OPTnndist,	O(TkBeamparam, maxsize),	nil,
56 	"weight",		OPTnndist,	O(TkBeamparam, weight),	nil,
57 	"pad",		OPTnndist,	O(TkBeamparam, pad),		nil,
58 	"name",		OPTtext,		O(TkBeamparam, name),		nil,
59 	"equalise",	OPTstab,		O(TkBeamparam, equalise),	tkbool,
60 	nil
61 };
62 
63 void
64 printgrid(TkGrid *grid)
65 {
66 	int x, y;
67 	Point dim;
68 
69 	dim = grid->dim;
70 	print("grid %P\n", grid->dim);
71 	print("  row heights: ");
72 	for(y = 0; y < dim.y; y++)
73 		print("%d[%d,%d,w%d,p%d]%s ",
74 			grid->rows[y].act,
75 			grid->rows[y].minsize,
76 			grid->rows[y].maxsize < 0x7fffffff ? grid->rows[y].maxsize : -1,
77 			grid->rows[y].weight,
78 			grid->rows[y].pad,
79 			grid->rows[y].name ? grid->rows[y].name : "");
80 	print("\n");
81 	print("  col widths: ");
82 	for(x = 0; x < dim.x; x++)
83 		print("%d[%d,%d,w%d,p%d]%s ",
84 			grid->cols[x].act,
85 			grid->cols[x].minsize,
86 			grid->cols[x].maxsize < 0x7fffffff ? grid->cols[x].maxsize : -1,
87 			grid->cols[x].weight,
88 			grid->cols[x].pad,
89 			grid->cols[x].name ? grid->cols[x].name : "");
90 	print("\n");
91 	for(y = 0; y < dim.y; y++){
92 		print("  row %d: ", y);
93 		for(x = 0; x < dim.x; x++){
94 			print("%p;", grid->cells[y][x].tk);
95 			print("%s%P\t", grid->cells[y][x].tk?grid->cells[y][x].tk->name->name:"(nil)",
96 				grid->cells[y][x].span);
97 		}
98 		print("\n");
99 	}
100 }
101 
102 static void
103 tkgridsetopt(TkGridparam *p, Tk *tk)
104 {
105 	if(p->pad.x != -1)
106 		tk->pad.x = p->pad.x*2;
107 	if(p->pad.y != -1)
108 		tk->pad.y = p->pad.y*2;
109 	if(p->ipad.x != -1)
110 		tk->ipad.x = p->ipad.x*2;
111 	if(p->ipad.y != -1)
112 		tk->ipad.y = p->ipad.y*2;
113 	if(p->sticky != -1)
114 		tk->flag = (tk->flag & ~(Tkanchor|Tkfill)) | (p->sticky & (Tkanchor|Tkfill));
115 }
116 
117 static void
118 initbeam(TkGridbeam *beam, int n)
119 {
120 	int i;
121 	memset(beam, 0, n * sizeof(TkGridbeam));
122 	for(i = 0; i < n; i++)
123 		beam[i].maxsize = 0x7fffffff;
124 }
125 
126 static char*
127 ensuregridsize(TkGrid *grid, Point dim)
128 {
129 	TkGridcell **cells, *cellrow;
130 	TkGridbeam *cols, *rows;
131 	Point olddim;
132 	int i;
133 	olddim = grid->dim;
134 	if(dim.x < olddim.x)
135 		dim.x = olddim.x;
136 	if(dim.y < olddim.y)
137 		dim.y = olddim.y;
138 	if(dim.y > olddim.y){
139 		cells = realloc(grid->cells, sizeof(TkGridcell*)*dim.y);
140 		if(cells == nil)
141 			return TkNomem;
142 		grid->cells = cells;
143 		for(i = olddim.y; i < dim.y; i++){
144 			cells[i] = malloc(sizeof(TkGridcell)*dim.x);
145 			if(cells[i] == nil){
146 				for(i--; i >= olddim.y; i--)
147 					free(cells[i]);
148 				return TkNomem;
149 			}
150 		}
151 		rows = realloc(grid->rows, sizeof(TkGridbeam)*dim.y);
152 		if(rows == nil)
153 			return TkNomem;
154 		grid->rows = rows;
155 		initbeam(rows + olddim.y, dim.y - olddim.y);
156 		grid->dim.y = dim.y;
157 	}
158 
159 	if(dim.x > olddim.x){
160 		/*
161 		 * any newly allocated rows will have the correct number of
162 		 * columns, so we don't need to reallocate them
163 		 */
164 		cells = grid->cells;
165 		for(i = 0; i < olddim.y; i++){
166 			cellrow = realloc(cells[i], sizeof(TkGridcell) * dim.x);
167 			if(cellrow == nil)
168 				return TkNomem;	/* leak some earlier rows, but not permanently */
169 			memset(cellrow + olddim.x, 0, (dim.x-olddim.x)*sizeof(TkGridcell));
170 			cells[i] = cellrow;
171 		}
172 		cols = realloc(grid->cols, sizeof(TkGridbeam)*dim.x);
173 		if(cols == nil)
174 			return TkNomem;
175 		initbeam(cols + olddim.x, dim.x - olddim.x);
176 		grid->cols = cols;
177 		grid->dim.x = dim.x;
178 	}
179 	return nil;
180 }
181 
182 static TkGridbeam*
183 delbeams(TkGridbeam *beam, int nb, int x0, int x1)
184 {
185 	int i;
186 	TkGridbeam *b;
187 	for(i = x0; i < x1; i++)
188 		free(beam[i].name);
189 	memmove(&beam[x0], &beam[x1], sizeof(TkGridbeam) * (nb-x1));
190 	b = realloc(beam, sizeof(TkGridbeam) * (nb-(x1-x0)));
191 	return b ? b : beam;
192 }
193 
194 static void
195 delrows(TkGrid *grid, int y0, int y1)
196 {
197 	TkGridcell **cells;
198 	memmove(grid->cells+y0, grid->cells+y1, sizeof(TkGridcell*) * (grid->dim.y-y1));
199 	grid->dim.y -= (y1 - y0);
200 	cells = realloc(grid->cells, sizeof(TkGridcell*) * grid->dim.y);
201 	if(cells != nil || grid->dim.y == 0)
202 		grid->cells = cells;		/* can realloc to a smaller size ever fail? */
203 }
204 
205 static void
206 delcols(TkGrid *grid, int x0, int x1)
207 {
208 	TkGridcell **cells, *row;
209 	int y, ndx;
210 	Point dim;
211 	dim = grid->dim;
212 	ndx = dim.x - (x1 - x0);
213 	cells = grid->cells;
214 	for(y = 0; y < dim.y; y++){
215 		row = cells[y];
216 		memmove(row+x0, row+x1, sizeof(TkGridcell) * (dim.x - x1));
217 		row = realloc(row, sizeof(TkGridcell) * ndx);
218 		if(row != nil || ndx == 0)
219 			cells[y] = row;
220 	}
221 	grid->dim.x = ndx;
222 }
223 
224 /*
225  * insert items into rows/cols; the beam has already been expanded appropriately.
226  */
227 void
228 insbeams(TkGridbeam *beam, int nb, int x, int n)
229 {
230 	memmove(&beam[x+n], &beam[x], sizeof(TkGridbeam)*(nb-x-n));
231 	initbeam(beam+x, n);
232 }
233 
234 static char*
235 insrows(TkGrid *grid, int y0, int n)
236 {
237 	Point olddim;
238 	char *e;
239 	TkGridcell **cells, *tmp;
240 	int y;
241 
242 	olddim = grid->dim;
243 	if(y0 > olddim.y){
244 		n = y0 + n - olddim.y;
245 		y0 = olddim.y;
246 	}
247 
248 	e = ensuregridsize(grid, Pt(olddim.x, olddim.y + n));
249 	if(e != nil)
250 		return e;
251 	/*
252 	 * we know the extra rows will have been filled
253 	 * with blank, properly allocated rows, so just swap 'em with the
254 	 * ones that need moving.
255 	 */
256 	cells = grid->cells;
257 	for(y = olddim.y - 1; y >= y0; y--){
258 		tmp = cells[y + n];
259 		cells[y + n] = cells[y];
260 		cells[y] = tmp;
261 	}
262 	insbeams(grid->rows, grid->dim.y, y0, n);
263 	return nil;
264 }
265 
266 static char*
267 inscols(TkGrid *grid, int x0, int n)
268 {
269 	TkGridcell **cells;
270 	Point olddim;
271 	int y;
272 	char *e;
273 
274 	olddim = grid->dim;
275 	if(x0 > olddim.x){
276 		n = x0 + n - olddim.x;
277 		x0 = olddim.x;
278 	}
279 
280 	e = ensuregridsize(grid, Pt(olddim.x + n, olddim.y));
281 	if(e != nil)
282 		return e;
283 
284 	cells = grid->cells;
285 	for(y = 0; y < olddim.y; y++){
286 		memmove(cells[y] + x0 + n, cells[y] + x0, sizeof(TkGridcell) * (olddim.x - x0));
287 		memset(cells[y] + x0, 0, sizeof(TkGridcell) * n);
288 	}
289 	insbeams(grid->cols, grid->dim.x, x0, n);
290 	return nil;
291 }
292 
293 static int
294 maximum(int a, int b)
295 {
296 	if(a > b)
297 		return a;
298 	return b;
299 }
300 
301 /*
302  * return the width of cols/rows between x0 and x1 in the beam,
303  * excluding the padding at either end, but including padding in the middle.
304  */
305 static int
306 beamsize(TkGridbeam *cols, int x0, int x1)
307 {
308 	int tot, fpad, x;
309 
310 	if(x0 >= x1)
311 		return 0;
312 
313 	tot = cols[x0].act;
314 	fpad = cols[x0].pad;
315 	for(x = x0 + 1; x < x1; x++){
316 		tot += cols[x].act + maximum(cols[x].pad, fpad);
317 		fpad = cols[x].pad;
318 	}
319 	return tot;
320 }
321 
322 /*
323  * return starting position of cell index on beam, relative
324  * to top-left of grid
325  */
326 static int
327 beamcellpos(TkGridbeam *beam, int blen, int index)
328 {
329 	int x;
330 	if(blen == 0 || index >= blen || index < 0)
331 		return 0;
332 	x = beam[0].pad + beamsize(beam, 0, index);
333 	if(index > 0)
334 		x += maximum(beam[index-1].pad, beam[index].pad);
335 	return x;
336 }
337 
338 static Rectangle
339 cellbbox(TkGrid *grid, Point pos)
340 {
341 	Point dim;
342 	Rectangle r;
343 
344 	dim = grid->dim;
345 	if(pos.x > dim.x)
346 		pos.x = dim.x;
347 	if(pos.y > dim.y)
348 		pos.y = dim.y;
349 
350 	r.min.x = beamcellpos(grid->cols, dim.x, pos.x);
351 	r.min.y = beamcellpos(grid->rows, dim.y, pos.y);
352 	if(pos.x == dim.x)
353 		r.max.x = r.min.x;
354 	else
355 		r.max.x = r.min.x + grid->cols[pos.x].act;
356 	if(pos.y == dim.y)
357 		r.max.y = r.min.y;
358 	else
359 		r.max.y = r.min.y + grid->rows[pos.y].act;
360 	return rectaddpt(r, grid->origin);
361 }
362 
363 /*
364  * return true ifthere are any spanning cells covering row _index_
365  */
366 static int
367 gridrowhasspan(TkGrid *grid, int index)
368 {
369 	int i, d;
370 	Point dim;
371 	TkGridcell *cell;
372 
373 	dim = grid->dim;
374 	if(index > 0 && index < dim.y){
375 		for(i = 0; i < dim.x; i++){
376 			cell = &grid->cells[index][i];
377 			if(cell->tk != nil){
378 				d = cell->span.x;
379 				if(d == 0)
380 					return 1;
381 				i += d - 1;
382 			}
383 		}
384 	}
385 	return 0;
386 }
387 
388 /*
389  * return true ifthere are any spanning cells covering column _index_
390  */
391 static int
392 gridcolhasspan(TkGrid *grid, int index)
393 {
394 	int i, d;
395 	Point dim;
396 	TkGridcell *cell;
397 
398 	dim = grid->dim;
399 	if(index > 0 && index < dim.x){
400 		for(i = 0; i < dim.y; i++){
401 			cell = &grid->cells[i][index];
402 			if(cell->tk != nil){
403 				d = cell->span.y;
404 				if(d == 0)
405 					return 1;
406 				i += d - 1;
407 			}
408 		}
409 	}
410 	return 0;
411 }
412 
413 /*
414  * find cell that's spanning the grid position p
415  */
416 static int
417 findspan(TkGrid *grid, Point p, Point *cp)
418 {
419 	Point dim;
420 	TkGridcell **cells;
421 	Tk *tk;
422 
423 	dim = grid->dim;
424 	cells = grid->cells;
425 
426 	if(p.x < 0 || p.y < 0 || p.x >= dim.x || p.y >= dim.y)
427 		return 0;
428 
429 	if(cells[p.y][p.x].tk == nil)
430 		return 0;
431 
432 	if(cells[p.y][p.x].span.x == 0){
433 		tk = cells[p.y][p.x].tk;
434 		for(; p.y >= 0; p.y--)
435 			if(cells[p.y][p.x].tk != tk)
436 				break;
437 		p.y++;
438 		for(; p.x >= 0; p.x--)
439 			if(cells[p.y][p.x].tk != tk)
440 				break;
441 		p.x++;
442 	}
443 	*cp = p;
444 	return 1;
445 }
446 
447 static int
448 parsegridindex(TkGridbeam *beam, int blen, char *s)
449 {
450 	int n, i;
451 	char *e;
452 
453 	if(s[0] == '\0')
454 		return -1;
455 
456 	n = strtol(s, &e, 10);
457 	if(*e == '\0')
458 		return n;
459 
460 	if(strcmp(s, "end") == 0)
461 		return blen;
462 
463 	for(i = 0; i < blen; i++)
464 		if(beam[i].name != nil && strcmp(beam[i].name, s) == 0)
465 			return i;
466 	return -1;
467 }
468 
469 static char*
470 tkgridconfigure(TkTop *t, TkGridparam *p, TkName *names)
471 {
472 	TkGrid *grid;
473 	TkGridcell **cells;
474 	TkName *n;
475 	Tk *tkf, *tkp;
476 	Point dim, pos, q, span, startpos;
477 	int maxcol, c, i, j, x;
478 	char *e;
479 
480 	if(names == nil)
481 		return nil;
482 
483 	if(p->span.x < 1 || p->span.y < 1)
484 		return TkBadvl;
485 
486 	tkf = nil;
487 
488 	maxcol = 0;
489 	for(n = names; n; n = n->link){
490 		c = n->name[0];
491 		if((c=='-' || c=='^' || c=='x') && n->name[1] == '\0'){
492 			maxcol++;
493 			continue;
494 		}
495 		tkp = tklook(t, n->name, 0);
496 		if(tkp == nil){
497 			tkerr(t, n->name);
498 			return TkBadwp;
499 		}
500 		if(tkp->flag & Tkwindow)
501 			return TkIstop;
502 		if(tkp->parent != nil)
503 			return TkWpack;
504 
505 		/*
506 		 * unpacking now does give an non-reversible side effect
507 		 * ifthere's an error encountered later, but also means
508 		 * that a widget repacked in the same grid will
509 		 * have its original cell still available
510 		 */
511 		if(tkp->master != nil){
512 			tkpackqit(tkp->master);
513 			tkdelpack(tkp);
514 		}
515 		if(tkf == nil)
516 			tkf = tkp;
517 		n->obj = tkp;
518 		tkp->flag &= ~Tkgridpack;
519 		maxcol += p->span.x;
520 	}
521 
522 	if(p->in == nil && tkf != nil)
523 		p->in = tklook(t, tkf->name->name, 1);
524 
525 	if(p->in == nil)
526 		return TkNomaster;
527 
528 	grid = p->in->grid;
529 	if(grid == nil && p->in->slave != nil)
530 		return TkNotgrid;
531 
532 	if(grid == nil){
533 		grid = malloc(sizeof(TkGrid));
534 		if(grid == nil)
535 			return TkNomem;
536 		p->in->grid = grid;
537 	}
538 
539 	dim = grid->dim;
540 	pos = ZP;
541 	if(p->row != nil){
542 		pos.y = parsegridindex(grid->rows, dim.y, p->row);
543 		if(pos.y < 0)
544 			return TkBadix;
545 	}
546 	if(p->col != nil){
547 		pos.x = parsegridindex(grid->cols, dim.x, p->col);
548 		if(pos.x < 0)
549 			return TkBadix;
550 	}
551 	/*
552 	 * ifrow is not specified, find first unoccupied row
553 	 */
554 	if(p->row == nil){
555 		for(pos.y = 0; pos.y < dim.y; pos.y++){
556 			for(x = 0; x < dim.x; x++)
557 				if(grid->cells[pos.y][x].tk != nil)
558 					break;
559 			if(x == dim.x)
560 				break;
561 		}
562 	}
563 	e = ensuregridsize(grid, Pt(pos.x + maxcol, pos.y + p->span.y));
564 	if(e != nil)
565 		return e;
566 	cells = grid->cells;
567 
568 	startpos = pos;
569 	/*
570 	 * check that all our grid cells are empty, and that row/col spans
571 	 * are well formed
572 	 */
573 	n = names;
574 	while(n != nil){
575 		c = n->name[0];
576 		switch (c){
577 		case 'x':
578 			n = n->link;
579 			pos.x++;
580 			break;
581 		case '^':
582 			if(findspan(grid, Pt(pos.x, pos.y - 1), &q) == 0)
583 				return TkBadspan;
584 			span = cells[q.y][q.x].span;
585 			for(i = 0; i < span.x; i++){
586 				if(n == nil || strcmp(n->name, "^"))
587 					return TkBadspan;
588 				if(cells[pos.y][pos.x + i].tk != nil)
589 					return TkBadgridcell;
590 				n = n->link;
591 			}
592 			pos.x += span.x;
593 			break;
594 		case '-':
595 			return TkBadspan;
596 		case '.':
597 			tkp = n->obj;
598 			if(tkisslave(p->in, tkp))
599 				return TkRecur;
600 			n = n->link;
601 			if(tkp->flag & Tkgridpack)
602 				return TkWpack;
603 			tkp->flag |= Tkgridpack;
604 			span = p->span;
605 			for(; n != nil && strcmp(n->name, "-") == 0; n = n->link)
606 				span.x++;
607 			for(i = pos.x; i < pos.x + span.x; i++)
608 				for(j = pos.y; j < pos.y + span.y; j++)
609 					if(cells[j][i].tk != nil)
610 						return TkBadgridcell;
611 			pos.x = i;
612 			break;
613 		}
614 	}
615 
616 	/*
617 	 * actually insert the items into the grid
618 	 */
619 	n = names;
620 	pos = startpos;
621 	while(n != nil){
622 		c = n->name[0];
623 		switch (c){
624 		case 'x':
625 			n = n->link;
626 			pos.x++;
627 			break;
628 		case '^':
629 			findspan(grid, Pt(pos.x, pos.y - 1), &q);
630 			span = cells[q.y][q.x].span;
631 			tkf = cells[q.y][q.x].tk;
632 			if(q.y + span.y == pos.y)
633 				cells[q.y][q.x].span.y++;
634 
635 			for(i = 0; i < span.x; i++){
636 				cells[pos.y][pos.x++].tk = tkf;
637 				n = n->link;
638 			}
639 			break;
640 		case '.':
641 			tkf = n->obj;
642 			n = n->link;
643 			span = p->span;
644 			for(; n != nil && strcmp(n->name, "-") == 0; n = n->link)
645 				span.x++;
646 			for(i = pos.x; i < pos.x + span.x; i++)
647 				for(j = pos.y; j < pos.y + span.y; j++)
648 					cells[j][i].tk = tkf;
649 			cells[pos.y][pos.x].span = span;
650 			tkf->master = p->in;
651 			tkf->next = p->in->slave;
652 			p->in->slave = tkf;
653 			if(p->in->flag & Tksubsub)
654 				tksetbits(tkf, Tksubsub);
655 			tkgridsetopt(p, tkf);
656 			pos.x = i;
657 			break;
658 		}
659 	}
660 	tkpackqit(p->in);
661 	tkrunpack(t);
662 	return nil;
663 }
664 
665 void
666 tkgriddelslave(Tk *tk)
667 {
668 	int y, x, yy;
669 	TkGrid *grid;
670 	TkGridcell **cells, *cell;
671 	Point dim, span;
672 
673 	if(tk == nil || tk->master == nil || tk->master->grid == nil)
674 		return;
675 	grid = tk->master->grid;
676 	cells = grid->cells;
677 	dim = grid->dim;
678 	for(y = 0; y < dim.y; y++){
679 		for(x = 0; x < dim.x; x++){
680 			cell = &cells[y][x];
681 			if(cell->tk == tk){
682 				span = cell->span;
683 				for(yy = y; yy < y + span.y; yy++)
684 					memset(cells[yy] + x, 0, span.x * sizeof(TkGridcell));
685 				return;
686 			}
687 		}
688 	}
689 }
690 
691 char*
692 tkgetgridmaster(TkTop *t, char **arg, char *buf, char *ebuf, Tk **master)
693 {
694 	TkGrid *grid;
695 
696 	*arg = tkword(t, *arg, buf, ebuf, nil);
697 	*master = tklook(t, buf, 0);
698 	if(*master == nil)
699 		return TkBadwp;
700 	grid = (*master)->grid;
701 	if(grid == nil && (*master)->slave != nil)
702 		return TkNotgrid;
703 	return nil;
704 }
705 
706 static int
707 gridfindloc(TkGridbeam *beam, int blen, int f)
708 {
709 	int x, i, fpad;
710 	if(blen == 0 || f < 0)
711 		return -1;
712 
713 	fpad = 0;
714 	x =  0;
715 	for(i = 0; i < blen; i++){
716 		x += maximum(fpad, beam[i].pad);
717 		if(x <= f && f < x + beam[i].act)
718 			return i;
719 		x += beam[i].act;
720 	}
721 	return -1;
722 }
723 
724 /*
725  * optimised way to find a given slave, but somewhat more fragile
726  * as it assumes the slave has already been placed on the grid.
727  * not tested.
728  */
729 static int
730 findslave(TkGrid *grid, Tk *tk, Point *pt)
731 {
732 	Point loc, dim, p;
733 	TkGridcell **cells;
734 	dim = grid->dim;
735 	cells = grid->cells;
736 	loc.x = gridfindloc(grid->cols, grid->dim.x, tk->act.x);
737 	if(loc.x == -1)
738 		loc.x = 0;
739 	loc.y = gridfindloc(grid->rows, grid->dim.y, tk->act.y);
740 	if(loc.y == -1)
741 		loc.y = 0;
742 	for(p.y = loc.y; p.y < dim.y; p.y++)
743 		for(p.x = loc.x; p.x < dim.x; p.x++)
744 			if(cells[p.y][p.x].tk == tk){
745 				*pt = p;
746 				return 1;
747 			}
748 	return 0;
749 }
750 static char*
751 tkgridcellinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
752 {
753 	/* grid cellinfo master x y */
754 	Tk *master;
755 	char *e;
756 	Point p;
757 	TkGrid *grid;
758 	TkGridcell **cells;
759 
760 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
761 	if(e != nil || master->grid == nil)
762 		return e;
763 	grid = master->grid;
764 
765 	e = tkfracword(t, &arg, &p.x, nil);
766 	if(e != nil)
767 		return e;
768 	e = tkfracword(t, &arg, &p.y, nil);
769 	if(e != nil)
770 		return e;
771 
772 	p.x = TKF2I(p.x);
773 	p.y = TKF2I(p.y);
774 	if(p.x < 0 || p.x >= grid->dim.x || p.y < 0 || p.y >= grid->dim.y)
775 		return nil;
776 
777 	if(!findspan(grid, p, &p))
778 		return nil;
779 
780 	cells = grid->cells;
781 	return tkvalue(val, "%s -in %s -column %d -row %d -columnspan %d -rowspan %d",
782 		cells[p.y][p.x].tk->name->name,
783 		cells[p.y][p.x].tk->master->name->name, p.x, p.y,
784 		cells[p.y][p.x].span.x, cells[p.y][p.x].span.y);
785 }
786 
787 static char*
788 tkgridlocation(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
789 {
790 	/* grid location master x y */
791 	Tk *master;
792 	char *e;
793 	Point p;
794 	int col, row;
795 	TkGrid *grid;
796 
797 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
798 	if(e != nil || master->grid == nil)
799 		return e;
800 	grid = master->grid;
801 
802 	e = tkfracword(t, &arg, &p.x, nil);
803 	if(e != nil)
804 		return e;
805 	e = tkfracword(t, &arg, &p.y, nil);
806 	if(e != nil)
807 		return e;
808 
809 	p.x = TKF2I(p.x);
810 	p.y = TKF2I(p.y);
811 
812 	p = subpt(p, grid->origin);
813 	col = gridfindloc(grid->cols, grid->dim.x, p.x);
814 	row = gridfindloc(grid->rows, grid->dim.y, p.y);
815 	if(col < 0 || row < 0)
816 		return nil;
817 	return tkvalue(val, "%d %d", col, row);
818 }
819 
820 static char*
821 tkgridinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
822 {
823 	Tk *tk;
824 	TkGrid *grid;
825 	int x, y;
826 	Point dim;
827 	TkGridcell *row;
828 
829 	tkword(t, arg, buf, ebuf, nil);
830 	tk = tklook(t, buf, 0);
831 	if(tk == nil)
832 		return TkBadwp;
833 	if(tk->master == nil || tk->master->grid == nil)
834 		return TkNotgrid;
835 	grid = tk->master->grid;
836 	dim = grid->dim;
837 	for(y = 0; y < dim.y; y++){
838 		row = grid->cells[y];
839 		for(x = 0; x < dim.x; x++)
840 			if(row[x].tk == tk)
841 				goto Found;
842 	}
843 	return TkNotgrid;		/* should not happen */
844 Found:
845 	return tkvalue(val, "-in %s -column %d -row %d -columnspan %d -rowspan %d",
846 		tk->master->name->name, x, y, grid->cells[y][x].span.x, grid->cells[y][x].span.y);
847 }
848 
849 static char*
850 tkgridforget(TkTop *t, char *arg, char *buf, char *ebuf)
851 {
852 	Tk *tk;
853 	for(;;){
854 		arg = tkword(t, arg, buf, ebuf, nil);
855 		if(arg == nil || buf[0] == '\0')
856 			break;
857 		tk = tklook(t, buf, 0);
858 		if(tk == nil){
859 			tkrunpack(t);
860 			tkerr(t, buf);
861 			return TkBadwp;
862 		}
863 		tkpackqit(tk->master);
864 		tkdelpack(tk);
865 	}
866 	tkrunpack(t);
867 	return nil;
868 }
869 
870 static char*
871 tkgridslaves(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
872 {
873 	Tk *master, *tk;
874 	char *fmt;
875 	int i, isrow, index;
876 	TkGrid *grid;
877 	TkGridcell *cell;
878 	char *e;
879 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
880 	if(e != nil || master->grid == nil)
881 		return e;
882 	grid = master->grid;
883 	arg = tkword(t, arg, buf, ebuf, nil);
884 	fmt = "%s";
885 	if(buf[0] == '\0'){
886 		for(tk = master->slave; tk != nil; tk = tk->next){
887 			if(tk->name != nil){
888 				e = tkvalue(val, fmt, tk->name->name);
889 				if(e != nil)
890 					return e;
891 				fmt = " %s";
892 			}
893 		}
894 		return nil;
895 	}
896 	if(strcmp(buf, "-row") == 0)
897 		isrow = 1;
898 	else if(strcmp(buf, "-column") == 0)
899 		isrow = 0;
900 	else
901 		return TkBadop;
902 	tkword(t, arg, buf, ebuf, nil);
903 	if(isrow)
904 		index = parsegridindex(grid->rows, grid->dim.y, buf);
905 	else
906 		index = parsegridindex(grid->cols, grid->dim.x, buf);
907 	if(index < 0)
908 		return TkBadix;
909 	if(isrow){
910 		if(index >= grid->dim.y)
911 			return nil;
912 		for(i = 0; i < grid->dim.x; i++){
913 			cell = &grid->cells[index][i];
914 			if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
915 				e = tkvalue(val, fmt, cell->tk->name->name);
916 				if(e != nil)
917 					return e;
918 				fmt = " %s";
919 			}
920 		}
921 	} else{
922 		if(index >= grid->dim.x)
923 			return nil;
924 		for(i = 0; i < grid->dim.y; i++){
925 			cell = &grid->cells[i][index];
926 			if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
927 				e = tkvalue(val, fmt, cell->tk->name->name);
928 				if(e != nil)
929 					return e;
930 				fmt = " %s";
931 			}
932 		}
933 	}
934 
935 	return nil;
936 }
937 
938 static char*
939 tkgriddelete(TkTop *t, char *arg, char *buf, char *ebuf, int delrow)
940 {
941 	Tk *master, **l, *f;
942 	TkGrid *grid;
943 	TkGridbeam *beam;
944 	int blen, i0, i1, x, y;
945 	Point dim;
946 	TkGridcell **cells;
947 	char *e;
948 
949 	/*
950 	 * grid (columndelete|rowdelete) master index0 ?index1?
951 	 */
952 
953 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
954 	if(e != nil || master->grid == nil)
955 		return e;
956 	grid = master->grid;
957 
958 	if(delrow){
959 		beam = grid->rows;
960 		blen = grid->dim.y;
961 	} else{
962 		beam = grid->cols;
963 		blen = grid->dim.x;
964 	}
965 
966 	arg = tkword(t, arg, buf, ebuf, nil);
967 	i0 = parsegridindex(beam, blen, buf);
968 	if(i0 < 0)
969 		return TkBadix;
970 
971 	tkword(t, arg, buf, ebuf, nil);
972 	if(buf[0] == '\0')
973 		i1 = i0 + 1;
974 	else
975 		i1 = parsegridindex(beam, blen, buf);
976 	if(i1 < 0 || i0 > i1)
977 		return TkBadix;
978 	if(i0 > blen || i0 == i1)
979 		return nil;
980 	if(i1 > blen)
981 		i1 = blen;
982 	cells = grid->cells;
983 	dim = grid->dim;
984 	if(delrow){
985 		if(gridrowhasspan(grid, i0) || gridrowhasspan(grid, i1))
986 			return TkBadgridcell;
987 		for(y = i0; y < i1; y++)
988 			for(x = 0; x < dim.x; x++)
989 				if(cells[y][x].tk != nil)
990 					cells[y][x].tk->flag |= Tkgridremove;
991 		delrows(grid, i0, i1);
992 		grid->rows = delbeams(beam, blen, i0, i1);
993 	} else{
994 		if(gridcolhasspan(grid, i0) || gridcolhasspan(grid, i1))
995 			return TkBadgridcell;
996 		for(y = 0; y < dim.y; y++)
997 			for(x = i0; x < i1; x++)
998 				if(cells[y][x].tk != nil)
999 					cells[y][x].tk->flag |= Tkgridremove;
1000 		delcols(grid, i0, i1);
1001 		grid->cols = delbeams(beam, blen, i0, i1);
1002 	}
1003 	l = &master->slave;
1004 	for(f = *l; f; f = f->next){
1005 		if(f->flag & Tkgridremove){
1006 			*l = f->next;
1007 			f->master = nil;
1008 			f->flag &= ~Tkgridremove;
1009 		} else
1010 			l = &f->next;
1011 	}
1012 	tkpackqit(master);
1013 	tkrunpack(t);
1014 	return nil;
1015 }
1016 
1017 
1018 static char*
1019 tkgridinsert(TkTop *t, char *arg, char *buf, char *ebuf, int insertrow)
1020 {
1021 	int index, count;
1022 	Point dim;
1023 	Tk *master;
1024 	TkGrid *grid;
1025 	int gotarg;
1026 	char *e;
1027 
1028 	/*
1029 	 * grid (rowinsert|columninsert) master index ?count?
1030 	 * it's an error ifthe insert splits any spanning cells.
1031 	 */
1032 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1033 	if(e != nil || master->grid == nil)
1034 		return e;
1035 	grid = master->grid;
1036 	dim = grid->dim;
1037 
1038 	arg = tkword(t, arg, buf, ebuf, nil);
1039 	if(insertrow)
1040 		index = parsegridindex(grid->rows, dim.y, buf);
1041 	else
1042 		index = parsegridindex(grid->cols, dim.x, buf);
1043 	if(index < 0 || index > (insertrow ? dim.y : dim.x))
1044 		return TkBadix;
1045 
1046 	tkword(t, arg, buf, ebuf, &gotarg);
1047 	if(gotarg){
1048 		count = strtol(buf, &buf, 10);
1049 		if(buf[0] != '\0' || count < 0)
1050 			return TkBadvl;
1051 	} else
1052 		count = 1;
1053 
1054 	/*
1055 	 * check that we're not splitting any spanning cells
1056 	 */
1057 	if(insertrow){
1058 		if(gridrowhasspan(grid, index))
1059 			return TkBadgridcell;
1060 		e = insrows(grid, index, count);
1061 	} else{
1062 		if(gridcolhasspan(grid, index))
1063 			return TkBadgridcell;
1064 		e = inscols(grid, index, count);
1065 	}
1066 	tkpackqit(master);
1067 	tkrunpack(t);
1068 	return e;
1069 }
1070 
1071 /*
1072  * (rowconfigure|columnconfigure) master index ?-option value ...?
1073  */
1074 static char*
1075 tkbeamconfigure(TkTop *t, char *arg, int isrow)
1076 {
1077 	TkBeamparam p;
1078 	TkOptab tko[2];
1079 	TkName *names;
1080 	Tk *master;
1081 	int index;
1082 	TkGrid *grid;
1083 	TkGridbeam *beam;
1084 	Point dim;
1085 	char *e;
1086 
1087 	p.equalise = BoolX;
1088 	p.name = nil;
1089 	p.weight = -1;
1090 	p.minsize = -1;
1091 	p.maxsize = -1;
1092 	p.pad = -1;
1093 
1094 	tko[0].ptr = &p;
1095 	tko[0].optab = beamopts;
1096 	tko[1].ptr = nil;
1097 
1098 	names = nil;
1099 	e = tkparse(t, arg, tko, &names);
1100 	if(e != nil)
1101 		return e;
1102 
1103 	if(names == nil || names->link == nil)
1104 		return TkBadvl;
1105 
1106 	master = tklook(t, names->name, 0);
1107 	if(master == nil)
1108 		return TkBadwp;
1109 
1110 	grid = master->grid;
1111 	if(grid == nil){
1112 		if(master->slave != nil)
1113 			return TkNotgrid;
1114 		grid = master->grid = malloc(sizeof(TkGrid));
1115 		if(grid == nil){
1116 			tkfreename(names);
1117 			return TkNomem;
1118 		}
1119 	}
1120 
1121 	if(isrow){
1122 		index = parsegridindex(grid->rows, grid->dim.y, names->link->name);
1123 	} else
1124 		index = parsegridindex(grid->cols, grid->dim.x, names->link->name);
1125 	if(index < 0){
1126 		e = TkBadix;
1127 		goto Error;
1128 	}
1129 	if(isrow)
1130 		dim = Pt(grid->dim.x, index + 1);
1131 	else
1132 		dim = Pt(index + 1, grid->dim.y);
1133 	e = ensuregridsize(grid, dim);
1134 	if(e != nil)
1135 		goto Error;
1136 
1137 	if(isrow)
1138 		beam = &grid->rows[index];
1139 	else
1140 		beam = &grid->cols[index];
1141 
1142 	if(p.minsize >= 0)
1143 		beam->minsize = p.minsize;
1144 	if(p.maxsize >= 0)
1145 		beam->maxsize = p.maxsize;
1146 	if(p.weight >= 0)
1147 		beam->weight = p.weight;
1148 	if(p.pad >= 0)
1149 		beam->pad = p.pad;
1150 	if(p.name != nil){
1151 		free(beam->name);
1152 		beam->name = p.name;
1153 	}
1154 	if(p.equalise != BoolX)
1155 		beam->equalise = p.equalise == BoolT;
1156 
1157 	tkpackqit(master);
1158 	tkrunpack(t);
1159 
1160 Error:
1161 	tkfreename(names);
1162 	return e;
1163 }
1164 
1165 char*
1166 tkgridsize(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
1167 {
1168 	Tk *master;
1169 	TkGrid *grid;
1170 	char *e;
1171 
1172 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1173 	if(e != nil)
1174 		return e;
1175 	grid = master->grid;
1176 	if(grid == nil)
1177 		return tkvalue(val, "0 0");
1178 	else
1179 		return tkvalue(val, "%d %d", grid->dim.x, grid->dim.y);
1180 }
1181 
1182 char*
1183 tkgridbbox(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
1184 {
1185 	Point p0, p1;
1186 	Tk *master;
1187 	TkGrid *grid;
1188 	char *e;
1189 	int gotarg;
1190 	Point dim;
1191 	Rectangle r;
1192 
1193 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1194 	if(e != nil || master->grid == nil)
1195 		return e;
1196 
1197 	grid = master->grid;
1198 	dim = grid->dim;
1199 	arg = tkword(t, arg, buf, ebuf, &gotarg);
1200 	if(!gotarg){
1201 		p0 = ZP;
1202 		p1 = dim;
1203 	} else{
1204 		p0.x = parsegridindex(grid->cols, dim.x, buf);
1205 		arg = tkword(t, arg, buf, ebuf, &gotarg);
1206 		if(!gotarg)
1207 			return TkFewpt;
1208 		p0.y = parsegridindex(grid->rows, dim.y, buf);
1209 		arg = tkword(t, arg, buf, ebuf, &gotarg);
1210 		if(!gotarg){
1211 			p1 = p0;
1212 		} else{
1213 			p1.x = parsegridindex(grid->cols, dim.x, buf);
1214 			arg = tkword(t, arg, buf, ebuf, &gotarg);
1215 			if(!gotarg)
1216 				return TkFewpt;
1217 			p1.y = parsegridindex(grid->rows, dim.y, buf);
1218 		}
1219 	}
1220 	if(p0.x < 0 || p0.y < 0 || p1.x < 0 || p1.y < 0)
1221 		return TkBadix;
1222 
1223 	r = cellbbox(grid, p0);
1224 	if(!eqpt(p0, p1))
1225 		combinerect(&r, cellbbox(grid, p1));
1226 	return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
1227 }
1228 
1229 char*
1230 tkgridindex(TkTop *t, char *arg, char **val, char *buf, char *ebuf, int isrow)
1231 {
1232 	Tk *master;
1233 	TkGrid *grid;
1234 	TkGridbeam *beam;
1235 	int blen, i;
1236 
1237 	arg = tkword(t, arg, buf, ebuf, nil);
1238 	master = tklook(t, buf, 0);
1239 	if(master == nil)
1240 		return TkBadwp;
1241 	tkword(t, arg, buf, ebuf, nil);
1242 	grid = master->grid;
1243 	if(grid == nil){
1244 		beam = nil;
1245 		blen = 0;
1246 	} else if(isrow){
1247 		beam = grid->rows;
1248 		blen = grid->dim.y;
1249 	} else{
1250 		beam = grid->cols;
1251 		blen = grid->dim.x;
1252 	}
1253 	i = parsegridindex(beam, blen, buf);
1254 	if(i < 0)
1255 		return TkBadix;
1256 	return tkvalue(val, "%d", i);
1257 }
1258 
1259 void
1260 tkfreegrid(TkGrid *grid)
1261 {
1262 	Point dim;
1263 	int i;
1264 	dim = grid->dim;
1265 	for(i = 0; i < dim.x; i++)
1266 		free(grid->cols[i].name);
1267 	for(i = 0; i < dim.y; i++)
1268 		free(grid->rows[i].name);
1269 	for(i = 0; i < dim.y; i++)
1270 		free(grid->cells[i]);
1271 	free(grid->cells);
1272 	free(grid->rows);
1273 	free(grid->cols);
1274 	free(grid);
1275 }
1276 
1277 char*
1278 tkgrid(TkTop *t, char *arg, char **val)
1279 {
1280 	TkGridparam *p;
1281 	TkOptab tko[2];
1282 	TkName *names;
1283 	char *e, *w, *buf;
1284 
1285 	buf = mallocz(Tkmaxitem, 0);
1286 	if(buf == nil)
1287 		return TkNomem;
1288 
1289 	w = tkword(t, arg, buf, buf+Tkmaxitem, nil);
1290 	if('a' <= buf[0] && buf[0] <= 'z'){
1291 		if(strcmp(buf, "debug") == 0){
1292 			Tk *tk;
1293 			e = tkgetgridmaster(t, &w, buf, buf+Tkmaxitem, &tk);
1294 			if(e == nil)
1295 				printgrid(tk->grid);
1296 		} else
1297 		if(strcmp(buf, "forget") == 0)
1298 			e = tkgridforget(t, w, buf, buf+Tkmaxitem);
1299 		else if(strcmp(buf, "propagate") == 0)
1300 			e = tkpropagate(t, w);
1301 		else if(strcmp(buf, "slaves") == 0)
1302 			e = tkgridslaves(t, w, val, buf, buf+Tkmaxitem);
1303 		else if(strcmp(buf, "rowconfigure") == 0)
1304 			e = tkbeamconfigure(t, w, 1);
1305 		else if(strcmp(buf, "columnconfigure") == 0)
1306 			e = tkbeamconfigure(t, w, 0);
1307 		else if(strcmp(buf, "rowinsert") == 0)
1308 			e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 1);
1309 		else if(strcmp(buf, "columninsert") == 0)
1310 			e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 0);
1311 		else if(strcmp(buf, "size") == 0)
1312 			e = tkgridsize(t, w, val, buf, buf+Tkmaxitem);
1313 		else if(strcmp(buf, "rowdelete") == 0)
1314 			e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 1);
1315 		else if(strcmp(buf, "columndelete") == 0)
1316 			e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 0);
1317 		else if(strcmp(buf, "rowindex") == 0)
1318 			e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 1);
1319 		else if(strcmp(buf, "columnindex") == 0)
1320 			e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 0);
1321 		else if(strcmp(buf, "bbox") == 0)
1322 			e = tkgridbbox(t, w, val, buf, buf+Tkmaxitem);
1323 		else if(strcmp(buf, "location") == 0)
1324 			e = tkgridlocation(t, w, val, buf, buf+Tkmaxitem);
1325 		else if(strcmp(buf, "cellinfo") == 0)
1326 			e = tkgridcellinfo(t, w, val, buf, buf+Tkmaxitem);
1327 		else if(strcmp(buf, "info") == 0)
1328 			e = tkgridinfo(t, w, val, buf, buf+Tkmaxitem);
1329 		else{
1330 			tkerr(t, buf);
1331 			e = TkBadcm;
1332 		}
1333 	} else{
1334 		p = malloc(sizeof(TkGridparam));
1335 		if(p == nil)
1336 			return TkNomem;
1337 		tko[0].ptr = p;
1338 		tko[0].optab = opts;
1339 		tko[1].ptr = nil;
1340 
1341 		p->span.x = 1;
1342 		p->span.y = 1;
1343 		p->pad.x = p->pad.y = p->ipad.x = p->ipad.y = -1;
1344 		p->sticky = -1;
1345 
1346 		names = nil;
1347 		e = tkparse(t, arg, tko, &names);
1348 		if(e != nil){
1349 			free(p);
1350 			return e;
1351 		}
1352 
1353 		e = tkgridconfigure(t, p, names);
1354 		free(p->row);
1355 		free(p->col);
1356 		free(p);
1357 		tkfreename(names);
1358 	}
1359 	free(buf);
1360 	return e;
1361 }
1362 
1363 /*
1364  * expand widths of rows/columns according to weight.
1365  * return amount of space still left over.
1366  */
1367 static int
1368 expandwidths(int x0, int x1, int totwidth, TkGridbeam *cols, int expandzero)
1369 {
1370 	int share, x, slack, m, w, equal;
1371 
1372 	if(x0 >= x1)
1373 		return 0;
1374 
1375 	share = 0;
1376 	for(x = x0; x < x1; x++)
1377 		share += cols[x].weight;
1378 
1379 	slack = totwidth - beamsize(cols, x0, x1);
1380 	if(slack <= 0)
1381 		return 0;
1382 
1383 	if(share == 0 && expandzero){
1384 		share = x1 - x0;
1385 		equal = 1;
1386 	} else
1387 		equal = 0;
1388 
1389 	for(x = x0; x < x1 && share > 0 ; x++){
1390 		w = equal ? 1 : cols[x].weight;
1391 		m = slack * w / share;
1392 		cols[x].act += m;
1393 		slack -= m;
1394 		share -= w;
1395 	}
1396 	return slack;
1397 }
1398 
1399 static void
1400 gridequalise(TkGridbeam *beam, int blen)
1401 {
1402 	int i, max;
1403 
1404 	max = 0;
1405 	for(i = 0; i < blen; i++)
1406 		if(beam[i].equalise == BoolT && beam[i].act > max)
1407 			max = beam[i].act;
1408 
1409 	if(max > 0)
1410 		for(i = 0; i < blen; i++)
1411 			if(beam[i].equalise == BoolT)
1412 				beam[i].act = max;
1413 }
1414 
1415 /*
1416  * take into account min/max beam sizes.
1417  * max takes precedence
1418  */
1419 static void
1420 beamminmax(TkGridbeam *beam, int n)
1421 {
1422 	TkGridbeam *e;
1423 	e = &beam[n];
1424 	for(; beam < e; beam++){
1425 		if(beam->act < beam->minsize)
1426 			beam->act = beam->minsize;
1427 		if(beam->act > beam->maxsize)
1428 			beam->act = beam->maxsize;
1429 	}
1430 }
1431 
1432 int
1433 tkgridder(Tk *master)
1434 {
1435 	TkGrid *grid;
1436 	TkGridcell **cells, *cell;
1437 	TkGridbeam *rows, *cols;
1438 	TkGeom pos;
1439 	Point org;
1440 	Tk *slave;
1441 	int dx, dy, x, y, w, bw2, fpadx, fpady;
1442 	Point req;
1443 
1444 	grid = master->grid;
1445 	dx = grid->dim.x;
1446 	dy = grid->dim.y;
1447 	cells = grid->cells;
1448 	rows = grid->rows;
1449 	cols = grid->cols;
1450 
1451 	for(x = 0; x < dx; x++)
1452 		cols[x].act = 0;
1453 
1454 	/* calculate column widths and row heights (ignoring multi-column cells) */
1455 	for(y = 0; y < dy; y++){
1456 		rows[y].act = 0;
1457 		for(x = 0; x < dx; x++){
1458 			cell = &cells[y][x];
1459 			if((slave = cell->tk) != nil){
1460 				bw2 = slave->borderwidth * 2;
1461 				w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
1462 				if(cell->span.x == 1 && w > cols[x].act)
1463 					cols[x].act = w;
1464 				w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
1465 				if(cell->span.y == 1 && w > rows[y].act)
1466 					rows[y].act = w;
1467 			}
1468 		}
1469 	}
1470 
1471 	beamminmax(rows, dy);
1472 	beamminmax(cols, dx);
1473 
1474 	/* now check that spanning cells fit in their rows/columns */
1475 	for(y = 0; y < dy; y++)
1476 		for(x = 0; x < dx; x++){
1477 			cell = &cells[y][x];
1478 			if((slave = cell->tk) != nil){
1479 				bw2 = slave->borderwidth * 2;
1480 				if(cell->span.x > 1){
1481 					w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
1482 					expandwidths(x, x+cell->span.x, w, cols, 1);
1483 				}
1484 				if(cell->span.y > 1){
1485 					w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
1486 					expandwidths(y, y+cell->span.y, w, rows, 1);
1487 				}
1488 			}
1489 		}
1490 
1491 	gridequalise(rows, dy);
1492 	gridequalise(cols, dx);
1493 
1494 	if(dx == 0)
1495 		req.x = 0;
1496 	else
1497 		req.x = beamsize(cols, 0, dx) + cols[0].pad + cols[dx-1].pad;
1498 
1499 	if(dy == 0)
1500 		req.y = 0;
1501 	else
1502 		req.y = beamsize(rows, 0, dy) + rows[0].pad + rows[dy-1].pad;
1503 
1504 	if(req.x != master->req.width || req.y != master->req.height)
1505 	if((master->flag & Tknoprop) == 0){
1506 		if(master->geom != nil){
1507 			master->geom(master, master->act.x, master->act.y,
1508 					req.x, req.y);
1509 		} else{
1510 			master->req.width = req.x;
1511 			master->req.height = req.y;
1512 			tkpackqit(master->master);
1513 		}
1514 		return 0;
1515     	}
1516 	org = ZP;
1517 	if(dx > 0 && master->act.width > req.x)
1518 		org.x = expandwidths(0, dx,
1519 				master->act.width - (cols[0].pad + cols[dx-1].pad),
1520 				cols, 0) / 2;
1521 	if(dy > 0 && master->act.height > req.y)
1522 		org.y = expandwidths(0, dy,
1523 				master->act.height - (rows[0].pad + rows[dy-1].pad),
1524 				rows, 0) / 2;
1525 
1526 	grid->origin = org;
1527 	pos.y = org.y;
1528 	fpady = 0;
1529 	for(y = 0; y < dy; y++){
1530 		pos.y += maximum(fpady, rows[y].pad);
1531 		fpady = rows[y].pad;
1532 
1533 		pos.x = org.x;
1534 		fpadx = 0;
1535 		for(x = 0; x < dx; x++){
1536 			cell = &cells[y][x];
1537 			pos.x += maximum(fpadx, cols[x].pad);
1538 			fpadx = cols[x].pad;
1539 			if((slave = cell->tk) != nil && cell->span.x > 0){
1540 				pos.width = beamsize(cols, x, x + cell->span.x);
1541 				pos.height = beamsize(rows, y, y + cell->span.y);
1542 				tksetslavereq(slave, pos);
1543 			}
1544 			pos.x += cols[x].act;
1545 		}
1546 		pos.y += rows[y].act;
1547 	}
1548 
1549 	master->dirty = tkrect(master, 1);
1550 	tkdirty(master);
1551 	return 1;
1552 }
1553