TableBaseImpl.Mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Rick Beach, February 18, 1985 8:59:19 pm PST
DIRECTORY
Convert, CornerStitching, EditSpan, EditSpanSupport, IO, Real, Rope, TableBase, TextNode, TiogaFileOps, TSArtwork, TSGraphic, TSOutput, TSTypes;
TableBaseImpl:
CEDAR
PROGRAM
IMPORTS Convert, CornerStitching, EditSpan, EditSpanSupport, IO, Rope, TextNode, TiogaFileOps, TSTypes
EXPORTS TableBase = {
OPEN TableBase;
ImplementationError: PUBLIC ERROR [ROPE] = CODE;
UnimplementedCase: PUBLIC ERROR = CODE;
A Tioga table is expected to be contained within one Tioga branch. The branch root node contains the fillInOrder of ByRowThenColumn or ByColumnThenRow. If omitted, then order is defaulted to ByRowThenColumn. An optional template can be included for empty nodes created in the table by specifying EmptyNodeTemplate in the table-fillInOrder-node. The first child node is saved for this purpose. Each table box is expected to be a child of the branch root node in the appropriate order. The contents of each table box is expected to be the nested subbranch under the table box node. An example:
Grid <rowGrids> Rows <columnGrids> Columns <table-fillInOrder> [EmptyNodeTemplate]
optional emptyNodeTemplate
<table-box-header>
table-box-contents-branch
<table-box-header>
table-box-contents-branch
. . .
<table-box-header>
table-box-contents-branch
where <rowGrids> and <columnGrids> are in the range 0..LAST[CARDINAL],
<table-fillInOrder> is one of byRowThenColumn or byColumnThenRow,
EmptyNodeTemplate specifies that the first child of this node is a template to use for empty nodes in the table,
<table-box-header> is <boxType> (a,b) (c,d) <attributes>
where <boxType> is one of box, rule, or background
and (a,b) (c,d) are the top left and bottom right grid coordinates of the box,
<attributes> are <rowAlignment> <colAlignment> <alignmentCharacter> <bearoff-distances>
BranchToTable:
PUBLIC PROCEDURE [node: TextNode.Ref]
RETURNS [table: RefTable] ~ {
s: IO.STREAM ← IO.RIS[RopeOfNode[node].Concat["\n"]];
objects: ObjectList ← NEW[ObjectListRep ← [IO.GetRefAnyLine[s]]];
atom: ATOM;
tableBoxHeader: TextNode.Ref ← TextNode.FirstChild[node];
table ← NEW[Table];
table.tableGrid ← CornerStitching.NewTesselation[];
table.branch ← node;
IF FirstObject[objects] =
NIL
THEN
ERROR ImplementationError["You have to supply some clues for me in the root node of the table."];
Grid <rowGrids> Rows <columnGrids> Columns
IF
NOT ISTYPE[FirstObject[objects],
ATOM]
THEN
ERROR ImplementationError["Expecting Grid keyword first."];
atom ← NARROW[NextObject[objects]];
IF atom # $Grid
AND atom # $grid
THEN
ERROR ImplementationError["Expecting Grid keyword first, found something else."];
IF
NOT ISTYPE[FirstObject[objects],
REF INT]
THEN
ERROR ImplementationError["Expecting number of row grids after Grid keyword."];
table.rowGrids ← NARROW[NextObject[objects], REF INT]^;
IF
NOT ISTYPE[FirstObject[objects],
ATOM]
THEN
ERROR ImplementationError["Expecting Rows keyword after number of vertical grids."];
atom ← NARROW[NextObject[objects], ATOM];
IF atom # $Rows
AND atom # $rows
THEN
ERROR ImplementationError["Expecting Rows keyword between the numbers."];
IF
NOT ISTYPE[FirstObject[objects],
REF INT]
THEN
ERROR ImplementationError["Expecting number of column grids after Rows keyword."];
table.columnGrids ← NARROW[NextObject[objects], REF INT]^;
IF
NOT ISTYPE[FirstObject[objects],
ATOM]
THEN
ERROR ImplementationError["Expecting Columns keyword after number of vertical grids."];
atom ← NARROW[NextObject[objects], ATOM];
IF atom # $Columns
AND atom # $columns
THEN
ERROR ImplementationError["Expecting Columns keyword between the numbers."];
<fill-in-order> could be either byRowThenColumn or byColumnThenRow
IF
NOT ISTYPE[FirstObject[objects],
ATOM]
THEN
ERROR ImplementationError["Expecting fill in order after grid specification."];
table.fillInOrder ←
SELECT
NARROW[NextObject[objects],
ATOM]
FROM
$ByRowThenColumn => byRowThenColumn,
$ByColumnThenRow => byColumnThenRow,
ENDCASE => ERROR ImplementationError["Unrecognized fill-in-order keyword."];
<gridOverlay> is optional
IF FirstObject[objects] #
NIL
AND ISTYPE[FirstObject[objects],
ATOM]
AND
NARROW[FirstObject[objects],
ATOM] = $GridOverlay
THEN {
[] ← NextObject[objects];
table.gridOverlayThickness ← GetDimn[objects];
table.gridOverlayHue ← GetReal[objects];
table.gridOverlaySaturation ← GetReal[objects];
table.gridOverlayBrightness ← GetReal[objects];
};
EmptyNodeTemplate is optional.
If present, the first child node is a template for empty table boxes.
IF FirstObject[objects] #
NIL
AND ISTYPE[FirstObject[objects],
ATOM]
AND
NARROW[FirstObject[objects],
ATOM] = $EmptyNodeTemplate
THEN {
table.emptyNodeTemplateRoot ← TextNode.NewTextNode[];
[] ← NextObject[objects];
[] ← EditSpan.Copy[
destRoot: table.emptyNodeTemplateRoot,
sourceRoot: TextNode.Root[node],
dest: TextNode.MakeNodeLoc[table.emptyNodeTemplateRoot],
source: TextNode.MakeNodeSpan[tableBoxHeader, tableBoxHeader],
nesting: 1];
tableBoxHeader ← TextNode.NthSibling[tableBoxHeader, 1];
};
Check for supplied constraints
IF FirstObject[objects] #
NIL
AND
ISTYPE[FirstObject[objects],
ATOM]
AND
NARROW[FirstObject[objects],
ATOM] = $RowConstraints
THEN {
constraint: RefConstraint;
WHILE (constraint ← ParseConstraints[table~table, node~tableBoxHeader, row~
TRUE]) #
NIL
DO
table.rowConstraints ← CONS[constraint, table.rowConstraints];
tableBoxHeader ← TextNode.NthSibling[tableBoxHeader, 1];
ENDLOOP;
[] ← NextObject[objects];
};
IF FirstObject[objects] #
NIL
AND
ISTYPE[FirstObject[objects],
ATOM]
AND
NARROW[FirstObject[objects],
ATOM] = $ColConstraints
THEN {
constraint: RefConstraint;
WHILE (constraint ← ParseConstraints[table~table, node~tableBoxHeader, row~
FALSE]) #
NIL
DO
table.colConstraints ← CONS[constraint, table.colConstraints];
tableBoxHeader ← TextNode.NthSibling[tableBoxHeader, 1];
ENDLOOP;
[] ← NextObject[objects];
};
IF FirstObject[objects] #
NIL
THEN
ERROR ImplementationError["Extraneous objects on table header"];
Parse successive table box headers
WHILE tableBoxHeader #
NIL
DO
entry: RefTableEntry ← ParseTableBox[table, tableBoxHeader];
InsertEntryInGrid[table, entry];
tableBoxHeader ← TextNode.Next[tableBoxHeader];
ENDLOOP;
table.branch ← NIL; -- same some space, eh?
};
InsertEntryInGrid:
PUBLIC PROCEDURE [table: RefTable, entry: RefTableEntry] ~ {
WITH entry
SELECT
FROM
box: RefTableBox => {
rect: CornerStitching.Rect ~ InsideGridToRect[box.left, box.top, box.right, box.bottom];
CornerStitching.ChangeRect[plane~table.tableGrid, rect~rect, newvalue~box, checkOldvalue~
TRUE, oldvalue~
NIL !
CornerStitching.TileValue =>
ERROR ImplementationError["Box overlaps another box or rule"]];
};
rule: RefTableRule => {
rect: CornerStitching.Rect ~ OnGridToRect[rule.left, rule.top, rule.right, rule.bottom];
overlap: BOOLEAN ← FALSE;
CornerStitching.ChangeRect[plane~table.tableGrid, rect~rect, newvalue~rule, checkOldvalue~FALSE];
CornerStitching.ChangeRect[plane~table.tableGrid, rect~rect, newvalue~rule, checkOldvalue~TRUE, oldvalue~NIL !
CornerStitching.TileValue => {overlap ← TRUE; CONTINUE}];
IF rule overlaps with boxes THEN
ERROR ImplementationError["Rule overlaps another box"];
IF rule overlaps with rule THEN handle corner and intersections;
};
background: RefTableBackground => {
table.backgrounds ← CONS[background, table.backgrounds];
};
ENDCASE => ERROR UnimplementedCase;
};
ParseTableBox:
PROCEDURE [table: RefTable, tableBoxHeader: TextNode.Ref]
RETURNS [entry: RefTableEntry] ~ {
s: IO.STREAM ~ IO.RIS[RopeOfNode[tableBoxHeader].Concat["\n"]];
objects: ObjectList ← NEW[ObjectListRep ← [IO.GetRefAnyLine[s]]];
gridList: LIST OF REF ANY;
top, left, bottom, right: GridNumber;
boxType: BoxType;
<boxType> (a,b) (c,d) <attributes>
IF
NOT ISTYPE[FirstObject[objects],
ATOM]
THEN
ERROR ImplementationError["Expected keyword box type first in the table box header"];
SELECT
NARROW[NextObject[objects],
ATOM]
FROM
$Box => boxType ← box;
$Rule => boxType ← rule;
$Background => boxType ← background;
ENDCASE => ERROR ImplementationError["Unrecognized box type keyword."];
IF
NOT ISTYPE[FirstObject[objects],
LIST
OF
REF
ANY]
THEN
ERROR ImplementationError["Expected grid coordinates as first object in table box header"];
gridList ← NARROW[NextObject[objects], LIST OF REF ANY];
top ← NARROW[gridList.first, REF INT]^;
left ← NARROW[gridList.rest.first, REF INT]^;
IF
NOT ISTYPE[FirstObject[objects],
LIST
OF
REF
ANY]
THEN
ERROR ImplementationError["Expected grid coordinates as second object in table box header"];
gridList ← NARROW[NextObject[objects], LIST OF REF ANY];
bottom ← NARROW[gridList.first, REF INT]^;
right ← NARROW[gridList.rest.first, REF INT]^;
IF top > table.rowGrids
OR bottom > table.rowGrids
OR left > table.columnGrids
OR right > table.columnGrids
THEN
ERROR ImplementationError["Coordinates are outside the table grid."];
SELECT boxType
FROM
box => {
box: RefTableBox ← NEW[TableBox ← [top~top, left~left, bottom~bottom, right~right, entrySpecs~box[]]];
IF FirstObject[objects] #
NIL
THEN {
vertical alignment specification is optional
box.rowAlignment ←
SELECT
NARROW[NextObject[objects],
ATOM]
FROM
$FlushTop => flushTop,
$FlushBottom => flushBottom,
$Center => center,
$Centre => center,
$TopBaseline => topBaseline,
$BottomBaseline => bottomBaseline,
$CenterOnTopBaseline => centerOnTopBaseline,
$CentreOnTopBaseline => centerOnTopBaseline,
$CenterOnBottomBaseline => centerOnBottomBaseline,
$CentreOnBottomBaseline => centerOnBottomBaseline,
ENDCASE => ERROR ImplementationError["expected vertical alignment as third object in table box header"];
};
IF FirstObject[objects] #
NIL
THEN {
horizontal alignment specification is optional
box.colAlignment ←
SELECT
NARROW[NextObject[objects],
ATOM]
FROM
$FlushLeft => flushLeft,
$FlushRight => flushRight,
$Center => center,
$Centre => center,
ENDCASE => ERROR ImplementationError["expected horizontal alignment as fourth object in table box header"];
};
IF FirstObject[objects] #
NIL
AND
ISTYPE[FirstObject[objects],
ATOM]
AND
$CharAlign =
NARROW[NextObject[objects],
ATOM]
THEN {
character alignment is optional but must be followed by a character
box.alignOnChar ← TRUE;
box.alignChar ← NARROW[NextObject[objects], REF CHAR]^;
};
IF FirstObject[objects] #
NIL
THEN {
bearoff extents are optional but must be last
box.bearoffExtents[left] ← GetDimn[objects];
box.bearoffExtents[right] ← GetDimn[objects];
box.bearoffExtents[up] ← GetDimn[objects];
box.bearoffExtents[down] ← GetDimn[objects];
};
box.node ← TextNode.FirstChild[tableBoxHeader];
entry ← box;
};
rule => {
rule: RefTableRule ← NEW[TableRule ← [top~top, left~left, bottom~bottom, right~right, entrySpecs~rule[]]];
IF top # bottom
AND left # right
THEN
ERROR ImplementationError["Rules must be along a grid line with top = bottom or left = right"];
rule.orientation ← IF top = bottom THEN horizontal ELSE vertical;
rule.thickness ← GetDimn[objects];
entry ← rule;
};
background => {
background: RefTableBackground ← NEW[TableBackground ← [top~top, left~left, bottom~bottom, right~right, entrySpecs~background[]]];
background.hue ← GetReal[objects];
background.saturation ← GetReal[objects];
background.brightness ← GetReal[objects];
entry ← background;
};
ENDCASE => ERROR UnimplementedCase;
IF FirstObject[objects] #
NIL
THEN
ERROR ImplementationError["Extraneous objects for this table entry"];
};
ParseConstraints:
PROC [table: RefTable, node: TextNode.Ref, row:
BOOLEAN]
RETURNS [constraint: RefConstraint] ~ {
s: IO.STREAM ~ IO.RIS[RopeOfNode[node].Concat["\n"]];
sign: REAL ← +1.0;
coeff: Coefficient;
coeffs: LIST OF Coefficient;
equality: BOOLEAN;
tokenKind: IO.TokenKind;
token: ROPE;
charsSkipped: INT;
Check that the expected constraint keyword appears
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
IF tokenKind # tokenID
THEN
ERROR ImplementationError["Expected constraint keyword to come first"];
IF row
AND
NOT token.Equal["RowConstraint"]
OR
~row
AND
NOT token.Equal["ColConstraint"]
THEN
RETURN [NIL];
Accept a leading negative sign for the constraint expression
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
IF token.Equal["-"]
THEN {
sign ← -1.0;
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
}
Accept an expression of the form: <term> <+|-> <expression>
Where <term> is of the form: <number> * <id>
DO
SELECT tokenKind
FROM
tokenREAL => coeff.coefficient ← sign*Convert.RealFromRope[token];
tokenDECIMAL => coeff.coefficient ← sign*Convert.IntFromRope[token];
ENDCASE => ERROR ImplementationError["Expected numeric coefficient"];
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
IF tokenKind # tokenSINGLE
OR
NOT token.Equal["*"]
THEN
ERROR ImplementationError["Expected multiply sign"];
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
IF tokenKind # tokenID
THEN
ERROR ImplementationError["Expected identifier"];
coeff.unknown ← token;
coeffs ← CONS[coeff, coeffs];
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
SELECT
TRUE
FROM
token.Equal["="] => { equality ← TRUE; EXIT; };
token.Equal[">="] => { equality ← FALSE; EXIT; };
token.Equal["+"] => sign ← +1.0;
token.Equal["-"] => sign ← -1.0;
ENDCASE => ERROR ImplementationError["Expected additive or relational operator"];
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
ENDLOOP;
[tokenKind, token, charsSkipped] ← IO.GetCedarTokenRope[s];
IF
NOT token.Equal["0"]
AND
NOT token.Equal["0.0"]
THEN
ERROR ImplementationError["Expected right-hand side to always be 0 or 0.0"];
constraint ← NEW[Constraint ← [equality, coeffs]];
};
ObjectList: TYPE ~ REF ObjectListRep;
ObjectListRep: TYPE ~ RECORD[list: LIST OF REF ANY];
FirstObject:
PROCEDURE[objects: ObjectList]
RETURNS [o:
REF
ANY] = {
IF objects #
NIL
AND objects.list #
NIL
THEN
o ← objects.list.first;
};
NextObject:
PROCEDURE[objects: ObjectList]
RETURNS [o:
REF
ANY] = {
IF objects =
NIL
OR objects.list =
NIL
THEN
ERROR ImplementationError["prematurely exhausted the supply of objects for this table entry"];
o ← objects.list.first;
objects.list ← objects.list.rest;
};
GetDimn:
PROC [objects: ObjectList]
RETURNS [TSTypes.Dimn] ~ {
object: REF ANY ~ NextObject[objects];
r: REAL;
multiplier: REAL;
IF ISTYPE[object, REF REAL] THEN r ← NARROW[object, REF REAL]^
ELSE IF ISTYPE[object, REF INT] THEN r ← NARROW[object, REF INT]^
ELSE ERROR ImplementationError["Expecting a real number and didn't get it"];
multiplier ←
SELECT
NARROW[NextObject[objects],
ATOM]
FROM
$in => 72.0,
$pt => 72.0/72.27,
$cm => 72.0/2.54,
$mm => 72.0/25.4,
$bp => 1.0,
ENDCASE => 1.0;
RETURN [TSTypes.Pt[r*multiplier]];
};
GetReal:
PROC [objects: ObjectList]
RETURNS [r:
REAL] ~ {
object: REF ANY ~ NextObject[objects];
IF ISTYPE[object, REF REAL] THEN r ← NARROW[object, REF REAL]^
ELSE IF ISTYPE[object, REF INT] THEN r ← NARROW[object, REF INT]^
ELSE ERROR ImplementationError["Expecting a real number and didn't get it"];
};
TableToBranch:
PUBLIC PROCEDURE [table: RefTable]
RETURNS [nodeRef: TextNode.Ref] ~ {
branchRoot, branch, prevLast: TiogaFileOps.Ref;
nodeRope: ROPE;
CopyNodeAsChildOfLastNode:
PROCEDURE [node: TextNode.Ref, prevLast: TiogaFileOps.Ref] = {
prevLastTextNode: TextNode.Ref;
TRUSTED { prevLastTextNode ← LOOPHOLE[prevLast] };
prevLastTextNode.child ← EditSpanSupport.CopySpan[TextNode.MakeNodeSpan[node, TextNode.LastWithin[node]]].start.node;
prevLastTextNode.child.next ← prevLastTextNode;
};
InsertTableBox: EnumeratedEntryProc = {
PROCEDURE [table: RefTable, entry: RefTableEntry] RETURNS [stop: BOOLEAN ← FALSE];
rope: ROPE ← TableEntryToRope[entry];
prevLast ← TiogaFileOps.InsertAsLastChild[branch, prevLast];
TiogaFileOps.SetContents[prevLast, rope];
TiogaFileOps.SetFormat[prevLast, "block"];
WITH entry
SELECT
FROM
box: RefTableBox => CopyNodeAsChildOfLastNode[box.node, prevLast];
ENDCASE => NULL;
};
branchRoot ← TiogaFileOps.CreateRoot[];
TRUSTED { branch ←
LOOPHOLE[EditSpan.Copy[
destRoot: LOOPHOLE[branchRoot],
sourceRoot: TextNode.Root[table.branch],
dest: TextNode.MakeNodeLoc[LOOPHOLE[branchRoot]],
source: TextNode.MakeNodeSpan[table.branch, table.branch],
nesting: 1].start.node] };
prevLast ← NIL;
nodeRope ← "Grid";
nodeRope ← nodeRope.Concat[IO.PutFR[" %g Rows %g Columns", IO.card[table.rowGrids], IO.card[table.columnGrids]]];
nodeRope ← nodeRope.Concat[
SELECT table.fillInOrder
FROM
byRowThenColumn => " ByRowThenColumn",
byColumnThenRow => " ByColumnThenRow",
ENDCASE => ""];
IF table.gridOverlayThickness # TSTypes.nilDimn
THEN {
nodeRope ← nodeRope.Concat[" GridOverlay"];
nodeRope ← nodeRope.Concat[IO.PutFR[" %g pt", IO.real[table.gridOverlayThickness.texPts]]];
nodeRope ← nodeRope.Concat[IO.PutFR[" %g %g %g", IO.real[table.gridOverlayHue], IO.real[table.gridOverlaySaturation], IO.real[table.gridOverlayBrightness]]];
};
fill in table attributes here . . . if we had any (not yet implemented)
IF table.emptyNodeTemplateRoot #
NIL THEN {
nodeRope ← nodeRope.Concat[" EmptyNodeTemplate"];
CopyNodeAsChildOfLastNode[TextNode.FirstChild[table.emptyNodeTemplateRoot], branch];
TRUSTED { prevLast ← LOOPHOLE[TextNode.FirstChild[LOOPHOLE[branch]]] };
};
TiogaFileOps.SetContents[branch, nodeRope];
IF table.backgrounds #
NIL
THEN {
FOR b:
LIST
OF RefTableBackground ← table.backgrounds, b.rest
WHILE b #
NIL
DO
background: RefTableBackground ← b.first;
[] ← InsertTableBox[table, background];
ENDLOOP;
};
fill in table entries here
IF table.fillInOrder = byRowThenColumn
THEN
EnumerateByRows[table, InsertTableBox]
ELSE
EnumerateByColumns[table, InsertTableBox];
TRUSTED { nodeRef ← LOOPHOLE[branch] };
};
TableEntryToRope:
PUBLIC PROCEDURE [entry: RefTableEntry]
RETURNS [rope:
ROPE ←
NIL] ~ {
rope ←
SELECT entry.boxType
FROM
box => "Box",
rule => "Rule",
background => "Background",
ENDCASE => "";
rope ← rope.Concat[" "];
rope ← rope.Concat[IO.PutFR["(%g, %g) (%g, %g)", IO.card[entry.top], IO.card[entry.left], IO.card[entry.bottom], IO.card[entry.right]]];
rope ← rope.Concat[" "];
WITH entry
SELECT
FROM
box: RefTableBox => {
rope ← rope.Concat[
SELECT box.rowAlignment
FROM
flushTop => "FlushTop",
flushBottom => "FlushBottom",
center => "Center",
topBaseline => "TopBaseline",
bottomBaseline => "BottomBaseline",
centerOnTopBaseline => "CenterOnTopBaseline",
centerOnBottomBaseline => "CenterOnBottomBaseline",
ENDCASE => ""];
rope ← rope.Concat[" "];
rope ← rope.Concat[
SELECT box.colAlignment
FROM
flushLeft => "FlushLeft",
flushRight => "FlushRight",
center => "Center",
ENDCASE => ""];
IF box.alignOnChar
THEN {
rope ← rope.Concat[" CharAlign"];
rope ← rope.Cat["'", Rope.FromChar[box.alignChar]];
};
IF box.bearoffExtents[left] # TSTypes.nilDimn
THEN {
rope ← rope.Concat[" "];
rope ← rope.Concat[IO.PutFR["%g pt %g pt %g pt %g pt", IO.real[box.bearoffExtents[left]], IO.real[box.bearoffExtents[right]], IO.real[box.bearoffExtents[up]], IO.real[box.bearoffExtents[down]]]]
};
};
rule: RefTableRule => {
rope ← rope.Concat[IO.PutFR["%g pt", IO.real[rule.thickness.texPts]]];
};
background: RefTableBackground => {
rope ← rope.Concat[IO.PutFR["%g %g %g", IO.real[background.hue], IO.real[background.saturation], IO.real[background.brightness]]];
};
ENDCASE => ERROR UnimplementedCase;
};
RopeOfNode:
PROCEDURE [node: TextNode.Ref]
RETURNS [rope:
ROPE ←
NIL] ~ {
WITH node SELECT FROM x: TextNode.RefTextNode => RETURN[x.rope]; ENDCASE => NULL;
};
BoxFromTile:
PUBLIC
PROCEDURE [tile: CornerStitching.TilePtr]
RETURNS [box: RefTableBox] ~ {
IF tile.Value #
NIL
AND
ISTYPE[tile.Value, RefTableBox]
THEN
box ← NARROW[tile.Value];
};
TileFromBox:
PUBLIC
PROCEDURE [box: RefTableBox]
RETURNS [tile: CornerStitching.TilePtr] ~ {
oops -- we need the table to find the table grid to use CornerStitching.TileAt
ERROR ImplementationError["TileFromBox not implemented yet"];
};
InsideGridToRect:
PUBLIC PROCEDURE [left, top, right, bottom: GridNumber]
RETURNS [CornerStitching.Rect] = {
RETURN [[4*left+2, 4*top+2, 4*right-1, 4*bottom-1]]
};
OnGridToRect:
PUBLIC PROCEDURE [left, top, right, bottom: GridNumber]
RETURNS [CornerStitching.Rect] = {
RETURN [[4*left, 4*top, 4*right+1, 4*bottom+1]]
};
EnumerateByRows:
PUBLIC PROCEDURE [table: RefTable, entryProc: EnumeratedEntryProc] ~ {
EnumerateTable[table~table, entryProc~entryProc, left~FIRST[GridNumber], right~LAST[GridNumber], top~FIRST[GridNumber], bottom~LAST[GridNumber]];
};
EnumerateByColumns:
PUBLIC PROCEDURE [table: RefTable, entryProc: EnumeratedEntryProc] ~ {
EnumerateByRows[table, entryProc];
};
EnumerateTable:
PUBLIC
PROCEDURE [table: RefTable, entryProc: EnumeratedEntryProc, left: GridNumber ←
FIRST[GridNumber], right: GridNumber ←
LAST[GridNumber], top: GridNumber ←
FIRST[GridNumber], bottom: GridNumber ←
LAST[GridNumber]] ~ {
TileProc: CornerStitching.PerTileProc ~ {
entry: RefTableEntry ← NARROW[CornerStitching.Value[tile]];
IF entry # NIL THEN [] ← entryProc[table, entry];
RETURN[NIL];
};
[] ← CornerStitching.EnumerateArea[plane~table.tableGrid, rect~OnGridToRect[left, top, right, bottom], perTile~TileProc];
};
WithinGridLines:
PUBLIC PROCEDURE [entry: RefTableBox, which: RowOrColumn, grid1, grid2: GridNumber]
RETURNS [
BOOLEAN] ~ {
RETURN [
SELECT which
FROM
row => entry.top >= grid1 AND entry.bottom <= grid2,
column => entry.left >= grid1 AND entry.right <= grid2,
ENDCASE => FALSE]
};
}.