ref: d09cf07a82cf4ffb846a31d0384e774b4c4661e1
dir: /appl/ebook/table.b/
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; }