code: 9ferno

ref: f1eaa84bca784511be2b98c4f82a858c42110b4d
dir: /appl/ebook/table.b/

View raw version
implement Table;
include "sys.m";
	sys: Sys;
include "draw.m";
	draw: Draw;
	Point, Rect: import draw;
include "tk.m";
	tk: Tk;
include "table.m";

init()
{
	sys = load Sys Sys->PATH;
	draw = load Draw Draw->PATH;
	tk = load Tk Tk->PATH;
}

newcell(w: string, span: Draw->Point): ref Cell
{
	return ref Cell(w, span, (0, 0));
}

layout(cells: array of array of ref Cell, win: ref Tk->Toplevel, w: string)
{
	if (len cells == 0)
		return;
	dim := Point(len cells, len cells[0]);
	for (y := 0; y < dim.y; y++) {
		for (x := 0; x < dim.x; x++) {
			cell := cells[x][y];
			# XXX should take into account cell padding
			if (cell != nil) {
				cell.sizereq = getsize(win, cell.w);
# sys->print("cell %d %d size %s span %s\n", x, y, p2s(cell.sizereq), p2s(cell.span));
			}
#  else
# sys->print("cell %d %d blank\n", x, y);
		}
	}

	colwidths := array[dim.x] of {* => 0};
	# calculate column widths (ignoring multi-column cells)
	for (x := 0; x < dim.x; x++) {
		for (y = 0; y < dim.y; y++) {
			cell := cells[x][y];
			if (cell != nil && cell.span.x == 1 && cell.sizereq.x > colwidths[x])
				colwidths[x] = cell.sizereq.x;
		}
	}

	# now check that multi-column cells fit in their columns
	colexpand := array[dim.x] of {* => 1};
	for (x = 0; x < dim.x; x++) {
		for (y = 0; y < dim.y; y++) {
			cell := cells[x][y];
			if (cell != nil && cell.span.x > 1)
				expandwidths(x, cell.sizereq.x, cell.span.x, colwidths, colexpand);
		}
	}
	colexpand = nil;

	rowheights := array[dim.y] of {* => 0};
	# calculate row heights (ignoring multi-row cells)
	for (y = 0; y < dim.y; y++) {
		for (x = 0; x < dim.x; x++) {
			cell := cells[x][y];
			if (cell != nil && cell.span.y == 1 && cell.sizereq.y > rowheights[y])
				rowheights[y] = cell.sizereq.y;
		}
	}

# 	for (i := 0; i < len colwidths; i++)
# 		sys->print("colwidth %d -> %d\n", i, colwidths[i]);
# 	for (i = 0; i < len rowheights; i++)
# 		sys->print("rowheight %d -> %d\n", i, rowheights[i]);

	rowexpand := array[dim.y] of {* => 1};
	# now check that multi-row cells fit in their columns
	for (y = 0; y < dim.y; y++) {
		for (x = 0; x < dim.x; x++) {
			cell := cells[x][y];
			if (cell != nil && cell.span.y > 1)
				expandwidths(y, cell.sizereq.y, cell.span.y, rowheights, rowexpand);
		}
	}

#	if (rowequalise)
#		equalise(rowheights, dim.y);

#	if (colequalise)
#		equalise(colwidths, dim.x);

	# calculate total width and height (including cell padding)
	totsize := Point(0, 0);
	for (x = 0; x < dim.x; x++)
		totsize.x += colwidths[x];
	for (y = 0; y < dim.y; y++)
		totsize.y += rowheights[y];

	cmd(win, "canvas " + w + " -width " + string totsize.x + " -bg white -height " + string totsize.y);
	p := Point(0, 0);
	for (y = 0; y < dim.y; y++) {
		p.x = 0;
		for (x = 0; x < dim.x; x++) {
			cell := cells[x][y];
			if (cell != nil) {
				cellsize := Point(0, 0);
				span := cell.span;
				for (xx := 0; xx < span.x; xx++)
					cellsize.x += colwidths[x + xx];
				for (yy := 0; yy < span.y; yy++)
					cellsize.y += rowheights[y + yy];
# sys->print("cell [%d %d] %d %d +[%d %d]\n", x, y, p.x, p.y, cellsize.x, cellsize.y);
				cmd(win, w + " create window " + p2s(p) +
					" -anchor nw -window " + cell.w +
					" -width " + string cellsize.x +
					" -height " + string cellsize.y);
			}
			p.x += colwidths[x];
		}
		p.y += rowheights[y];
	}
}

expandwidths(x: int, cellwidth, xcells: int, widths: array of int, expand: array of int)
{
	# find out total space available for cell
	share := 0;
	tot := 0;
	endx := x + xcells;
	for (xx := x; xx < endx; xx++) {
		tot += widths[xx];
		if (expand[xx])
			share++;
	}
	slack := cellwidth - tot;

	# if enough space, then nothing to do.
	if (slack <= 0)
		return;

	# allocate extra space amongst all cols that
	# want to expand. (if any do, otherwise share it
	# out between all of them)
	if (share == 0)
		share = xcells;
	for (xx = x; xx < endx; xx++) {
		m := slack / share;
		widths[xx] += m;
		slack -= m;
		share--;
	}
}

getsize(win: ref Tk->Toplevel, w: string): Point
{
	bd := 2 * int cmd(win, w + " cget -bd");
	return Point(int cmd(win, w + " cget -width") + bd,
			int cmd(win, w + " cget -height") + bd);
}

p2s(p: Point): string
{
	return string p.x + " " + string p.y;
}

cmd(win: ref Tk->Toplevel, s: string): string
{
#	sys->print("%ux	%s\n", win, s);
	r := tk->cmd(win, s);
	if (len r > 0 && r[0] == '!') {
		sys->fprint(sys->fildes(2), "error executing '%s': %s\n", s, r);
		raise "tk error";
	}
	return r;
}