Formatted Boxes To and From Tioga
CopyDocument:
PROC [root: TextNode.Node]
RETURNS [TextNode.Node] ~ {
IF root=NIL THEN RETURN[NIL]
ELSE {
CopyAction: NodeProps.MapPropsAction = {
[name: ATOM, value: REF] RETURNS [quit: BOOL ← FALSE];
newValue: REF ¬ NodeProps.CopyInfo[name, value];
NodeProps.PutProp[newRoot, name, newValue];
RETURN[FALSE];
};
span: TextNode.Span ¬ TextNode.MakeNodeSpan[first: TextNode.FirstChild[root], last: TextNode.LastLocWithin[root].node];
copy: TextNode.Span ¬ EditSpanSupport.CopySpan[span];
newRoot: TextNode.Ref ¬ TextNode.Root[copy.start.node];
[] ¬ NodeProps.MapProps[n: root, action: CopyAction];
RETURN [newRoot];
};
};
GetDataFromLoc:
PROC [node: TextNode.Ref, where:
INT]
RETURNS [BoxCharData] ~ {
charProps: Tioga.PropList ¬ TextEdit.GetCharPropList[node: node, index: where];
boxCharData: BoxCharData ¬ NARROW[TextEdit.GetPropFromList[charProps, $data]];
IF boxCharData =
NIL
THEN {
hoboRope: ROPE ¬ NARROW[TextEdit.GetPropFromList[charProps, $hoboRope]];
IF hoboRope #
NIL
THEN {
boxCharData ¬ NEW[BoxCharDataRep ¬
[node: TiogaIO.FromRope[hoboRope], screenBox: NIL, printBox: NIL]];
TextEdit.PutCharProp[node: node, index: where,
name: $data, value: boxCharData];
Is it safe to do this while formatting? If not, could spawn a process . . .
}
ELSE {
boxCharData ¬ NIL;
boxCharData ← NEW[BoxCharDataRep ←
[node: TextEdit.DocFromNode[TextEdit.FromRope["#"]],
screenBox: NIL, printBox: NIL]];
};
};
RETURN[boxCharData]
};
HoboNameAndRange:
PROC [node: TextNode.Ref]
RETURNS [name:
ATOM ¬ noName, start, end: TextNode.Location] ~ {
Returns start and end of node, which may be initial leftDelim and final rightDelim.
Returns name if node is bracketed with leftDelim"hobo=name" and rightDelim.
nodeRope: ROPE ¬ TextEditBogus.GetRope[TextNode.FirstChild[node]];
start ¬ [TextNode.FirstChild[node], 0];
end ¬ TextNode.LastLocWithin[node];
IF Rope.IsPrefix[prefix: hoboPrefix, subject: nodeRope, case:
TRUE]
AND end.where > 0
AND Char.Narrow[TextEdit.FetchChar[end.node, end.where-1]] = rightDelim
THEN
{
nameEnd: INT ¬ Rope.SkipOver[nodeRope, hoboPrefixLen, legalNameChars];
IF nameEnd > hoboPrefixLen
THEN name ¬ Atom.MakeAtom[nodeRope.Substr[
hoboPrefixLen, nameEnd-hoboPrefixLen]];
};
};
HoboArgumentFind: PROC [first, last: TextNode.Location, name: ROPE]
RETURNS [found: BOOL ← FALSE, node: TextNode.Ref, where: INT] ~ {
Looks for "name=", and returns location following "=". Uses TreeFind.
at, atEnd, before, after: INT;
IF name.InlineLength > 0
THEN {
[found, node, at, atEnd, before, after] ← TreeFind.Try[
finder: TreeFind.CreateFromRope[pattern: name, literal: TRUE, word: TRUE],
first: first.node, start: first.where, last: last.node, lastLen: last.where+1];
IF (found ← found
AND TextEdit.Size[node] > atEnd
AND TextEdit.FetchChar[node, atEnd].char = '=)
THEN where ← atEnd+1;
}
ELSE {
[found, node, at, where, before, after] ← TreeFind.Try[
finder: TreeFind.CreateFromRope[pattern: "=", literal: TRUE, word: FALSE],
first: first.node, start: first.where, last: last.node, lastLen: last.where+1];
};
};
HoboArgumentFind:
PROC [first, last: TextNode.Location, name:
ROPE]
RETURNS [found:
BOOL ¬
FALSE, node: TextNode.Ref, where:
INT] ~ {
Looks for "name=", and returns location following "=".
IF name.Length > 0
THEN {
target: TextFind.Target ~ TextFind.TargetFromRope[name];
[node: node, matchEnd: where] ¬ TiogaFind.Search[direction: forward,
loc1: first, loc2: last, target: target, match: word];
IF node#NIL
AND where<TextEdit.Size[node]
AND TextEdit.FetchChar[node, where] = Char.Widen['=]
THEN { found ¬ TRUE; where ¬ where+1 };
}
ELSE {
target: TextFind.Target ~ TextFind.TargetFromRope["="];
[node: node, matchEnd: where] ¬ TiogaFind.Search[direction: forward,
loc1: first, loc2: last, target: target, match: any];
IF node # NIL THEN found ¬ TRUE;
};
IF NOT found THEN RETURN[FALSE, NIL, 0];
};
HoboArgumentBox:
PROC [first, last: TextNode.Location, name:
ROPE, default: BoxCharData]
RETURNS [found:
BOOL ¬
FALSE, val: BoxCharData] ~ {
Looks for "name=X", where X should be BoxChar or node, and returns data for X.
hitNode: TextNode.Ref;
where: INT;
[found, hitNode, where] ¬ HoboArgumentFind[first, last, name];
IF found
THEN {
IF TextEdit.Size[hitNode] > where
THEN {
data: BoxCharData ¬ GetDataFromLoc[hitNode, where];
IF data # NIL THEN RETURN[TRUE, data];
}
ELSE {
IF TextNode.FirstChild[hitNode] #
NIL
THEN {
argSpan: TextNode.Span ¬ TextNode.MakeNodeSpan[
TextNode.FirstChild[hitNode], TextNode.LastWithin[hitNode]];
copySpan: TextNode.Span ¬ EditSpanSupport.CopySpan[argSpan];
copyRoot: TextNode.Ref ¬ TextNode.Root[copySpan.start.node];
EditSpan.Inherit[TextNode.Root[hitNode], copyRoot, TRUE];
RETURN[TRUE, NEW[BoxCharDataRep ¬ [node: copyRoot]]];
};
};
};
RETURN[FALSE, default];
};
HoboArgumentReal:
PROC [first, last: TextNode.Location, name:
ROPE, default:
REAL]
RETURNS [found:
BOOL ¬
FALSE, val:
REAL] ~ {
Looks for "name=X", where X should be real literal, and returns value for X, else default.
hitNode: TextNode.Ref;
where, size: INT;
val ¬ default;
[found, hitNode, where] ¬ HoboArgumentFind[first, last, name];
IF found
AND (size ¬ TextEdit.Size[hitNode]) > where
THEN {
val ¬ Convert.RealFromRope[TextEditBogus.GetRope[hitNode].Substr[where, size-1]
! Convert.Error => {
Scold[Rope.Cat["\tError in REAL arg \"", name, "\"."]];
CONTINUE;
}];
}
};
HoboArgumentInt:
PROC [first, last: TextNode.Location, name:
ROPE, default:
INT]
RETURNS [found:
BOOL ¬
FALSE, val:
INT] ~ {
Looks for "name=X", where X should be int literal, and returns value for X, else default.
hitNode: TextNode.Ref;
where, size: INT;
val ¬ default;
[found, hitNode, where] ¬ HoboArgumentFind[first, last, name];
IF found
AND (size ¬ TextEdit.Size[hitNode]) > where
THEN {
val ¬ Convert.IntFromRope[TextEditBogus.GetRope[hitNode].Substr[where, size-1]
! Convert.Error => {
Scold[Rope.Cat["\tError in REAL arg \"", name, "\"."]];
CONTINUE;
}];
}
};
HoboArgumentName:
PROC [first, last: TextNode.Location, name:
ROPE, default:
ATOM]
RETURNS [found:
BOOL ¬
FALSE, val:
ATOM] ~ {
Looks for "name=X", where X should be atom literal, and returns value for X, else default.
hitNode: TextNode.Ref;
where, size: INT;
val ¬ default;
[found, hitNode, where] ¬ HoboArgumentFind[first, last, name];
IF found
AND (size ¬ TextEdit.Size[hitNode]) > where
THEN {
argRope: ROPE ¬ TextEditBogus.GetRope[hitNode].Substr[where, size-1];
val ¬ Convert.AtomFromRope[argRope.Substr[0,argRope.SkipOver[skip: legalNameChars]]
! Convert.Error => {
Scold[Rope.Cat["\tError in name arg \"", name, "\"."]];
CONTINUE;
}];
}
};
BoxFromNode:
PROC [node: TextNode.Ref, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [myBox: TiogaImager.Box ¬ TiogaImager.EmptyBox[[2, 0]]] ~ {
name: ATOM;
firstLoc, lastLoc: TextNode.Location;
boxMakerRec: BoxMakerRec;
IF node = NIL THEN RETURN;
[name, firstLoc, lastLoc] ¬ HoboNameAndRange[node];
boxMakerRec ¬ NARROW[RefTab.Fetch[boxMakerTable, name].val];
IF boxMakerRec =
NIL
OR boxMakerRec.proc =
NIL
THEN {
Scold[Rope.Cat["\tUnknown hobo name \"", Atom.GetPName[name], "\"."]];
myBox ¬ NoNameBoxMaker[firstLoc, lastLoc, train, bounds];
}
ELSE myBox ¬ boxMakerRec.proc[firstLoc, lastLoc, train, bounds];
};
BoxInTrain:
PROC [data: BoxCharData, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [box: TiogaImager.Box] ~ {
IF train.style.kind # print
THEN {
IF data.screenBox =
NIL
THEN data.screenBox ¬ BoxFromNode[data.node, train, bounds];
box ¬ data.screenBox;
}
ELSE {
IF data.printBox =
NIL
THEN data.printBox ¬ BoxFromNode[data.node, train, bounds];
box ¬ data.printBox;
};
};
Scold:
PROC [msg:
ROPE] ~ {
MessageWindow.Append[msg, TRUE];
MessageWindow.Blink[];
};
BoxFromSelection: TEditInput.CommandProc ~ {
[viewer: ViewerClasses.Viewer ← NIL] RETURNS [recordAtom: BOOL ← TRUE, quit: BOOL ← FALSE]
IF TEditSelection.pSel.granularity = point
-- TiogaAccess blows up on point selection
THEN Scold["\tSelect more than a point."]
ELSE {
reader: TiogaAccess.Reader ¬ TiogaAccessViewers.FromSelection[];
ref: TextNode.Ref ¬ IF TiogaAccess.EndOf[reader] THEN TextNode.NewTextNode[] ELSE TextNode.Root[TiogaAccess.GetLocation[reader].node];
myRef: TextNode.Ref ¬ CopyDocument[ref]; -- make a copy that we own. System will finalize its own copy.
selectionBox ¬ NEW[BoxCharDataRep ¬ [node: myRef, screenBox: NIL, printBox: NIL]];
MessageWindow.Append["\tNew box made from selection.", TRUE];
RETURN[TRUE, TRUE];
};
};
BoxToSelection: TEditInput.CommandProc ~ {
[viewer: ViewerClasses.Viewer ← NIL] RETURNS [recordAtom: BOOL ← TRUE, quit: BOOL ← FALSE]
IF TEditSelection.pSel.granularity = point
THEN Scold["\tSelect more than a point."]
ELSE {
selectionViewer: Viewer ¬ TEditSelection.pSel.viewer;
reader: TiogaAccess.Reader ¬ TiogaAccessViewers.FromSelection[];
writer: TiogaAccess.Writer ¬ TiogaAccess.Create[];
locked:
PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] ~ {
TiogaAccessViewers.WriteSelection[writer]; -- replaces the primary selection
};
tc: TiogaAccess.TiogaChar ¬ reader.Get[];
hoboRope: ROPE ¬ TiogaIO.ToRope[selectionBox.node];
newProps: Tioga.PropList ¬ LIST[
[$data, selectionBox],
[$hoboRope, hoboRope],
[$Artwork, whoWeAreRope]];
FOR pl: Tioga.PropList ¬ tc.propList, pl.rest
UNTIL pl =
NIL DO
IF pl.first.key # $data
AND pl.first.key # $hoboRope
AND pl.first.key # $Artwork
THEN newProps ¬ CONS[pl.first, newProps];
ENDLOOP;
tc.propList ¬ newProps; -- preserve immutability of prop list
tc.charSet ¬ 0;
tc.char ¬ 'x;
TiogaAccess.Put[writer, tc]; -- puts everything at once
TEditInputOps.CallWithLocks[locked];
ViewerOps.PaintViewer[viewer: selectionViewer, hint: client];
MessageWindow.Append["\tBoxChar created.", TRUE];
};
RETURN[TRUE, TRUE];
};
ViewBox: TEditInput.CommandProc ~ {
[viewer: ViewerClasses.Viewer ← NIL] RETURNS [recordAtom: BOOL ← TRUE, quit: BOOL ← FALSE]
IF PrimarySelectionIsOneChar[]
THEN {
loc: TextNode.Location ¬ TEditSelection.pSel.start.pos;
data: BoxCharData ¬ GetDataFromLoc[loc.node, loc.where];
IF data =
NIL
THEN Scold["\tNot a BoxChar."]
ELSE {
viewer: Viewer ¬ ViewerTools.MakeNewTextViewer[info: [name: "TiogaFromBoxChar", column: TEditSelection.pSel.viewer.column], paint: FALSE ];
May need TextEdit.DocFromNode[TiogaIO.FromRope[data.node]]
viewer.class.set[viewer, CopyDocument[data.node], FALSE, $TiogaDocument];
ViewerOps.OpenIcon[viewer];
};
}
ELSE Scold["\tSelect a single BoxChar."];
RETURN[TRUE, TRUE];
};
PrimarySelectionIsOneChar:
PROC
RETURNS [
BOOL] ~ {
pSelStart: TextNode.Location ¬ TEditSelection.pSel.start.pos;
pSelEnd: TextNode.Location ¬ TEditSelection.pSel.end.pos;
RETURN[TEditSelection.pSel.granularity # point AND pSelStart.node = pSelEnd.node AND pSelStart.where = pSelEnd.where];
};
Box Makers
NoNameBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [myBox: TiogaImager.Box ¬ TiogaImager.EmptyBox[[2, 0]]] ~ {
Ordinary old box from node
IF first.node = last.node
THEN {
Try a one-liner, which ought to be very common . . .
nodeStyle: NodeStyle.Ref ¬ NodeStyleOps.Alloc[];
NodeStyleOps.ApplyAll[nodeStyle, first.node, train.style.kind];
myBox ¬ TiogaImager.FormatLine[[first.node, 0], bounds.x, nodeStyle];
NodeStyleOps.Free[nodeStyle];
IF myBox.nChars = TextEdit.Size[first.node]
THEN {
Whew! It all fit. Bounding box can be tight easily.
myBox.bounds ¬ TEditFormat.BoundingBox[NARROW[myBox.data]];
myBox.escapement ¬ [myBox.bounds.xmax, 0];
RETURN;
};
};
Looks like a one-liner won't do it. Return sloppy bounding box. Sigh.
{
myFN: TiogaImager.FormattedNodes ¬ IF first.node # NIL
THEN TiogaImager.FormatNodes[
start: [first.node, 0], bounds: bounds, styleKind: train.style.kind]
ELSE [TiogaImager.EmptyBox[], TextNode.MakeNodeLoc[NIL]];
subBoxList: LIST OF TiogaImager.Box ¬ TiogaImager.UnBox[myBox ¬ myFN.box];
vshift: REAL ¬ IF subBoxList # NIL THEN -subBoxList.first.escapement.y ELSE 0;
myBox.escapement ¬ [myBox.bounds.xmax, 0];
myBox ¬ myBox.ModifyBox[ImagerTransformation.Translate[[0, vshift]]];
RETURN;
};
};
TripileBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [triBox: TiogaImager.Box] ~ {
Attempting a composite box!! args are "upper=U center=C lower=L"
default: BoxCharData ~ emptyBoxChar;
upper: BoxCharData ¬ HoboArgumentBox[first, last, "upper", default].val;
center: BoxCharData ¬ HoboArgumentBox[first, last, "center", default].val;
lower: BoxCharData ¬ HoboArgumentBox[first, last, "lower", default].val;
upperGap: REAL ¬ HoboArgumentReal[first, last, "upperGap", 0.0].val;
lowerGap: REAL ¬ HoboArgumentReal[first, last, "lowerGap", 0.0].val;
upperBox: TiogaImager.Box ¬ BoxInTrain[upper, train];
centerBox: TiogaImager.Box ¬ BoxInTrain[center, train];
lowerBox: TiogaImager.Box ¬ BoxInTrain[lower, train];
ub, cb, lb: Imager.Box;
uBox, cBox, lBox: TiogaImager.Box;
[uBox, ub] ¬ TopCenterBox[upperBox];
uBox.escapement.y ¬ uBox.escapement.y-upperGap;
[cBox, cb] ¬ TopCenterBox[centerBox];
cBox.escapement.y ¬ cBox.escapement.y-lowerGap;
[lBox, lb] ¬ TopCenterBox[lowerBox];
triBox ¬ TiogaImager.BoxFromList[LIST[uBox, cBox, lBox]];
triBox ¬ triBox.ModifyBox[
ImagerTransformation.Translate[[-triBox.bounds.xmin, cb.ymax-uBox.escapement.y]]];
triBox.escapement ¬ [triBox.bounds.xmax, 0];
};
TopCenterBox:
PROC [box: TiogaImager.Box]
RETURNS [tcBox: TiogaImager.Box, bds: Imager.Box] ~ {
bds ¬ box.bounds;
tcBox ¬ box.ModifyBox[
ImagerTransformation.Translate[[-(bds.xmin+bds.xmax)/2, -bds.ymax]]];
tcBox.escapement ¬ [0, bds.ymin-bds.ymax];
};
RootBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [fractionBox: TiogaImager.Box]
~ {
default: BoxCharData ~ emptyBoxChar;
radicandData: BoxCharData ¬ HoboArgumentBox[first, last, "radicand", default].val;
rootData: BoxCharData ← HoboArgumentBox[first, last, "root", default].val;
barGap: REAL ¬ HoboArgumentReal[first, last, "barGap", 2.0].val;
barThickness: REAL ¬ HoboArgumentReal[first, last, "barThickness", 0.72].val;
radicandBox: TiogaImager.Box ¬ BoxInTrain[radicandData, train];
rootBox: TiogaImager.Box ← BoxInTrain[rootData, train];
grid: Grid ¬ EmptyGrid[];
ExtendGraph[grid, [2, 4], 2*barGap+barThickness, 0];
GridInsertEntry[grid, [0, 3], [2, 4], [level: 'n, plumb: 'c], radicandBox];
GridInsertEntry[grid, [1, 0], [2, 1], [level: 'n, plumb: 'c], rootBox];
GridInsertSep[grid, [1, 0], [1, 1], barThickness];
grid.plumbBase ¬ NIL;
grid.levelBase ¬ LineName[0, "0NL2"];
fractionBox ¬ BoxFromGrid[grid];
};
FractionBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [fractionBox: TiogaImager.Box]
~ {
default: BoxCharData ~ emptyBoxChar;
numerData: BoxCharData ¬ HoboArgumentBox[first, last, "numer", default].val;
denomData: BoxCharData ¬ HoboArgumentBox[first, last, "denom", default].val;
barGap: REAL ¬ HoboArgumentReal[first, last, "barGap", 2.0].val;
numerOffset: REAL ¬ HoboArgumentReal[first, last, "numerOffset", 4.0].val;
denomOffset: REAL ¬ HoboArgumentReal[first, last, "denomOffset", 8.0].val;
barThickness: REAL ¬ HoboArgumentReal[first, last, "barThickness", 0.72].val;
axisHeight: REAL ¬ HoboArgumentReal[first, last, "axisHeight", GetAxisHeight[train]].val;
numerBox: TiogaImager.Box ¬ BoxInTrain[numerData, train];
denomBox: TiogaImager.Box ¬ BoxInTrain[denomData, train];
grid: Grid ¬ EmptyGrid[];
ExtendGraph[grid, [2, 1], 2*barGap+barThickness, 0];
GridInsertEntry[grid, [0, 0], [1, 1], [level: 'n, plumb: 'c], numerBox];
GridInsertEntry[grid, [1, 0], [2, 1], [level: 'n, plumb: 'c], denomBox];
[] ¬ SpreadSpan[grid, LineName[0, "NL", 1], LineName[1, "SL"], NIL, numerOffset];
[] ¬ SpreadSpan[grid, LineName[1, "SL"], LineName[1, "NL", 2], NIL, denomOffset];
GridInsertSep[grid, [1, 0], [1, 1], barThickness];
[] ¬ SpreadSpan[grid, LineName[1, "SL"], LineName[0, "BASE"], NIL, axisHeight];
grid.plumbBase ¬ NIL;
grid.levelBase ¬ LineName[0, "BASE"];
fractionBox ¬ BoxFromGrid[grid];
};
LimitBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [limitsBox: TiogaImager.Box]
~ {
default: BoxCharData ~ emptyBoxChar;
upperData: BoxCharData ¬ HoboArgumentBox[first, last, "upper", default].val;
operatorData: BoxCharData ¬ HoboArgumentBox[first, last, "operator", default].val;
lowerData: BoxCharData ¬ HoboArgumentBox[first, last, "lower", default].val;
gap: REAL ¬ HoboArgumentReal[first, last, "gap", 2.0].val;
upperOffset: REAL ¬ HoboArgumentReal[first, last, "upperOffset", 4.0].val;
lowerOffset: REAL ¬ HoboArgumentReal[first, last, "lowerOffset", 8.0].val;
axisHeight: REAL ← HoboArgumentReal[first, last, "axisHeight", GetAxisHeight[train]].val;
upperBox: TiogaImager.Box ¬ BoxInTrain[upperData, train];
operatorBox: TiogaImager.Box ¬ BoxInTrain[operatorData, train];
lowerBox: TiogaImager.Box ¬ BoxInTrain[lowerData, train];
grid: Grid ¬ EmptyGrid[];
ExtendGraph[grid, [3, 1], gap, 0];
GridInsertEntry[grid, [0, 0], [1, 1], [level: 'n, plumb: 'c], upperBox];
GridInsertEntry[grid, [1, 0], [2, 1], [level: 'n, plumb: 'c], operatorBox];
GridInsertEntry[grid, [2, 0], [3, 1], [level: 'n, plumb: 'c], lowerBox];
[] ¬ SpreadSpan[grid, LineName[0, "NL", 1], LineName[1, "NL", 2], NIL, upperOffset];
[] ¬ SpreadSpan[grid, LineName[1, "NL", 2], LineName[2, "NL", 3], NIL, lowerOffset];
grid.plumbBase ¬ NIL;
grid.levelBase ¬ LineName[1, "NL", 2];
limitsBox ¬ BoxFromGrid[grid];
};
BarBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [myBox: TiogaImager.Box ¬ TiogaImager.EmptyBox[[2, 0]]] ~ {
height: REAL ¬ HoboArgumentReal[first, last, "height", 1.0].val;
width: REAL ¬ HoboArgumentReal[first, last, "width", 1.0].val;
myBox ¬
NEW[TiogaImager.BoxRep ¬ [
nChars: 0,
bounds: [xmin: 0, ymin: -height, xmax: width, ymax: 0],
expansion: [0, 0],
escapement: [width, 0],
stretch: [0, 0],
shrink: [0, 0],
class: barClass,
data: NEW[BarDataRep ¬ [width, height]]
]];
};
BarRender:
PROC [box: TiogaImager.Box, context: Imager.Context, position: Imager
.VEC] ~ {
DoBar:
PROC ~ {
context.SetXY[position];
context.StartUnderline[];
context.SetXRel[data.width];
context.MaskUnderline[dy: 0, h: data.height];
};
data: BarData ¬ NARROW[box.data];
context.DoSaveAll[DoBar];
};
AxisCenteredBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [myBox: TiogaImager.Box ¬ TiogaImager.EmptyBox[[2, 0]]] ~ {
default: BoxCharData ~ emptyBoxChar;
axisHeight: REAL ¬ HoboArgumentReal[first, last, "axisHeight", GetAxisHeight[train]].val;
center: REAL;
myBox ¬ BoxInTrain[HoboArgumentBox[first, last, "arg", default].val, train];
center ¬ (myBox.bounds.ymax + myBox.bounds.ymin)/2;
myBox ¬ myBox.ModifyBox[ImagerTransformation.Translate[[0, axisHeight-center]]];
};
GetAxisHeight:
PROC [train: Train]
RETURNS [axisHeight:
REAL ¬ 0.0] ~ {
font: ImagerFont.Font ¬ NodeStyle.GetFont[train.style];
eqExtents: ImagerFont.Extents ¬ font.RopeBoundingBox["="];
axisHeight ¬ (eqExtents.ascent+eqExtents.descent)/2 - eqExtents.descent;
};
GridBoxMaker:
PROC [first, last: TextNode.Location, train: Train, bounds: Imager.
VEC ¬ arbBounds]
RETURNS [myBox: TiogaImager.Box ¬ TiogaImager.EmptyBox[[2, 0]]] ~ {
For matrices and tables. Takes a series of boxes, and aligns them both ways.
default: BoxCharData ~ emptyBoxChar;
gridEntry: BoxCharData;
base: ATOM ~ Atom.EmptyAtom[];
plumbBase: ROPE ¬ Atom.GetPName[HoboArgumentName[first, last, "plumbBase", base].val];
levelBase: ROPE ¬ Atom.GetPName[HoboArgumentName[first, last, "levelBase", base].val];
verticalSkip: REAL ¬ HoboArgumentReal[first, last, "verticalSkip", 32].val;
verticalGap: REAL ¬ HoboArgumentReal[first, last, "verticalGap", 3].val;
horizontalSkip: REAL ¬ HoboArgumentReal[first, last, "horizontalSkip", 20].val;
horizontalGap: REAL ¬ HoboArgumentReal[first, last, "horizontalGap", 3].val;
defaultBarThickness: REAL ¬ HoboArgumentReal[first, last, "defaultBarThickness", 0].val;
barThickness: REAL ¬ 0.0;
defaultFillColor: Imager.Color ¬ NIL;
fillColor: Imager.Color;
rowStep: INT ¬ HoboArgumentInt[first, last, "rowStep", 0].val;
colStep: INT ¬ HoboArgumentInt[first, last, "colStep", 1].val;
rowNow, colNow: INT ¬ 0;
rowOpp, colOpp: INT ¬ 1;
found, foundOther: BOOL ¬ FALSE;
lineup: FeaturePair ¬ ['n, 'n];
loc, nextLoc, endLoc: TextNode.Location;
grid: Grid ¬ EmptyGrid[];
GetNextEntry:
PROC
RETURNS[
BOOL] ~ {
size: INT;
[found, loc.node, loc.where] ¬ HoboArgumentFind[nextLoc, endLoc, ""];
IF NOT found THEN RETURN[FALSE];
[[rowNow, colNow], [rowOpp, colOpp], lineup]
¬ GetCorners[[rowNow, colNow], [rowOpp, colOpp], rowStep, colStep, loc];
size ¬ TextEdit.Size[loc.node];
IF (foundOther ¬ lineup.plumb = 's
OR lineup.plumb = 'f)
THEN {
gridEntry ¬ NIL;
IF TextEdit.Size[loc.node] > loc.where
THEN {
nextLoc ¬ [node: loc.node, where: loc.where+1];
SELECT lineup.plumb
FROM
's => {
barThickness ¬ Convert.RealFromRope[
Rope.Substr[TextEditBogus.GetRope[loc.node], loc.where, size-1]
! Convert.Error => {CONTINUE}];
};
'f => {
gray: REAL ¬ 0.25;
gray ¬ Convert.RealFromRope[
Rope.Substr[TextEditBogus.GetRope[loc.node], loc.where, size-1]
! Convert.Error => {CONTINUE}];
fillColor ¬ Imager.MakeGray[gray];
};
ENDCASE;
}
ELSE {
IF loc.node.next =
NIL
THEN nextLoc ¬ endLoc
ELSE nextLoc ¬ [node: loc.node.next, where: 0];
};
}
ELSE {
IF TextEdit.Size[loc.node] > loc.where
THEN {
nextLoc ¬ [node: loc.node, where: loc.where+1];
gridEntry ¬ GetDataFromLoc[loc.node, loc.where];
}
ELSE {
IF loc.node.next =
NIL
THEN nextLoc ¬ endLoc
ELSE nextLoc ¬ [node: loc.node.next, where: 0];
IF TextNode.FirstChild[loc.node] #
NIL
THEN {
argSpan: TextNode.Span ¬ TextNode.MakeNodeSpan[
TextNode.FirstChild[loc.node], TextNode.LastWithin[loc.node]];
copySpan: TextNode.Span ¬ EditSpanSupport.CopySpan[argSpan];
copyRoot: TextNode.Ref ¬ TextNode.Root[copySpan.start.node];
EditSpan.Inherit[TextNode.Root[loc.node], copyRoot, TRUE];
gridEntry ¬ NEW[BoxCharDataRep ¬ [node: copyRoot]];
}
ELSE gridEntry ¬ NIL;
};
};
RETURN[TRUE];
};
[found, loc.node, loc.where] ¬ HoboArgumentFind[first, last, "entries"];
IF NOT found THEN RETURN;
nextLoc ¬ [node: TextNode.FirstChild[loc.node], where: 0];
endLoc ¬ TextNode.LastLocWithin[loc.node];
WHILE GetNextEntry[]
DO
ExtendGraph[grid, [rowOpp, colOpp], verticalGap, horizontalGap];
IF gridEntry #
NIL
THEN GridInsertEntry[grid, [rowNow, colNow], [rowOpp, colOpp], lineup, BoxInTrain[gridEntry, train]]
ELSE {
IF foundOther
THEN
SELECT lineup.plumb
FROM
's => GridInsertSep[grid, [rowNow, colNow], [rowOpp, colOpp], barThickness];
'f => GridInsertFill[grid, [rowNow, colNow], [rowOpp, colOpp], fillColor];
ENDCASE;
};
ENDLOOP;
grid.plumbBase ¬ plumbBase;
grid.levelBase ¬ levelBase;
myBox ¬ BoxFromGrid[grid];
};
EmptyGrid:
PROC RETURNS [grid: Grid]
~ {grid ¬ NEW[GridRep ¬ [centerList: RefTab.Create[], graph: QPSetup.NewGraph[]]]};
LineName:
PROC [topLine:
INT, name:
ROPE, botLine:
INT ¬ -1]
RETURNS [
ROPE]
~ {RETURN[Rope.Cat[Convert.RopeFromInt[topLine], name,
IF botLine < 0 THEN "" ELSE Convert.RopeFromInt[botLine]]]};
ExtendGraph:
PROC [grid: Grid, botCorner: Corner, verticalGap, horizontalGap:
REAL]
~ {
graph: Graph ¬ grid.graph;
FOR this:
NAT
IN (grid.maxLevel..botCorner.level]
DO
ExtendGraphOne[graph, this, "SL", "TL", "BL", verticalGap];
ENDLOOP;
grid.maxLevel ¬ MAX[grid.maxLevel, botCorner.level];
FOR this:
NAT
IN (grid.maxPlumb..botCorner.plumb]
DO
ExtendGraphOne[graph, this, "SP", "LP", "RP", horizontalGap];
ENDLOOP;
grid.maxPlumb ¬ MAX[grid.maxPlumb, botCorner.plumb];
};
ExtendGraphOne:
PROC [graph: Graph, this:
NAT, sep, top, bot:
ROPE, gap:
REAL]
~ {
prev: NAT ¬ this-1;
prevSep: Vertex ¬ QPSetup.AcquireVertex[graph, LineName[prev, sep]];
prevTop: Vertex ¬ QPSetup.AcquireVertex[graph, LineName[prev, top]];
thisBot: Vertex ¬ QPSetup.AcquireVertex[graph, LineName[this, bot]];
thisSep: Vertex ¬ QPSetup.AcquireVertex[graph, LineName[this, sep]];
sPt: Edge ¬ QPSetup.AcquireEdge[graph, prevSep, prevTop];
pt: Edge ¬ QPSetup.AcquireEdge[graph, prevTop, thisBot];
bPs: Edge ¬ QPSetup.AcquireEdge[graph, thisBot, thisSep];
AdjustEdge[graph, sPt, gap/2, 100.0];
AdjustEdge[graph, bPs, gap/2, 100.0];
};
AdjustEdge:
PROC [graph: Graph, edge: Edge,
spread, squeeze:
REAL ¬ 0.0, relax:
BOOL ¬
TRUE]
~ {
QPSetup.SpreadEdge[graph, edge, MAX[edge.eData.spread, spread]];
IF squeeze # 0.0 THEN QPSetup.SqueezeEdge[graph, edge, squeeze];
IF relax THEN QPSetup.RelaxEdge[graph, edge, MAX[edge.eData.relaxed, spread]];
};
SpreadSpan:
PROC [
grid: Grid, top, bot, mid:
ROPE, spread:
REAL, offset:
REAL ¬ 0.0, relax:
BOOL ¬
TRUE]
RETURNS [Vertex]
~ {
graph: Graph ¬ grid.graph;
topVertex: Vertex ¬ QPSetup.AcquireVertex[graph, top];
botVertex: Vertex ¬ QPSetup.AcquireVertex[graph, bot];
midVertex: Vertex;
span: Edge ¬ QPSetup.AcquireEdge[graph, topVertex, botVertex];
topMid, midBot: Edge;
AdjustEdge[graph, span, spread, 0.0, relax];
IF Rope.IsEmpty[mid] THEN RETURN[IF offset < 0 THEN topVertex ELSE botVertex];
midVertex ¬ QPSetup.AcquireVertex[graph, mid];
topMid ¬ QPSetup.AcquireEdge[graph, topVertex, midVertex];
midBot ¬ QPSetup.AcquireEdge[graph, midVertex, botVertex];
AdjustEdge[graph, topMid, offset, 0.0, relax];
AdjustEdge[graph, midBot, spread-offset, 0.0, relax];
IF Rope.SkipTo[mid, 0, "C"] < Rope.Length[mid]
IF Rope.Fetch[mid, 0] = 'C
THEN {
eqn: QPSetup.TermList ¬ LIST[[topMid.eData, 1.0], [midBot.eData, -1.0]];
[] ¬ RefTab.Insert[grid.centerList, midVertex, eqn];
};
RETURN[midVertex];
};
GridInsertSep:
PROC [grid: Grid, topCorner, botCorner: Corner, barThickness:
REAL] ~ {
graph: Graph ¬ grid.graph;
sepInfo: GridSep ¬ [barThickness: barThickness];
sepInfo.topLevel ¬ QPSetup.AcquireVertex[graph, LineName[topCorner.level, "SL"]];
sepInfo.topPlumb ¬ QPSetup.AcquireVertex[graph, LineName[topCorner.plumb, "SP"]];
sepInfo.botLevel ¬ QPSetup.AcquireVertex[graph, LineName[botCorner.level, "SL"]];
sepInfo.botPlumb ¬ QPSetup.AcquireVertex[graph, LineName[botCorner.plumb, "SP"]];
grid.sepList ¬ CONS[sepInfo, grid.sepList];
};
GridInsertFill:
PROC [grid: Grid, topCorner, botCorner: Corner, fillColor: Imager.Color] ~ {
graph: Graph ~ grid.graph;
fillInfo: GridFill ¬ [fillColor: fillColor];
fillInfo.topLevel ¬ QPSetup.AcquireVertex[graph, LineName[topCorner.level, "SL"]];
fillInfo.topPlumb ¬ QPSetup.AcquireVertex[graph, LineName[topCorner.plumb, "SP"]];
fillInfo.botLevel ¬ QPSetup.AcquireVertex[graph, LineName[botCorner.level, "SL"]];
fillInfo.botPlumb ¬ QPSetup.AcquireVertex[graph, LineName[botCorner.plumb, "SP"]];
grid.fillList ¬ CONS[fillInfo, grid.fillList];
};
GridInsertEntry:
PROC [grid: Grid, topCorner, botCorner: Corner, lineup: FeaturePair, box: TiogaImager.Box] ~ {
quote: CHAR ~ '\047;
xmin: REAL ~ MIN[0.0, box.bounds.xmin];
ymin: REAL ~ MIN[0.0, box.bounds.ymin];
xmax: REAL ~ MAX[0.0, box.bounds.xmax];
ymax: REAL ~ MAX[0.0, box.bounds.ymax];
width: REAL ~ xmax-xmin;
height: REAL ~ ymax-ymin;
trueWidth: REAL ~ box.bounds.xmax-box.bounds.xmin;
trueHeight: REAL ~ box.bounds.ymax-box.bounds.ymin;
graph: Graph ~ grid.graph;
tackLevel, tackPlumb: Vertex;
offsetLevel, offsetPlumb: REAL ¬ 0.0;
Extend graph plumb and level lines to include botCorner of new entry.
Spread sides of span far enough to accomodate box.
Include offset to base point, decimal point, or center point.
SELECT lineup.level
FROM
'N, 'n => {
tackLevel ¬ SpreadSpan[grid,
LineName[topCorner.level, "TL"], LineName[botCorner.level, "BL"],
LineName[topCorner.level, "NL", botCorner.level],
height, ymax];
};
'C, 'c => {
tackLevel ¬ SpreadSpan[grid,
LineName[topCorner.level, "TL"], LineName[botCorner.level, "BL"],
LineName[topCorner.level, "CL", botCorner.level],
trueHeight, trueHeight/2];
offsetLevel ¬ -ymin-trueHeight/2;
};
'T, 't => {
tackLevel ¬ SpreadSpan[grid,
LineName[topCorner.level, "TL"], LineName[botCorner.level, "BL"], NIL,
height, -1];
offsetLevel ¬ -ymax;
};
'B, 'b => {
tackLevel ¬ SpreadSpan[grid,
LineName[topCorner.level, "TL"], LineName[botCorner.level, "BL"], NIL,
height, +1];
offsetLevel ¬ -ymin;
};
ENDCASE => tackLevel ¬ SpreadSpan[grid,
LineName[topCorner.level, "TL"], LineName[botCorner.level, "BL"], NIL, height];
SELECT lineup.plumb
FROM
'N, 'n => {
tackPlumb ¬ SpreadSpan[grid,
LineName[topCorner.plumb, "LP"], LineName[botCorner.plumb, "RP"],
LineName[topCorner.plumb, "NP", botCorner.plumb],
width, xmin];
};
'C, 'c => {
tackPlumb ¬ SpreadSpan[grid,
LineName[topCorner.plumb, "LP"], LineName[botCorner.plumb, "RP"],
LineName[topCorner.plumb, "CP", botCorner.plumb],
trueWidth, trueWidth/2];
offsetPlumb ¬ -xmin-trueWidth/2;
};
'L, 'l => {
tackPlumb ¬ SpreadSpan[grid,
LineName[topCorner.plumb, "LP"], LineName[botCorner.plumb, "RP"], NIL,
width, -1];
offsetPlumb ¬ -xmin;
};
'R, 'r => {
tackPlumb ¬ SpreadSpan[grid,
LineName[topCorner.plumb, "LP"], LineName[botCorner.plumb, "RP"], NIL,
width, +1];
offsetPlumb ¬ -xmax;
};
'D, 'd, quote => {
toDecimal: REAL ¬ xmax;
IF lineup.plumb # quote THEN alignOnChar ¬ '.;
WITH box.data
SELECT
FROM
lineInfo: TEditFormat.LineInfo => {
rope: ROPE ¬ TextEditBogus.GetRope[lineInfo.startPos.node].Substr[lineInfo.startPos.where];
decpnt: INT ¬ rope.SkipTo[0, Rope.FromChar[alignOnChar]];
toDecimal ¬ TEditFormat.BoundingBox[lineInfo, 0, decpnt].xmax;
offsetPlumb ¬ -toDecimal;
};
ENDCASE;
tackPlumb ¬ SpreadSpan[grid,
LineName[topCorner.plumb, "LP"], LineName[botCorner.plumb, "RP"],
LineName[topCorner.plumb, "DP", botCorner.plumb],
width, toDecimal];
};
ENDCASE => tackPlumb ¬ SpreadSpan[grid,
LineName[topCorner.plumb, "LP"], LineName[botCorner.plumb, "RP"], NIL, width];
IF offsetLevel # 0.0
OR offsetPlumb # 0.0
THEN box ¬ TiogaImager.ModifyBox[box,
ImagerTransformation.Translate[[offsetPlumb, offsetLevel]]];
grid.entryList ¬ CONS[[tackLevel, tackPlumb, box], grid.entryList];
};
BoxFromGrid:
PROC [grid: Grid]
RETURNS [box: TiogaImager.Box] ~ {
Center: RefTab.EachPairAction
~ {QPSetup.ConstrainEdges[grid.graph, NARROW[val]]};
xSize, ySize, yCenter, xOrg, yOrg: REAL ¬ 0;
[] ¬ SpreadSpan[grid, "00BL", "0SL", NIL, 0.0]; -- Make top corner alphabetically first.
[] ¬ SpreadSpan[grid, "00RP", "0SP", NIL, 0.0]; -- This should make its coords (0, 0).
[] ¬ RefTab.Pairs[grid.centerList, Center];
QPSetup.SolveGraph[grid.graph];
xSize ¬ QPSetup.AcquireVertex[grid.graph, LineName[grid.maxPlumb, "SP"]].val;
ySize ¬ QPSetup.AcquireVertex[grid.graph, LineName[grid.maxLevel, "SL"]].val;
IF grid.plumbBase.IsEmpty[]
THEN xOrg ¬ 0
ELSE xOrg ¬ QPSetup.AcquireVertex[grid.graph, grid.plumbBase].val;
IF grid.levelBase.IsEmpty[]
THEN yOrg ¬ ySize
ELSE yOrg ¬ QPSetup.AcquireVertex[grid.graph, grid.levelBase].val;
grid.origin ← [0, ySize];
grid.origin ¬ [xOrg, yOrg];
box ¬
NEW[TiogaImager.BoxRep ¬ [
nChars: 0,
bounds: [xmin: 0, ymin: 0, xmax: xSize, ymax: ySize],
bounds: [xmin: -xOrg, ymin: yOrg-ySize, xmax: xSize-xOrg, ymax: yOrg],
expansion: [0, 0],
escapement: [xSize-xOrg, 0],
stretch: [0, 0],
shrink: [0, 0],
class: gridClass,
data: grid
]];
};
GridRender:
PROC [box: TiogaImager.Box, context: Imager.Context, position: Imager
.VEC] ~ {
DoGridBackground:
PROC ~ {
context.TranslateT[position];
context.TranslateT[grid.origin];
FOR fill:
LIST
OF GridFill ¬ grid.fillList, fill.rest
UNTIL fill =
NIL
DO
context.SetColor[fill.first.fillColor];
context.MaskBox[[fill.first.topPlumb.val, -fill.first.botLevel.val,
fill.first.botPlumb.val, -fill.first.topLevel.val]];
ENDLOOP;
context.SetStrokeEnd[square];
context.SetColor[Imager.black]; -- why did Ken add this?
FOR sep:
LIST
OF GridSep ¬ grid.sepList, sep.rest
UNTIL sep =
NIL
DO
context.SetStrokeWidth[sep.first.barThickness];
context.MaskVector[[sep.first.topPlumb.val, -sep.first.topLevel.val],
[sep.first.botPlumb.val, -sep.first.botLevel.val]];
ENDLOOP;
};
DoGridForeground:
PROC ~ {
context.TranslateT[position];
context.TranslateT[grid.origin];
FOR entry:
LIST
OF GridEntry ¬ grid.entryList, entry.rest
UNTIL entry =
NIL
DO
TiogaImager.Render[entry.first.box, context, [entry.first.tackPlumb.val, -entry.first.tackLevel.val]];
ENDLOOP;
IF ticWidth # 0.0 THEN ShowTics[context, position, grid.graph];
};
grid: Grid ¬ NARROW[box.data];
context.DoSaveAll[DoGridBackground];
context.DoSaveAll[DoGridForeground];
};
ShowTics:
PROC [context: Imager.Context, position: Imager.
VEC, graph: Graph]
~ {
Imager.SetStrokeWidth[context, 0.72];
FOR vL: QPSetup.VertexList ¬ graph.vList.rest, vL.rest
UNTIL vL.first =
NIL
DO
kind: ROPE ¬ Rope.Substr[vL.name, Rope.SkipOver[vL.name, 0, "0123456789"], 2];
vTics: BOOL ¬ Rope.Fetch[kind, 1] = 'P;
ticLen:
REAL ¬
SELECT Rope.Fetch[kind, 0]
FROM
'S => 7, 'N, 'D, 'C => 5, 'R, 'L, 'T, 'B => 3
ENDCASE => 2;
IF vTics
THEN Imager.MaskVector[context, [vL.first.val, 0], [vL.first.val, ticLen]]
ELSE Imager.MaskVector[context, [0, -vL.first.val], [-ticLen, -vL.first.val]];
ENDLOOP;
};
GetCorners:
PROC [topWas, botWas: Corner, rowStep, colStep:
INT, loc: TextNode.Location]
RETURNS [top: Corner ¬ [0, 0], bot: Corner¬ [1, 1], lineup: FeaturePair ¬ ['n, 'c]] ~ {
Should look for explicit Now, Opp before "=" here
Possibly use "r@c,ropp@copp/arac=", where omitted r or c defaults to 0
Omitted opp or now defaults to keep same opp-now difference (clipped to 0)
Omitted all just increments by step
Column alignment type ac is l, r, c, d, or n, for left, right, center, decimal, or natural
Row alignment type ar is t, b, c, or n, for top, bottom, center, or natural; default n
rowDif: INT ¬ botWas.level-topWas.level;
colDif: INT ¬ botWas.plumb-topWas.plumb;
nodeRope: ROPE ¬ TextEditBogus.GetRope[loc.node];
ris: IO.STREAM;
kind: IO.TokenKind;
token: ROPE;
expected: {toprow, topcol, botrow, botcol} ¬ toprow;
seen: BOOL ¬ FALSE;
rectRope: ROPE ¬ "=";
topSeen, botSeen: BOOL ¬ FALSE;
FOR i:
INT ¬ loc.where-2, i-1
WHILE i >= 0
DO
SELECT nodeRope.Fetch[i]
FROM
'\t, '\040 => {rectRope ¬ nodeRope.Substr[i+1, loc.where-i-1]; EXIT};
ENDCASE;
REPEAT
FINISHED => rectRope ¬ nodeRope.Substr[0, loc.where];
ENDLOOP;
ris ¬ IO.RIS[rectRope];
WHILE
NOT
IO.EndOf[ris]
DO
[kind, token, ] ¬ IO.GetCedarTokenRope[ris];
SELECT kind
FROM
tokenDECIMAL => {
IF
NOT seen
THEN {
SELECT expected
FROM
toprow => {top.level ¬ Convert.IntFromRope[token]; topSeen ¬ TRUE};
topcol => {top.plumb ¬ Convert.IntFromRope[token]; topSeen ¬ TRUE};
botrow => {bot.level ¬ Convert.IntFromRope[token]; botSeen ¬ TRUE};
botcol => {bot.plumb ¬ Convert.IntFromRope[token]; botSeen ¬ TRUE};
ENDCASE;
seen ¬ TRUE;
}
ELSE {Scold["\tGrid corner: Unexpected number."];};
};
tokenSINGLE => {
SELECT token.Fetch[0]
FROM
'@ => {
SELECT expected
FROM
toprow => expected ¬ topcol;
botrow => expected ¬ botcol;
ENDCASE => {Scold["\tGrid corner: Unexpected atsign."];};
seen ¬ FALSE;
};
', => {
SELECT expected
FROM
toprow, topcol => expected ¬ botrow;
ENDCASE => {Scold["\tGrid corner: Unexpected comma."];};
seen ¬ FALSE;
};
'= => EXIT;
'/ => {
quote: CHAR ~ '\047;
ch1: CHAR ¬ ris.GetChar[];
GetLineup:
PROC
RETURNS [lineup: FeaturePair]
~ {
<< Check for generalized decimal lineups like "/'?=" and "/t'\t=". >>
SELECT ch1
FROM
'= => {Scold["\tPremature equal sign"]; RETURN[['n, 'c]]};
quote => GOTO decimal1;
ENDCASE;
SELECT ris.PeekChar[]
FROM
'= => RETURN[['n, ch1]];
quote => GOTO decimal2;
ENDCASE;
RETURN[[ch1, ris.GetChar[]]];
EXITS
decimal1 => {
ris.Backup[ch1];
alignOnChar ¬ ris.GetCharLiteral[];
RETURN[['n, quote]];
};
decimal2 => {
alignOnChar ¬ ris.GetCharLiteral[];
RETURN[[ch1, quote]];
};
};
lineup ¬ GetLineup[];
};
ENDCASE => {Scold["\tGrid corner: Unexpected single token."];};
};
ENDCASE => {Scold["\tGrid corner: Unexpected token."];};
ENDLOOP;
SELECT
TRUE
FROM
NOT topSeen
AND
NOT botSeen => {
top.level ¬ topWas.level+rowStep;
top.plumb ¬ topWas.plumb+colStep;
bot.level ¬ top.level+rowDif;
bot.plumb ¬ top.plumb+colDif;
};
topSeen
AND
NOT botSeen => {
bot.level ¬ top.level+rowDif;
bot.plumb ¬ top.plumb+colDif;
};
NOT topSeen
AND botSeen => {
top.level ¬ top.level-rowDif;
top.plumb ¬ top.plumb-colDif;
};
topSeen AND botSeen => {--No defaulting needed--};
ENDCASE;
};
Cover: TYPE ~ REF CoverRep;
CoverRep: TYPE ~ RECORD [
min: INT ← 0,
max: INT ← 1,
dif: INT ← 1,
inc: INT ← 1,
end: INT ← 1,
its: INT ← 1,
align: ["c", "."],
tab: "\t"
];
GetCoverage: PROC [plumbWas, levelWas: Cover, : TextNode.Location]
RETURNS [plumbCover: Cover, levelCover: Cover, style: ROPE]
~ {
Syntax: [min:max<inc>end align] or {min:+dif^its}.
nodeRope: ROPE ← TextNode.NodeRope[loc.node];
ris: IO.STREAM;
kind: IO.TokenKind;
token: ROPE;
FOR i: INT ← loc.where-2, i-1 WHILE i >= 0 DO
SELECT nodeRope.InlineFetch[i] FROM
'\t, '\040 => {rectRope ← nodeRope.Substr[i+1, loc.where-i-1]; EXIT};
ENDCASE;
REPEAT
FINISHED => rectRope ← nodeRope.Substr[0, loc.where];
ENDLOOP;
ris ← IO.RIS[rectRope];
WHILE NOT IO.EndOf[ris] DO
[kind, token, ] ← IO.GetCedarTokenRope[ris];
SELECT kind FROM
tokenSINGLE => {
SELECT token.InlineFetch[0] FROM
ENDCASE => {Scold["\tGrid cover: Unexpected single token."];};
};
ENDCASE => {Scold["\tGrid cover: Unexpected token."];};
ENDLOOP;
};