xref: /inferno-os/libtk/grids.c (revision 5849851a19380dbb62a47d9c4d868a81e42fa79b)
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
printgrid(TkGrid * grid)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
tkgridsetopt(TkGridparam * p,Tk * tk)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
initbeam(TkGridbeam * beam,int n)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*
ensuregridsize(TkGrid * grid,Point dim)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 				while(--i >= olddim.y)
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*
delbeams(TkGridbeam * beam,int nb,int x0,int x1)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
delrows(TkGrid * grid,int y0,int y1)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
delcols(TkGrid * grid,int x0,int x1)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
insbeams(TkGridbeam * beam,int nb,int x,int n)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*
insrows(TkGrid * grid,int y0,int n)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*
inscols(TkGrid * grid,int x0,int n)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
maximum(int a,int b)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
beamsize(TkGridbeam * cols,int x0,int x1)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
beamcellpos(TkGridbeam * beam,int blen,int index)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
cellbbox(TkGrid * grid,Point pos)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
gridrowhasspan(TkGrid * grid,int index)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
gridcolhasspan(TkGrid * grid,int index)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
findspan(TkGrid * grid,Point p,Point * cp)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
parsegridindex(TkGridbeam * beam,int blen,char * s)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*
tkgridconfigure(TkTop * t,TkGridparam * p,TkName * names)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
tkgriddelslave(Tk * tk)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*
tkgetgridmaster(TkTop * t,char ** arg,char * buf,char * ebuf,Tk ** master)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
gridfindloc(TkGridbeam * beam,int blen,int f)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 static char*
tkgridcellinfo(TkTop * t,char * arg,char ** val,char * buf,char * ebuf)725 tkgridcellinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
726 {
727 	/* grid cellinfo master x y */
728 	Tk *master;
729 	char *e;
730 	Point p;
731 	TkGrid *grid;
732 	TkGridcell **cells;
733 
734 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
735 	if(e != nil || master->grid == nil)
736 		return e;
737 	grid = master->grid;
738 
739 	e = tkfracword(t, &arg, &p.x, nil);
740 	if(e != nil)
741 		return e;
742 	e = tkfracword(t, &arg, &p.y, nil);
743 	if(e != nil)
744 		return e;
745 
746 	p.x = TKF2I(p.x);
747 	p.y = TKF2I(p.y);
748 	if(p.x < 0 || p.x >= grid->dim.x || p.y < 0 || p.y >= grid->dim.y)
749 		return nil;
750 
751 	if(!findspan(grid, p, &p))
752 		return nil;
753 
754 	cells = grid->cells;
755 	return tkvalue(val, "%s -in %s -column %d -row %d -columnspan %d -rowspan %d",
756 		cells[p.y][p.x].tk->name->name,
757 		cells[p.y][p.x].tk->master->name->name, p.x, p.y,
758 		cells[p.y][p.x].span.x, cells[p.y][p.x].span.y);
759 }
760 
761 static char*
tkgridlocation(TkTop * t,char * arg,char ** val,char * buf,char * ebuf)762 tkgridlocation(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
763 {
764 	/* grid location master x y */
765 	Tk *master;
766 	char *e;
767 	Point p;
768 	int col, row;
769 	TkGrid *grid;
770 
771 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
772 	if(e != nil || master->grid == nil)
773 		return e;
774 	grid = master->grid;
775 
776 	e = tkfracword(t, &arg, &p.x, nil);
777 	if(e != nil)
778 		return e;
779 	e = tkfracword(t, &arg, &p.y, nil);
780 	if(e != nil)
781 		return e;
782 
783 	p.x = TKF2I(p.x);
784 	p.y = TKF2I(p.y);
785 
786 	p = subpt(p, grid->origin);
787 	col = gridfindloc(grid->cols, grid->dim.x, p.x);
788 	row = gridfindloc(grid->rows, grid->dim.y, p.y);
789 	if(col < 0 || row < 0)
790 		return nil;
791 	return tkvalue(val, "%d %d", col, row);
792 }
793 
794 static char*
tkgridinfo(TkTop * t,char * arg,char ** val,char * buf,char * ebuf)795 tkgridinfo(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
796 {
797 	Tk *tk;
798 	TkGrid *grid;
799 	int x, y;
800 	Point dim;
801 	TkGridcell *row;
802 
803 	tkword(t, arg, buf, ebuf, nil);
804 	tk = tklook(t, buf, 0);
805 	if(tk == nil)
806 		return TkBadwp;
807 	if(tk->master == nil || tk->master->grid == nil)
808 		return TkNotgrid;
809 	grid = tk->master->grid;
810 	dim = grid->dim;
811 	for(y = 0; y < dim.y; y++){
812 		row = grid->cells[y];
813 		for(x = 0; x < dim.x; x++)
814 			if(row[x].tk == tk)
815 				goto Found;
816 	}
817 	return TkNotgrid;		/* should not happen */
818 Found:
819 	return tkvalue(val, "-in %s -column %d -row %d -columnspan %d -rowspan %d",
820 		tk->master->name->name, x, y, grid->cells[y][x].span.x, grid->cells[y][x].span.y);
821 }
822 
823 static char*
tkgridforget(TkTop * t,char * arg,char * buf,char * ebuf)824 tkgridforget(TkTop *t, char *arg, char *buf, char *ebuf)
825 {
826 	Tk *tk;
827 	for(;;){
828 		arg = tkword(t, arg, buf, ebuf, nil);
829 		if(arg == nil || buf[0] == '\0')
830 			break;
831 		tk = tklook(t, buf, 0);
832 		if(tk == nil){
833 			tkrunpack(t);
834 			tkerr(t, buf);
835 			return TkBadwp;
836 		}
837 		tkpackqit(tk->master);
838 		tkdelpack(tk);
839 	}
840 	tkrunpack(t);
841 	return nil;
842 }
843 
844 static char*
tkgridslaves(TkTop * t,char * arg,char ** val,char * buf,char * ebuf)845 tkgridslaves(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
846 {
847 	Tk *master, *tk;
848 	char *fmt;
849 	int i, isrow, index;
850 	TkGrid *grid;
851 	TkGridcell *cell;
852 	char *e;
853 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
854 	if(e != nil || master->grid == nil)
855 		return e;
856 	grid = master->grid;
857 	arg = tkword(t, arg, buf, ebuf, nil);
858 	fmt = "%s";
859 	if(buf[0] == '\0'){
860 		for(tk = master->slave; tk != nil; tk = tk->next){
861 			if(tk->name != nil){
862 				e = tkvalue(val, fmt, tk->name->name);
863 				if(e != nil)
864 					return e;
865 				fmt = " %s";
866 			}
867 		}
868 		return nil;
869 	}
870 	if(strcmp(buf, "-row") == 0)
871 		isrow = 1;
872 	else if(strcmp(buf, "-column") == 0)
873 		isrow = 0;
874 	else
875 		return TkBadop;
876 	tkword(t, arg, buf, ebuf, nil);
877 	if(isrow)
878 		index = parsegridindex(grid->rows, grid->dim.y, buf);
879 	else
880 		index = parsegridindex(grid->cols, grid->dim.x, buf);
881 	if(index < 0)
882 		return TkBadix;
883 	if(isrow){
884 		if(index >= grid->dim.y)
885 			return nil;
886 		for(i = 0; i < grid->dim.x; i++){
887 			cell = &grid->cells[index][i];
888 			if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
889 				e = tkvalue(val, fmt, cell->tk->name->name);
890 				if(e != nil)
891 					return e;
892 				fmt = " %s";
893 			}
894 		}
895 	} else{
896 		if(index >= grid->dim.x)
897 			return nil;
898 		for(i = 0; i < grid->dim.y; i++){
899 			cell = &grid->cells[i][index];
900 			if(cell->tk != nil && cell->span.x > 0 && cell->tk->name != nil){
901 				e = tkvalue(val, fmt, cell->tk->name->name);
902 				if(e != nil)
903 					return e;
904 				fmt = " %s";
905 			}
906 		}
907 	}
908 
909 	return nil;
910 }
911 
912 static char*
tkgriddelete(TkTop * t,char * arg,char * buf,char * ebuf,int delrow)913 tkgriddelete(TkTop *t, char *arg, char *buf, char *ebuf, int delrow)
914 {
915 	Tk *master, **l, *f;
916 	TkGrid *grid;
917 	TkGridbeam *beam;
918 	int blen, i0, i1, x, y;
919 	Point dim;
920 	TkGridcell **cells;
921 	char *e;
922 
923 	/*
924 	 * grid (columndelete|rowdelete) master index0 ?index1?
925 	 */
926 
927 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
928 	if(e != nil || master->grid == nil)
929 		return e;
930 	grid = master->grid;
931 
932 	if(delrow){
933 		beam = grid->rows;
934 		blen = grid->dim.y;
935 	} else{
936 		beam = grid->cols;
937 		blen = grid->dim.x;
938 	}
939 
940 	arg = tkword(t, arg, buf, ebuf, nil);
941 	i0 = parsegridindex(beam, blen, buf);
942 	if(i0 < 0)
943 		return TkBadix;
944 
945 	tkword(t, arg, buf, ebuf, nil);
946 	if(buf[0] == '\0')
947 		i1 = i0 + 1;
948 	else
949 		i1 = parsegridindex(beam, blen, buf);
950 	if(i1 < 0 || i0 > i1)
951 		return TkBadix;
952 	if(i0 > blen || i0 == i1)
953 		return nil;
954 	if(i1 > blen)
955 		i1 = blen;
956 	cells = grid->cells;
957 	dim = grid->dim;
958 	if(delrow){
959 		if(gridrowhasspan(grid, i0) || gridrowhasspan(grid, i1))
960 			return TkBadgridcell;
961 		for(y = i0; y < i1; y++)
962 			for(x = 0; x < dim.x; x++)
963 				if(cells[y][x].tk != nil)
964 					cells[y][x].tk->flag |= Tkgridremove;
965 		delrows(grid, i0, i1);
966 		grid->rows = delbeams(beam, blen, i0, i1);
967 	} else{
968 		if(gridcolhasspan(grid, i0) || gridcolhasspan(grid, i1))
969 			return TkBadgridcell;
970 		for(y = 0; y < dim.y; y++)
971 			for(x = i0; x < i1; x++)
972 				if(cells[y][x].tk != nil)
973 					cells[y][x].tk->flag |= Tkgridremove;
974 		delcols(grid, i0, i1);
975 		grid->cols = delbeams(beam, blen, i0, i1);
976 	}
977 	l = &master->slave;
978 	for(f = *l; f; f = f->next){
979 		if(f->flag & Tkgridremove){
980 			*l = f->next;
981 			f->master = nil;
982 			f->flag &= ~Tkgridremove;
983 		} else
984 			l = &f->next;
985 	}
986 	tkpackqit(master);
987 	tkrunpack(t);
988 	return nil;
989 }
990 
991 
992 static char*
tkgridinsert(TkTop * t,char * arg,char * buf,char * ebuf,int insertrow)993 tkgridinsert(TkTop *t, char *arg, char *buf, char *ebuf, int insertrow)
994 {
995 	int index, count;
996 	Point dim;
997 	Tk *master;
998 	TkGrid *grid;
999 	int gotarg;
1000 	char *e;
1001 
1002 	/*
1003 	 * grid (rowinsert|columninsert) master index ?count?
1004 	 * it's an error ifthe insert splits any spanning cells.
1005 	 */
1006 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1007 	if(e != nil || master->grid == nil)
1008 		return e;
1009 	grid = master->grid;
1010 	dim = grid->dim;
1011 
1012 	arg = tkword(t, arg, buf, ebuf, nil);
1013 	if(insertrow)
1014 		index = parsegridindex(grid->rows, dim.y, buf);
1015 	else
1016 		index = parsegridindex(grid->cols, dim.x, buf);
1017 	if(index < 0 || index > (insertrow ? dim.y : dim.x))
1018 		return TkBadix;
1019 
1020 	tkword(t, arg, buf, ebuf, &gotarg);
1021 	if(gotarg){
1022 		count = strtol(buf, &buf, 10);
1023 		if(buf[0] != '\0' || count < 0)
1024 			return TkBadvl;
1025 	} else
1026 		count = 1;
1027 
1028 	/*
1029 	 * check that we're not splitting any spanning cells
1030 	 */
1031 	if(insertrow){
1032 		if(gridrowhasspan(grid, index))
1033 			return TkBadgridcell;
1034 		e = insrows(grid, index, count);
1035 	} else{
1036 		if(gridcolhasspan(grid, index))
1037 			return TkBadgridcell;
1038 		e = inscols(grid, index, count);
1039 	}
1040 	tkpackqit(master);
1041 	tkrunpack(t);
1042 	return e;
1043 }
1044 
1045 /*
1046  * (rowconfigure|columnconfigure) master index ?-option value ...?
1047  */
1048 static char*
tkbeamconfigure(TkTop * t,char * arg,int isrow)1049 tkbeamconfigure(TkTop *t, char *arg, int isrow)
1050 {
1051 	TkBeamparam p;
1052 	TkOptab tko[2];
1053 	TkName *names;
1054 	Tk *master;
1055 	int index;
1056 	TkGrid *grid;
1057 	TkGridbeam *beam;
1058 	Point dim;
1059 	char *e;
1060 
1061 	p.equalise = BoolX;
1062 	p.name = nil;
1063 	p.weight = -1;
1064 	p.minsize = -1;
1065 	p.maxsize = -1;
1066 	p.pad = -1;
1067 
1068 	tko[0].ptr = &p;
1069 	tko[0].optab = beamopts;
1070 	tko[1].ptr = nil;
1071 
1072 	names = nil;
1073 	e = tkparse(t, arg, tko, &names);
1074 	if(e != nil)
1075 		return e;
1076 
1077 	if(names == nil || names->link == nil)
1078 		return TkBadvl;
1079 
1080 	master = tklook(t, names->name, 0);
1081 	if(master == nil)
1082 		return TkBadwp;
1083 
1084 	grid = master->grid;
1085 	if(grid == nil){
1086 		if(master->slave != nil)
1087 			return TkNotgrid;
1088 		grid = master->grid = malloc(sizeof(TkGrid));
1089 		if(grid == nil){
1090 			tkfreename(names);
1091 			return TkNomem;
1092 		}
1093 	}
1094 
1095 	if(isrow){
1096 		index = parsegridindex(grid->rows, grid->dim.y, names->link->name);
1097 	} else
1098 		index = parsegridindex(grid->cols, grid->dim.x, names->link->name);
1099 	if(index < 0){
1100 		e = TkBadix;
1101 		goto Error;
1102 	}
1103 	if(isrow)
1104 		dim = Pt(grid->dim.x, index + 1);
1105 	else
1106 		dim = Pt(index + 1, grid->dim.y);
1107 	e = ensuregridsize(grid, dim);
1108 	if(e != nil)
1109 		goto Error;
1110 
1111 	if(isrow)
1112 		beam = &grid->rows[index];
1113 	else
1114 		beam = &grid->cols[index];
1115 
1116 	if(p.minsize >= 0)
1117 		beam->minsize = p.minsize;
1118 	if(p.maxsize >= 0)
1119 		beam->maxsize = p.maxsize;
1120 	if(p.weight >= 0)
1121 		beam->weight = p.weight;
1122 	if(p.pad >= 0)
1123 		beam->pad = p.pad;
1124 	if(p.name != nil){
1125 		free(beam->name);
1126 		beam->name = p.name;
1127 	}
1128 	if(p.equalise != BoolX)
1129 		beam->equalise = p.equalise == BoolT;
1130 
1131 	tkpackqit(master);
1132 	tkrunpack(t);
1133 
1134 Error:
1135 	tkfreename(names);
1136 	return e;
1137 }
1138 
1139 char*
tkgridsize(TkTop * t,char * arg,char ** val,char * buf,char * ebuf)1140 tkgridsize(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
1141 {
1142 	Tk *master;
1143 	TkGrid *grid;
1144 	char *e;
1145 
1146 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1147 	if(e != nil)
1148 		return e;
1149 	grid = master->grid;
1150 	if(grid == nil)
1151 		return tkvalue(val, "0 0");
1152 	else
1153 		return tkvalue(val, "%d %d", grid->dim.x, grid->dim.y);
1154 }
1155 
1156 char*
tkgridbbox(TkTop * t,char * arg,char ** val,char * buf,char * ebuf)1157 tkgridbbox(TkTop *t, char *arg, char **val, char *buf, char *ebuf)
1158 {
1159 	Point p0, p1;
1160 	Tk *master;
1161 	TkGrid *grid;
1162 	char *e;
1163 	int gotarg;
1164 	Point dim;
1165 	Rectangle r;
1166 
1167 	e = tkgetgridmaster(t, &arg, buf, ebuf, &master);
1168 	if(e != nil || master->grid == nil)
1169 		return e;
1170 
1171 	grid = master->grid;
1172 	dim = grid->dim;
1173 	arg = tkword(t, arg, buf, ebuf, &gotarg);
1174 	if(!gotarg){
1175 		p0 = ZP;
1176 		p1 = dim;
1177 	} else{
1178 		p0.x = parsegridindex(grid->cols, dim.x, buf);
1179 		arg = tkword(t, arg, buf, ebuf, &gotarg);
1180 		if(!gotarg)
1181 			return TkFewpt;
1182 		p0.y = parsegridindex(grid->rows, dim.y, buf);
1183 		arg = tkword(t, arg, buf, ebuf, &gotarg);
1184 		if(!gotarg){
1185 			p1 = p0;
1186 		} else{
1187 			p1.x = parsegridindex(grid->cols, dim.x, buf);
1188 			arg = tkword(t, arg, buf, ebuf, &gotarg);
1189 			if(!gotarg)
1190 				return TkFewpt;
1191 			p1.y = parsegridindex(grid->rows, dim.y, buf);
1192 		}
1193 	}
1194 	if(p0.x < 0 || p0.y < 0 || p1.x < 0 || p1.y < 0)
1195 		return TkBadix;
1196 
1197 	r = cellbbox(grid, p0);
1198 	if(!eqpt(p0, p1))
1199 		combinerect(&r, cellbbox(grid, p1));
1200 	return tkvalue(val, "%d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
1201 }
1202 
1203 char*
tkgridindex(TkTop * t,char * arg,char ** val,char * buf,char * ebuf,int isrow)1204 tkgridindex(TkTop *t, char *arg, char **val, char *buf, char *ebuf, int isrow)
1205 {
1206 	Tk *master;
1207 	TkGrid *grid;
1208 	TkGridbeam *beam;
1209 	int blen, i;
1210 
1211 	arg = tkword(t, arg, buf, ebuf, nil);
1212 	master = tklook(t, buf, 0);
1213 	if(master == nil)
1214 		return TkBadwp;
1215 	tkword(t, arg, buf, ebuf, nil);
1216 	grid = master->grid;
1217 	if(grid == nil){
1218 		beam = nil;
1219 		blen = 0;
1220 	} else if(isrow){
1221 		beam = grid->rows;
1222 		blen = grid->dim.y;
1223 	} else{
1224 		beam = grid->cols;
1225 		blen = grid->dim.x;
1226 	}
1227 	i = parsegridindex(beam, blen, buf);
1228 	if(i < 0)
1229 		return TkBadix;
1230 	return tkvalue(val, "%d", i);
1231 }
1232 
1233 void
tkfreegrid(TkGrid * grid)1234 tkfreegrid(TkGrid *grid)
1235 {
1236 	Point dim;
1237 	int i;
1238 	dim = grid->dim;
1239 	for(i = 0; i < dim.x; i++)
1240 		free(grid->cols[i].name);
1241 	for(i = 0; i < dim.y; i++)
1242 		free(grid->rows[i].name);
1243 	for(i = 0; i < dim.y; i++)
1244 		free(grid->cells[i]);
1245 	free(grid->cells);
1246 	free(grid->rows);
1247 	free(grid->cols);
1248 	free(grid);
1249 }
1250 
1251 char*
tkgrid(TkTop * t,char * arg,char ** val)1252 tkgrid(TkTop *t, char *arg, char **val)
1253 {
1254 	TkGridparam *p;
1255 	TkOptab tko[2];
1256 	TkName *names;
1257 	char *e, *w, *buf;
1258 
1259 	buf = mallocz(Tkmaxitem, 0);
1260 	if(buf == nil)
1261 		return TkNomem;
1262 
1263 	w = tkword(t, arg, buf, buf+Tkmaxitem, nil);
1264 	if('a' <= buf[0] && buf[0] <= 'z'){
1265 		if(strcmp(buf, "debug") == 0){
1266 			Tk *tk;
1267 			e = tkgetgridmaster(t, &w, buf, buf+Tkmaxitem, &tk);
1268 			if(e == nil)
1269 				printgrid(tk->grid);
1270 		} else
1271 		if(strcmp(buf, "forget") == 0)
1272 			e = tkgridforget(t, w, buf, buf+Tkmaxitem);
1273 		else if(strcmp(buf, "propagate") == 0)
1274 			e = tkpropagate(t, w);
1275 		else if(strcmp(buf, "slaves") == 0)
1276 			e = tkgridslaves(t, w, val, buf, buf+Tkmaxitem);
1277 		else if(strcmp(buf, "rowconfigure") == 0)
1278 			e = tkbeamconfigure(t, w, 1);
1279 		else if(strcmp(buf, "columnconfigure") == 0)
1280 			e = tkbeamconfigure(t, w, 0);
1281 		else if(strcmp(buf, "rowinsert") == 0)
1282 			e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 1);
1283 		else if(strcmp(buf, "columninsert") == 0)
1284 			e = tkgridinsert(t, w, buf, buf+Tkmaxitem, 0);
1285 		else if(strcmp(buf, "size") == 0)
1286 			e = tkgridsize(t, w, val, buf, buf+Tkmaxitem);
1287 		else if(strcmp(buf, "rowdelete") == 0)
1288 			e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 1);
1289 		else if(strcmp(buf, "columndelete") == 0)
1290 			e = tkgriddelete(t, w, buf, buf+Tkmaxitem, 0);
1291 		else if(strcmp(buf, "rowindex") == 0)
1292 			e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 1);
1293 		else if(strcmp(buf, "columnindex") == 0)
1294 			e = tkgridindex(t, w, val, buf, buf+Tkmaxitem, 0);
1295 		else if(strcmp(buf, "bbox") == 0)
1296 			e = tkgridbbox(t, w, val, buf, buf+Tkmaxitem);
1297 		else if(strcmp(buf, "location") == 0)
1298 			e = tkgridlocation(t, w, val, buf, buf+Tkmaxitem);
1299 		else if(strcmp(buf, "cellinfo") == 0)
1300 			e = tkgridcellinfo(t, w, val, buf, buf+Tkmaxitem);
1301 		else if(strcmp(buf, "info") == 0)
1302 			e = tkgridinfo(t, w, val, buf, buf+Tkmaxitem);
1303 		else{
1304 			tkerr(t, buf);
1305 			e = TkBadcm;
1306 		}
1307 	} else{
1308 		p = malloc(sizeof(TkGridparam));
1309 		if(p == nil)
1310 			return TkNomem;
1311 		tko[0].ptr = p;
1312 		tko[0].optab = opts;
1313 		tko[1].ptr = nil;
1314 
1315 		p->span.x = 1;
1316 		p->span.y = 1;
1317 		p->pad.x = p->pad.y = p->ipad.x = p->ipad.y = -1;
1318 		p->sticky = -1;
1319 
1320 		names = nil;
1321 		e = tkparse(t, arg, tko, &names);
1322 		if(e != nil){
1323 			free(p);
1324 			return e;
1325 		}
1326 
1327 		e = tkgridconfigure(t, p, names);
1328 		free(p->row);
1329 		free(p->col);
1330 		free(p);
1331 		tkfreename(names);
1332 	}
1333 	free(buf);
1334 	return e;
1335 }
1336 
1337 /*
1338  * expand widths of rows/columns according to weight.
1339  * return amount of space still left over.
1340  */
1341 static int
expandwidths(int x0,int x1,int totwidth,TkGridbeam * cols,int expandzero)1342 expandwidths(int x0, int x1, int totwidth, TkGridbeam *cols, int expandzero)
1343 {
1344 	int share, x, slack, m, w, equal;
1345 
1346 	if(x0 >= x1)
1347 		return 0;
1348 
1349 	share = 0;
1350 	for(x = x0; x < x1; x++)
1351 		share += cols[x].weight;
1352 
1353 	slack = totwidth - beamsize(cols, x0, x1);
1354 	if(slack <= 0)
1355 		return 0;
1356 
1357 	if(share == 0 && expandzero){
1358 		share = x1 - x0;
1359 		equal = 1;
1360 	} else
1361 		equal = 0;
1362 
1363 	for(x = x0; x < x1 && share > 0 ; x++){
1364 		w = equal ? 1 : cols[x].weight;
1365 		m = slack * w / share;
1366 		cols[x].act += m;
1367 		slack -= m;
1368 		share -= w;
1369 	}
1370 	return slack;
1371 }
1372 
1373 static void
gridequalise(TkGridbeam * beam,int blen)1374 gridequalise(TkGridbeam *beam, int blen)
1375 {
1376 	int i, max;
1377 
1378 	max = 0;
1379 	for(i = 0; i < blen; i++)
1380 		if(beam[i].equalise == BoolT && beam[i].act > max)
1381 			max = beam[i].act;
1382 
1383 	if(max > 0)
1384 		for(i = 0; i < blen; i++)
1385 			if(beam[i].equalise == BoolT)
1386 				beam[i].act = max;
1387 }
1388 
1389 /*
1390  * take into account min/max beam sizes.
1391  * max takes precedence
1392  */
1393 static void
beamminmax(TkGridbeam * beam,int n)1394 beamminmax(TkGridbeam *beam, int n)
1395 {
1396 	TkGridbeam *e;
1397 	e = &beam[n];
1398 	for(; beam < e; beam++){
1399 		if(beam->act < beam->minsize)
1400 			beam->act = beam->minsize;
1401 		if(beam->act > beam->maxsize)
1402 			beam->act = beam->maxsize;
1403 	}
1404 }
1405 
1406 int
tkgridder(Tk * master)1407 tkgridder(Tk *master)
1408 {
1409 	TkGrid *grid;
1410 	TkGridcell **cells, *cell;
1411 	TkGridbeam *rows, *cols;
1412 	TkGeom pos;
1413 	Point org;
1414 	Tk *slave;
1415 	int dx, dy, x, y, w, bw2, fpadx, fpady;
1416 	Point req;
1417 
1418 	grid = master->grid;
1419 	dx = grid->dim.x;
1420 	dy = grid->dim.y;
1421 	cells = grid->cells;
1422 	rows = grid->rows;
1423 	cols = grid->cols;
1424 
1425 	for(x = 0; x < dx; x++)
1426 		cols[x].act = 0;
1427 
1428 	/* calculate column widths and row heights (ignoring multi-column cells) */
1429 	for(y = 0; y < dy; y++){
1430 		rows[y].act = 0;
1431 		for(x = 0; x < dx; x++){
1432 			cell = &cells[y][x];
1433 			if((slave = cell->tk) != nil){
1434 				bw2 = slave->borderwidth * 2;
1435 				w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
1436 				if(cell->span.x == 1 && w > cols[x].act)
1437 					cols[x].act = w;
1438 				w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
1439 				if(cell->span.y == 1 && w > rows[y].act)
1440 					rows[y].act = w;
1441 			}
1442 		}
1443 	}
1444 
1445 	beamminmax(rows, dy);
1446 	beamminmax(cols, dx);
1447 
1448 	/* now check that spanning cells fit in their rows/columns */
1449 	for(y = 0; y < dy; y++)
1450 		for(x = 0; x < dx; x++){
1451 			cell = &cells[y][x];
1452 			if((slave = cell->tk) != nil){
1453 				bw2 = slave->borderwidth * 2;
1454 				if(cell->span.x > 1){
1455 					w = slave->req.width + bw2 + slave->pad.x + slave->ipad.x;
1456 					expandwidths(x, x+cell->span.x, w, cols, 1);
1457 				}
1458 				if(cell->span.y > 1){
1459 					w = slave->req.height + bw2 + slave->pad.y + slave->ipad.y;
1460 					expandwidths(y, y+cell->span.y, w, rows, 1);
1461 				}
1462 			}
1463 		}
1464 
1465 	gridequalise(rows, dy);
1466 	gridequalise(cols, dx);
1467 
1468 	if(dx == 0)
1469 		req.x = 0;
1470 	else
1471 		req.x = beamsize(cols, 0, dx) + cols[0].pad + cols[dx-1].pad;
1472 
1473 	if(dy == 0)
1474 		req.y = 0;
1475 	else
1476 		req.y = beamsize(rows, 0, dy) + rows[0].pad + rows[dy-1].pad;
1477 
1478 	if(req.x != master->req.width || req.y != master->req.height)
1479 	if((master->flag & Tknoprop) == 0){
1480 		if(master->geom != nil){
1481 			master->geom(master, master->act.x, master->act.y,
1482 					req.x, req.y);
1483 		} else{
1484 			master->req.width = req.x;
1485 			master->req.height = req.y;
1486 			tkpackqit(master->master);
1487 		}
1488 		return 0;
1489     	}
1490 	org = ZP;
1491 	if(dx > 0 && master->act.width > req.x)
1492 		org.x = expandwidths(0, dx,
1493 				master->act.width - (cols[0].pad + cols[dx-1].pad),
1494 				cols, 0) / 2;
1495 	if(dy > 0 && master->act.height > req.y)
1496 		org.y = expandwidths(0, dy,
1497 				master->act.height - (rows[0].pad + rows[dy-1].pad),
1498 				rows, 0) / 2;
1499 
1500 	grid->origin = org;
1501 	pos.y = org.y;
1502 	fpady = 0;
1503 	for(y = 0; y < dy; y++){
1504 		pos.y += maximum(fpady, rows[y].pad);
1505 		fpady = rows[y].pad;
1506 
1507 		pos.x = org.x;
1508 		fpadx = 0;
1509 		for(x = 0; x < dx; x++){
1510 			cell = &cells[y][x];
1511 			pos.x += maximum(fpadx, cols[x].pad);
1512 			fpadx = cols[x].pad;
1513 			if((slave = cell->tk) != nil && cell->span.x > 0){
1514 				pos.width = beamsize(cols, x, x + cell->span.x);
1515 				pos.height = beamsize(rows, y, y + cell->span.y);
1516 				tksetslavereq(slave, pos);
1517 			}
1518 			pos.x += cols[x].act;
1519 		}
1520 		pos.y += rows[y].act;
1521 	}
1522 
1523 	master->dirty = tkrect(master, 1);
1524 	tkdirty(master);
1525 	return 1;
1526 }
1527