DIRECTORY
Atom USING [GetPName],
Convert,
EditSpan USING [CannotDoEdit, Move],
Imager,
IO,
MessageWindow,
NodeProps,
NodeStyle,
NodeStyleOps,
ProcessProps,
PutGet USING [FromRope, ToRope],
Rope,
RuntimeError USING [UNCAUGHT],
Scaled,
TEditDocument USING [Selection],
TEditFormat,
TEditInput USING [CommandProc, currentEvent, Register],
TEditInputOps,
TEditSelection USING [Alloc, Copy, Free, GrowSelection, MakeSelection, pSel],
TextEdit,
TextNode,
TextLooks USING [allLooks, noLooks],
TiogaImager;
Tioga Input Operations: Open/Close CenteredDisplay
MakeBranchSelection:
PROC ~ {
tSel: TEditDocument.Selection;
IF TEditSelection.pSel=NIL OR TEditSelection.pSel.viewer=NIL THEN RETURN;
WHILE TEditSelection.pSel.granularity # branch
DO
TEditSelection.GrowSelection[];
ENDLOOP;
tSel ← TEditSelection.Alloc[];
TEditSelection.Copy[source: TEditSelection.pSel, dest: tSel];
tSel.pendingDelete ← TRUE;
tSel.granularity ← char; -- necessary for inserting text without destroying node properties!
tSel.insertion ← after;
TEditSelection.MakeSelection[new: tSel];
TEditSelection.Free[tSel]
};
DoWithLocks:
PROC [proc:
PROC[root: TextNode.Ref, tSel: TEditDocument.Selection]] ~ {
error: {none, cant, uncaught} ← none;
errorMessage: ROPE ← NIL;
TEditInputOps.CallWithLocks[proc, write !
Cant => {error ← cant; errorMessage ← cantMessage; CONTINUE};
RuntimeError.UNCAUGHT => {error ← uncaught; CONTINUE};
];
SELECT error
FROM
none => NULL;
cant => {
TEditInputOps.EditFailed[errorMessage];
};
uncaught => proc[NIL, TEditSelection.Alloc[]]; -- do without lock for easy debug
ENDCASE => ERROR;
};
OpenCenteredDisplayOp: TEditInput.CommandProc ~ {
PROC [viewer: ViewerClasses.Viewer ← NIL]
RETURNS [recordAtom: BOOL ← TRUE, quit: BOOL ← FALSE];
OpenCenteredDisplay:
PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] ~ {
displayNode: TextNode.Ref;
MakeBranchSelection[];
displayNode ← TEditSelection.pSel.start.pos.node;
IF displayNode =
NIL
OR
NOT IsCenteredDisplay[displayNode]
THEN
Cant["Not a proper CenteredDisplay node (artwork property missing)"]
ELSE {
displayRope: ROPE ← GetCDRope[displayNode];
IF
NOT Rope.Match["Artwork=CenteredDisplay*", displayRope]
THEN
Cant["Not a proper CenteredDisplay node (begin with Artwork=CenteredDisplay)"]
ELSE {
branch: TextNode.Ref ← PutGet.FromRope[rope: displayRope];
branchSpan: TextNode.Span ← TextNode.MakeNodeSpan[TextNode.FirstChild[branch], TextNode.LastWithin[branch]];
result: TextNode.Span ← EditSpan.Move[destRoot: TextNode.Root[displayNode], sourceRoot: branch, dest: TextNode.MakeNodeLoc[displayNode], source: branchSpan, where: after, nesting: 1, event: TEditInput.currentEvent !
EditSpan.CannotDoEdit => Cant[]];
UnCacheCD[displayNode];
tSel.pendingDelete ← FALSE;
tSel.start.pos ← [displayNode, 0];
tSel.end.pos ← [result.end.node, MAX[TextEdit.Size[result.end.node]-1, 0]];
tSel.granularity ← branch;
TEditSelection.MakeSelection[new: tSel, selection: primary];
};
};
};
DoWithLocks[OpenCenteredDisplay];
quit ← TRUE;
};
CloseCenteredDisplayOp: TEditInput.CommandProc ~ {
PROC [viewer: ViewerClasses.Viewer ← NIL]
RETURNS [recordAtom: BOOL ← TRUE, quit: BOOL ← FALSE];
CloseCenteredDisplay:
PROC [root: TextNode.Ref, tSel: TEditDocument.Selection] ~ {
displayNode: TextNode.Ref;
displayRope: ROPE;
MakeBranchSelection[];
displayNode ← TEditSelection.pSel.start.pos.node;
IF displayNode = TEditSelection.pSel.end.pos.node
THEN
Cant["Please select a branch"];
IF NodeProps.GetProp[displayNode, $Artwork] #
NIL
THEN
Cant["Already an Artwork node (of some kind)."];
displayRope ← PutGet.ToRope[node: displayNode].output;
IF
NOT Rope.Match["Artwork=CenteredDisplay*", displayRope]
THEN
Cant["Not a proper CenteredDisplay node (begin with Artwork=CenteredDisplay)"];
TEditInputOps.ChangeCaretLooks[add: TextLooks.noLooks, remove: TextLooks.allLooks];
TEditInputOps.InsertRope[TextNode.NodeRope[displayNode]];
UnCacheCD[displayNode];
NodeProps.PutProp[displayNode, $Artwork, Atom.GetPName[$CenteredDisplay]];
NodeProps.PutProp[displayNode, $CenteredDisplayRope, displayRope];
[] ← GetCDBox[displayNode];
};
DoWithLocks[CloseCenteredDisplay];
quit ← TRUE;
};
Tioga Formatting Procs
Format: TEditFormat.FormatProc ~ {
[lineInfo: TEditFormat.LineInfo, node: TextEdit.RefTextNode, startOffset: TextEdit.Offset, nodeStyle: NodeStyle.Ref, lineWidth: Scaled.Value, doLigsAndKern: BOOLEAN ← FALSE, kind: NodeStyleOps.OfStyle ← screen]
lineWidth is in points
Inner:
PROC ~ {
formattedBox: TiogaImager.Box ← GetCDBox[node, lineWidth, nodeStyle];
IF formattedBox #
NIL
THEN {
extents: Imager.Box ← formattedBox.bounds;
lineInfo.artworkData ← formattedBox;
compensate for Artwork origin 0,0 at bottom left, TiogaImager origin 0,0 at top right
lineInfo.xmin ← 0;
lineInfo.ymin ← 0;
lineInfo.xmax ← Ceiling[extents.xmax-extents.xmin];
lineInfo.ymax ← Ceiling[extents.ymax-extents.ymin];
{
calculate position for appropriate formatting
extraIndent: REAL ~ nodeStyle.GetFirstIndent;
leftIndent: REAL ~ nodeStyle.GetLeftIndent + extraIndent;
rightIndent: REAL ~ nodeStyle.GetRightIndent;
clippedLineWidth: REAL ~ PositiveMin[nodeStyle.GetLineLength, lineWidth.Float];
trimmedLineWidth: REAL ~ MAX[clippedLineWidth-leftIndent-rightIndent, 0];
SELECT nodeStyle.GetLineFormatting[]
FROM
FlushLeft => {lineInfo.xOffset ← Scaled.FromReal[leftIndent];};
FlushRight => {lineInfo.xOffset ← Scaled.FromReal[MAX[lineWidth.Float-leftIndent-extents.xmax, 0]]};
Centered, Justified => {lineInfo.xOffset ← Scaled.FromReal[leftIndent+(trimmedLineWidth-extents.xmax)/2]};
ENDCASE => ERROR;
};
};
};
lineInfo.startPos ← [node, 0];
lineInfo.nextPos ← [node, TextEdit.Size[node]];
lineInfo.nChars ← TextEdit.Size[node];
DoWithCatch[Inner];
};
DoWithCatch:
PROC [proc:
PROC] ~ {
error: {none, cant, uncaught} ← none;
errorMessage: ROPE ← NIL;
Log:
PROC [msg:
ROPE] ~ {
WITH ProcessProps.GetProp[$StdOut]
SELECT
FROM
errout:
IO.
STREAM => {
IO.PutRope[errout, msg];
IO.PutRope[errout, "\n"];
};
ENDCASE => {
MessageWindow.Append[message: msg, clearFirst: TRUE];
MessageWindow.Blink[];
};
};
proc[ !
Cant => {error ← cant; errorMessage ← cantMessage; CONTINUE};
RuntimeError.UNCAUGHT => {error ← uncaught; CONTINUE};
];
SELECT error
FROM
none => NULL;
cant => {
Log[errorMessage];
};
uncaught => proc[];
ENDCASE => ERROR;
};
pointsPerMeter: REAL ← Imager.pointsPerInch/Imager.metersPerInch;
infiniteDepth: REAL ← LAST[INT];
UnCacheCD:
PROC [node: TextNode.Ref] ~ {
NodeProps.RemProp[node, $Artwork];
NodeProps.RemProp[node, $Postfix];
NodeProps.RemProp[node, $CenteredDisplayRope];
NodeProps.RemProp[node, $CenteredDisplayRef];
NodeProps.RemProp[node, $CenteredDisplayBox];
};
GetCDRope:
PROC [node: TextNode.Ref]
RETURNS [displayRope:
ROPE] ~ {
ref: REF ← NodeProps.GetProp[node, $CenteredDisplayRope];
WITH ref
SELECT
FROM
r: ROPE => displayRope ← r;
ENDCASE => displayRope ← NIL;
};
GetCDRef:
PROC [node: TextNode.Ref, styleName:
ATOM]
RETURNS [branch: TextNode.Ref] ~ {
ref: REF ← NodeProps.GetProp[node, $CenteredDisplayRef];
WITH ref
SELECT
FROM
n: TextNode.Ref => branch ← n;
ENDCASE => branch ← NIL;
IF branch =
NIL
THEN {
displayRope: ROPE ← GetCDRope[node];
IF displayRope.IsEmpty THEN RETURN [NIL]
ELSE {
branch ← PutGet.FromRope[rope: displayRope];
NodeProps.PutProp[branch, $Prefix,
NodeProps.DoSpecs[$Prefix, Rope.Cat["\"", Atom.GetPName[styleName], "\" style"]]];
NodeProps.PutProp[branch, $Format, NodeProps.DoSpecs[$Format, "root"]];
NodeProps.RemProp[branch, $Postfix];
NodeProps.PutProp[node, $CenteredDisplayRef, branch];
};
};
};
GetCDBox:
PROC [cdNode: TextNode.Ref, lineWidth: Scaled.Value ← Scaled.zero,
nodeStyle: NodeStyle.Ref ←
NIL]
RETURNS [box: TiogaImager.Box] ~ {
if nodeStyle is NIL, then we assume we have to format the node
GetStyleAndWidth:
PROC [node: TextNode.Ref]
RETURNS [nodeStyle: NodeStyle.Ref, lineWidth: Scaled.Value] ~ {
nodeStyle ← NodeStyleOps.Create[];
NodeStyleOps.ApplyAll[nodeStyle, node, print];
{
nColumns: INT ~ MAX[nodeStyle.GetColumns, 1];
columnGap: REAL ~ GetStyleParam[nodeStyle, $columnGap, 36];
pageWidth: REAL ~ nodeStyle.GetPageWidth;
pageLength: REAL ~ nodeStyle.GetPageLength;
leftMargin: REAL ~ nodeStyle.GetLeftMargin;
rightMargin: REAL ~ nodeStyle.GetRightMargin;
bindingMargin: REAL ~ nodeStyle.GetBindingMargin;
totalWidth: REAL ~ MAX[pageWidth-leftMargin-rightMargin-bindingMargin, 0.0];
lineWidth ← Scaled.FromReal[MAX[(totalWidth-(nColumns-1)*columnGap)/nColumns, 0.0]];
};
};
ref: REF ← NodeProps.GetProp[cdNode, $CenteredDisplayBox];
WITH ref
SELECT
FROM
b: TiogaImager.Box => box ← b;
ENDCASE => box ← NIL;
IF box =
NIL
THEN {
branch: TextNode.Ref;
IF nodeStyle =
NIL
THEN
[nodeStyle, lineWidth] ← GetStyleAndWidth[cdNode];
branch ← GetCDRef[cdNode, nodeStyle.GetStyleName];
box ← FormatBranchToBox[branch, lineWidth, nodeStyle];
NodeProps.PutProp[cdNode, $CenteredDisplayBox, box];
NodeProps.PutProp[cdNode, $Postfix, NodeProps.DoSpecs[$Postfix, Rope.Cat[Convert.FtoRope[box.bounds.ymax-box.bounds.ymin, 2], " pt topLeading the topLeading topIndent 0 pt bottomLeading"]]];
};
};
FormatBranchToBox:
PROC [branch: TextNode.Ref, lineWidth: Scaled.Value,
nodeStyle: NodeStyle.Ref]
RETURNS [box: TiogaImager.Box] ~ {
extraIndent: REAL ~ nodeStyle.GetFirstIndent;
leftIndent: REAL ~ nodeStyle.GetLeftIndent + extraIndent;
rightIndent: REAL ~ nodeStyle.GetRightIndent;
clippedLineWidth: REAL ~ PositiveMin[nodeStyle.GetLineLength, lineWidth.Float];
trimmedLineWidth: REAL ~ MAX[clippedLineWidth-leftIndent-rightIndent, 0];
format branch for infinite depth then fix expansion to desired depth
inBounds: Imager.VEC ← [trimmedLineWidth, infiniteDepth];
formatted: TiogaImager.FormattedNodes ← TiogaImager.FormatNodes[start: [TextNode.FirstChild[branch], 0], bounds: inBounds, screenStyle: FALSE, filter: NIL, sep: NIL];
depth: REAL ← GetDepth[branch, nodeStyle];
box ← TiogaImager.BoxFromList[list: TiogaImager.UnBox[formatted.box],
yFix: IF depth = infiniteDepth THEN [] ELSE [expansion, depth]];
};
GetStyleParam:
PROC [style: NodeStyle.Ref, param:
ATOM, default:
REAL]
RETURNS [
REAL] ~ {
stolen unabashedly from TiogaImagerImpl, with simplification due to style.print
val: REAL ← default;
val ← NodeStyleOps.GetStyleParam[s: style, name: param, styleName: style.name[style],
kind: IF style.print THEN print ELSE screen];
RETURN [val]
};
GetDepth:
PROC [node: TextNode.Ref, nodeStyle: NodeStyle.Ref]
RETURNS [depth:
REAL ← 0.0] ~ {
returns the depth of the centered display
rope: ROPE ~ TextEdit.GetRope[node];
size: INT ~ Rope.Size[rope];
IF size # 0
THEN {
i: INT ← -1;
Getc: PROC RETURNS [CHAR] ~ {i←i+1; RETURN [IF i<size THEN rope.Fetch[i] ELSE ' ]};
c:
CHAR ← Getc[];
Invariant: c = (IF i<size THEN rope.Fetch[i] ELSE ' )
startTok: INT ← 0;
sizeTok: INT ← 0;
GetTok:
PROC ~ {
startTok ← i;
UNTIL c = ' DO c ← Getc[] ENDLOOP;
sizeTok ← i-startTok;
WHILE c = ' AND i<size DO c ← Getc[] ENDLOOP;
};
GetReal:
PROC
RETURNS [r:
REAL] ~ {
r ← 1.0;
IF c = '- THEN {r ← -r; c ← Getc[]};
GetTok[];
r ← r*Convert.RealFromLiteral[rope, startTok];
GetTok[];
SELECT
TRUE
FROM
Match["mm"] => r ← r*0.001;
Match["cm"] => r ← r*0.01;
Match["in"] => r ← r*0.0254;
Match["pt"] => r ← r*0.0254/72.27;
Match["pc"] => r ← r*12*0.0254/72.27;
Match["bp"] => r ← r*0.0254/72;
ENDCASE => ERROR;
r ← r*pointsPerMeter;
};
Match:
PROC [key:
ROPE]
RETURNS [
BOOL] ~ {
k: INT ~ Rope.Size[key];
RETURN [sizeTok = k AND Rope.Run[rope, startTok, key, 0, FALSE] = k]
};
WHILE c = ' AND i<size DO c ← Getc[] ENDLOOP;
UNTIL i>=size
DO
GetTok[]; -- ignored
GetTok[];
SELECT
TRUE
FROM
Match["depth"] => depth ← GetReal[];
ENDCASE => depth ← infiniteDepth;
ENDLOOP;
}
ELSE {
depth ← infiniteDepth;
};
};
PositiveMin:
PROC [a, b:
REAL]
RETURNS [
REAL] ~ {
IF a < 0 THEN RETURN [MAX[b, 0.01]];
IF b < 0 THEN RETURN [a];
RETURN [MIN[a, b]]
};
Floor:
PROC [r:
REAL]
RETURNS [i:
INT] ~ {
i ← Real.Round[r];
IF i > r THEN i ← i-1;
};
Ceiling:
PROC [r:
REAL]
RETURNS [i:
INT] ~ {
i ← Real.Round[r];
IF i < r THEN i ← i+1;
};
Resolve: TEditFormat.ResolveProc ~ {
loc ← lineInfo.startPos;
xmin ← lineInfo.xmin+lineInfo.xOffset.integerPart;
width ← lineInfo.xmax;
rightOfLine ← FALSE;
};
CharPosition: TEditFormat.CharPositionProc ~ {
x ← lineInfo.xmin+lineInfo.xOffset.integerPart;
width ← lineInfo.xmax;
};
BoundingBox: TEditFormat.BoundingBoxProc ~ {
RETURN [[lineInfo.xmin, lineInfo.ymin, lineInfo.xmax, lineInfo.ymax]]
};
Paint: TEditFormat.PaintProc ~ {
[lineInfo: TEditFormat.LineInfo, context: Imager.Context]
Action:
PROC ~ {
box: TiogaImager.Box ← NARROW[lineInfo.artworkData];
Imager.Trans[context];
TiogaImager.Render[box, context, [box.bounds.xmin, -box.bounds.ymin]];
};
Imager.DoSave[context, Action];
};
Initialization
centeredDisplayClass: TEditFormat.ArtworkClass ~
NEW[TEditFormat.ArtworkClassRep ← [
name: $CenteredDisplay,
format: Format,
paint: Paint,
resolve: Resolve,
charPosition: CharPosition,
boundingBox: BoundingBox
]];
TEditFormat.RegisterArtwork[centeredDisplayClass];
NodeProps.DeclarePropertyAttribute[name: $CenteredDisplayRope, attribute: $ClientOnly];
NodeProps.DeclarePropertyAttribute[name: $CenteredDisplayRef, attribute: $ClientOnly];
NodeProps.DeclarePropertyAttribute[name: $CenteredDisplayBox, attribute: $ClientOnly];
TEditInput.Register[name: $OpenCenteredDisplay, proc: OpenCenteredDisplayOp, before: TRUE];
TEditInput.Register[name: $CloseCenteredDisplay, proc: CloseCenteredDisplayOp, before: TRUE];