Copyright 1996-2000 Critical Mass, Inc. All Rights Reserved. See file COPYRIGHT-CMASS for details.
MODULE; IMPORT VBT, Split, ProperSplit, VBTClass, Region, Rect; IMPORT Point, PaintOp, Axis, Pixmap; REVEAL Private = ProperSplit.T BRANDED OBJECT END; T = Public BRANDED OBJECT tbl : REF ARRAY OF ARRAY OF VBT.T := NIL; nrows, ncols: [0..LAST(CARDINAL)]; rowsize, colsize: CARDINAL := 0; colsz: REF ARRAY OF CARDINAL; rowsz: REF ARRAY OF CARDINAL; margin: CARDINAL := 1; txt: Pixmap.T; bg: PaintOp.T; op: PaintOp.T; OVERRIDES init := Init; repaint := Repaint; reshape := Reshape; shape := Shape; rescreen := Rescreen; END; CONST SizeIncrement = 10; PROCEDURE GridSplit Init (v: T; nrows, ncols: CARDINAL; rowsize, colsize: CARDINAL; margin: CARDINAL; bg: PaintOp.T := PaintOp.Fg; op: PaintOp.T := PaintOp.Bg; txt: Pixmap.T := Pixmap.Solid): T = BEGIN v.margin := margin; v.txt := txt; v.op := op; v.bg := bg; v.tbl := NEW(REF ARRAY OF ARRAY OF VBT.T, nrows, ncols); v.nrows := nrows; v.ncols := ncols; v.rowsz := NEW(REF ARRAY OF CARDINAL, nrows); v.colsz := NEW(REF ARRAY OF CARDINAL, ncols); v.rowsize := rowsize; v.colsize := colsize; FOR row := 0 TO nrows-1 DO v.rowsz[row] := rowsize; END; FOR col := 0 TO ncols-1 DO v.colsz[col] := colsize; END; VBT.PaintTexture (v, VBT.Domain(v), v.bg, v.txt); RETURN v; END Init;
<*UNUSED*> PROCEDURE DumpSizes (v: T) = BEGIN
IO.Put (Fmt.F (Domain: (%s,%s)-(%s,%s)\n
, Fmt.Int(v.domain.west),
Fmt.Int(v.domain.east),
Fmt.Int(v.domain.north),
Fmt.Int(v.domain.south)));
IO.Put (Rows :
);
FOR i := 0 TO v.nrows-1 DO
IO.PutInt(v.rowsz[i]); IO.Put (=
);
IO.PutInt (v.tbl[i,0].domain.north);
IO.Put (
);
END;
IO.Put (\n
);
IO.Put (Cols :
);
FOR i := 0 TO v.ncols-1 DO
IO.PutInt(v.colsz[i]); IO.Put (=
);
IO.PutInt (v.tbl[0,i].domain.west); IO.Put (,
);
IO.PutInt (v.tbl[0,i].domain.east); IO.Put (
);
IO.Put (
);
END;
IO.Put (\n\n
);
END DumpSizes;
PROCEDUREPROCEDURE RemoveRow (v: T; start, n: CARDINAL) = (* RemovePut (v: T; row, col: CARDINAL; ch: VBT.T) = <* FATAL Split.NotAChild *> BEGIN <* ASSERT row < v.nrows *> <* ASSERT col < v.ncols *> IF v.tbl[row,col] # NIL THEN Split.Delete (v, v.tbl[row,col]); END; v.tbl[row,col] := ch; IF ch # NIL THEN Split.AddChild (v, ch); VBT.NewShape(v); END; END Put; PROCEDUREGet (v: T; row, col: CARDINAL): VBT.T = BEGIN RETURN v.tbl[row,col]; END Get; PROCEDUREGetRow (v: T; row: CARDINAL; VAR data: ARRAY OF VBT.T) = BEGIN data := v.tbl[row]; END GetRow; PROCEDUREPutRow (v: T; row: CARDINAL; READONLY data: ARRAY OF VBT.T) = BEGIN FOR col := FIRST(data) TO LAST(data) DO Put(v, row, col, data[col]); END; END PutRow; PROCEDUREInsertRow (v: T; row: CARDINAL; READONLY data: ARRAY OF VBT.T) = VAR orig_nrows := NumRows(v); BEGIN GrowRows(v); FOR i := orig_nrows-1 TO row BY -1 DO v.tbl[i+1] := v.tbl[i]; END; v.tbl[row] := data; Split.AddChildArray (v, data); END InsertRow; PROCEDURESwapRows (v: T; r1,r2: CARDINAL) = VAR temp := NEW(REF ARRAY OF VBT.T, NUMBER(v.tbl[r1])); BEGIN temp^ := v.tbl[r1]; v.tbl[r1] := v.tbl[r2]; v.tbl[r2] := temp^; VBT.Mark(v); END SwapRows; PROCEDURERemoveRow (v: T; row: CARDINAL; VAR deleted: ARRAY OF VBT.T) = <* FATAL Split.NotAChild *> BEGIN deleted := v.tbl[row]; FOR i := row TO v.nrows-2 DO v.tbl[i] := v.tbl[i+1]; END; DEC(v.nrows); FOR i := FIRST(deleted) TO LAST(deleted) DO Split.Delete (v, deleted[i]); END; VBT.NewShape(v); END RemoveRow;
n
rows starting with start
.
BEGIN SUBARRAY(v.tbl, start, n) := SUBARRAY(v.tbl, start + n, n); VBT.NewShape(v); END RemoveRow; *) PROCEDURE*** PROCEDURE OutR (tag: TEXT; READONLY r: Rect.T) = BEGIN IO.Put (tag &GetRowHeight (v: T; row: CARDINAL): CARDINAL = BEGIN RETURN v.rowsz[row]; END GetRowHeight; PROCEDUREGetColWidth (v: T; col: CARDINAL): CARDINAL = BEGIN RETURN v.colsz[col]; END GetColWidth; PROCEDURESetColWidth (v: T; col: CARDINAL; width: CARDINAL) = BEGIN v.colsz[col] := width; VBT.NewShape(v); END SetColWidth; PROCEDUREDim (v: T; VAR nrows, ncols: CARDINAL) = BEGIN nrows := v.nrows; ncols := v.ncols; END Dim; PROCEDURENumRows (v: T): CARDINAL = BEGIN RETURN v.nrows; END NumRows; PROCEDURENumCols (v: T): CARDINAL = BEGIN RETURN v.ncols; END NumCols; PROCEDUREGrowRows (v: T; nrows: CARDINAL := 1) = VAR oldtbl := v.tbl; BEGIN IF NUMBER(v.rowsz^) >= v.nrows THEN WITH incrows = MAX (SizeIncrement, nrows) DO v.rowsz := NEW(REF ARRAY OF CARDINAL, v.nrows + incrows); v.tbl := NEW(REF ARRAY OF ARRAY OF VBT.T, v.nrows + incrows, v.ncols); END; END; FOR row := 0 TO v.nrows-1 DO v.tbl[row] := oldtbl[row]; END; INC(v.nrows, nrows); FOR row := v.nrows-nrows TO v.nrows-1 DO FOR col := 0 TO v.ncols-1 DO v.tbl[row,col] := NIL; END; END; FOR row := 0 TO v.nrows-1 DO v.rowsz[row] := v.rowsize; END; VBT.NewShape(v); END GrowRows; PROCEDUREGetCoord (v: T; top, bottom, left, right: CARDINAL): Rect.T = VAR res := VBT.Domain(v); BEGIN res.north := res.north + v.margin; FOR row := 0 TO top-1 DO INC(res.north, v.rowsz[row] + v.margin); END; res.south := res.north; FOR row := top TO MIN(bottom, v.nrows-1) DO INC(res.south, v.rowsz[row] + v.margin); END; DEC (res.south, v.margin); res.west := res.west + v.margin; FOR col := 0 TO left-1 DO INC(res.west, v.colsz[col] + v.margin); END; res.east := res.west; FOR col := left TO MIN(right, v.ncols-1) DO INC(res.east, v.colsz[col] + v.margin); END; DEC (res.east, v.margin); RETURN res; END GetCoord; PROCEDURESetRowHeight (v: T; row: CARDINAL; height: CARDINAL) = BEGIN v.rowsz[row] := height; VBT.NewShape(v); END SetRowHeight; PROCEDUREWidth (v: T): CARDINAL = VAR res := v.margin * (v.ncols + 1); BEGIN FOR i := 0 TO v.ncols - 1 DO INC (res, v.colsz[i]); END; RETURN res; END Width; PROCEDUREHeight (v: T): CARDINAL = VAR res := v.margin * (v.nrows + 1); BEGIN FOR i := 0 TO v.nrows - 1 DO INC (res, v.rowsz[i]); END; RETURN res; END Height;
: [
& Fmt.Int (r.west) & ,
& Fmt.Int (r.north) & ] -> [
& Fmt.Int (r.east - r.west) & ,
& Fmt.Int (r.south - r.north)
& ]\r\n
);
END OutR;
***
PROCEDURERepaint (v: T; READONLY br: Region.T) RAISES {} = VAR dom := VBT.Domain(v); bigbad := Region.BoundingBox (br); r: Rect.T; height := Height(v); width := Width(v); BEGIN (* erase any bits outside the table... *) IF (dom.north + height < dom.south) THEN r := dom; INC(r.north, height); r := Rect.Meet(r, bigbad); IF NOT Rect.IsEmpty (r) THEN VBT.PaintTexture (v, r, v.bg, v.txt); END; END; IF (dom.west + width < dom.east) THEN r := dom; INC(r.west, width); r := Rect.Meet(r, bigbad); IF NOT Rect.IsEmpty (r) THEN VBT.PaintTexture (v, r, v.bg, v.txt); END; END; (* Horizontal repainting; slide the margin bar through, painting only the band which was covered by the bad region. *) r := Rect.Stretch (dom, Axis.T.Hor, dom.west, dom.west + v.margin); VBT.PaintTexture (v, Rect.Meet (r, bigbad), v.bg, v.txt); FOR i := 0 TO v.ncols-1 DO r := Rect.MoveH (r, v.margin + v.colsz[i]); IF r.east >= bigbad.west AND r.west < bigbad.east THEN VBT.PaintTexture (v, Rect.Meet(r, bigbad), v.bg, v.txt); END; END; (* Vertical repainting; slide the margin bar through, painting only the band which was covered by the bad region. *) r := Rect.Stretch (dom, Axis.T.Ver, dom.north, dom.north + v.margin); VBT.PaintTexture (v, Rect.Meet(r, bigbad), v.bg, v.txt); FOR i := 0 TO v.nrows-1 DO r := Rect.MoveV (r, v.margin + v.rowsz[i]); IF r.south >= bigbad.north AND r.north < bigbad.south THEN VBT.PaintTexture (v, Rect.Meet(r, bigbad), v.bg, v.txt); END; END; FOR row := 0 TO v.nrows-1 DO FOR col := 0 TO v.ncols-1 DO IF v.tbl[row,col] # NIL AND Rect.Meet(VBT.Domain(v.tbl[row,col]), bigbad) # Rect.Empty THEN VBT.ForceRepaint(v.tbl[row,col], br); END; END; END; END Repaint; PROCEDURERescreen (v: T; READONLY cd: VBT.RescreenRec) RAISES {} = BEGIN VBT.NewShape(v); Split.T.rescreen(v, cd) END Rescreen; PROCEDUREReshape (v: T; READONLY cd: VBT.ReshapeRec) RAISES {} = BEGIN VBTClass.LocateChanged(v); Reshape2(v, saved := cd.saved); IF NOT Rect.IsEmpty (cd.new) THEN Repaint (v, Region.FromRect(cd.new)) END; END Reshape; PROCEDUREReshape2 (v: T; irow, icol : CARDINAL := 0; saved: Rect.T := Rect.Empty) RAISES {} = VAR changed := FALSE; dom := VBT.Domain(v); voff: INTEGER := dom.north; hoff: INTEGER := dom.west; BEGIN FOR row := irow TO v.nrows-1 DO INC (voff, v.margin); hoff := dom.west; FOR col := icol TO v.ncols-1 DO INC (hoff, v.margin); WITH ch = v.tbl[row,col] DO VAR new := Rect.T { north := voff, south := voff + v.rowsz[row], west := hoff, east := hoff + v.colsz[col] }; BEGIN IF ch # NIL THEN IF new # VBT.Domain(ch) THEN VBTClass.Reshape (ch, new, saved); VBT.Mark (ch); changed := TRUE; END; END; END; END; INC (hoff, v.colsz[col]); END; INC (voff, v.rowsz[row]); END; IF changed THEN VBT.Mark (v) END; END Reshape2; PROCEDUREShape (v: T; ax: Axis.T; <*UNUSED*>n: CARDINAL): VBT.SizeRange RAISES {} = VAR sz: CARDINAL; BEGIN IF ax = Axis.T.Ver THEN sz := Height(v); ELSE sz := Width(v); END; RETURN VBT.SizeRange { sz, sz, sz+1 }; END Shape; PROCEDURELocate (v: T; p: Point.T): Location = VAR loc := Location { pl := Placement.None, row := 0, col := 0}; voff, hoff: INTEGER; CONST slop: INTEGER = 2; BEGIN (* Question: what happens to the Domain? Answer, you have to keep it in mind! *) IF p.h <= v.domain.west + v.margin AND p.v <= v.domain.north + v.margin THEN loc.pl := Placement.CrossMargins; loc.row := -1; loc.col := -1; ELSIF p.h <= v.domain.west + v.margin THEN loc.pl := Placement.VerMargin; loc.col := -1; ELSIF p.v <= v.domain.north + v.margin THEN loc.pl := Placement.HorMargin; loc.row := -1; END; (* Find the bottom-most and west-most margin which contains the point in question. *) voff := Height(v) + v.domain.north; FOR row := v.nrows-1 TO 0 BY -1 DO IF p.v > voff THEN loc.row := row+1; EXIT; END; DEC (voff, v.rowsz[row] + v.margin); END; hoff := Width(v) + v.domain.west; FOR col := v.ncols-1 TO 0 BY -1 DO IF p.h > hoff THEN loc.col := col+1; EXIT; END; DEC (hoff, v.colsz[col] + v.margin); END; IF loc.row < 0 OR loc.col < 0 OR loc.row >= v.nrows OR loc.col >= v.ncols THEN RETURN loc; END; (* 0 < loc.col < v.ncols and 0 < loc.rows < v.nrows *) IF p.h > hoff + v.colsz[loc.col] - slop THEN IF p.v > voff + v.rowsz[loc.row] - slop THEN loc.pl := Placement.CrossMargins; ELSE loc.pl := Placement.VerMargin; END; ELSE IF p.v > voff + v.rowsz[loc.row] - slop THEN loc.pl := Placement.HorMargin; ELSE loc.pl := Placement.InsideCell; END; END; RETURN loc; END Locate; BEGIN END GridSplit.