ArtworkCenteredImpl.mesa
Copyright Ó 1987, 1989, 1992 by Xerox Corporation. All rights reserved.
Rick Beach, July 15, 1987 10:06:34 pm PDT
Michael Plass, March 25, 1992 10:48 am PST
DIRECTORY
Atom USING [GetPName],
Convert,
EditSpan USING [CannotDoEdit, Move],
Imager,
IO,
MessageWindow,
NodeProps,
NodeStyle,
NodeStyleOps,
ProcessProps,
TiogaIO USING [FromRope, ToRope],
Real,
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;
ArtworkCenteredImpl: CEDAR PROGRAM
IMPORTS Atom, Convert, EditSpan, Imager, IO, MessageWindow, NodeProps, NodeStyle, NodeStyleOps, ProcessProps, TiogaIO, Real, Rope, RuntimeError, Scaled, TEditFormat, TEditInput, TEditInputOps, TEditSelection, TextEdit, TextNode, TiogaImager
~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
Cant: ERROR [cantMessage: ROPE ¬ NIL] ~ CODE;
IsCenteredDisplay: PROC [node: TextNode.Ref] RETURNS [BOOL] ~ {
prop: REF ~ NodeProps.GetProp[node, $Artwork];
WITH prop SELECT FROM
a: ATOM => RETURN [a = $CenteredDisplay];
r: ROPE => RETURN [Rope.Equal[r, "CenteredDisplay"]];
ENDCASE => RETURN [FALSE];
};
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: BOOLTRUE, quit: BOOLFALSE];
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 ¬ TiogaIO.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: BOOLTRUE, quit: BOOLFALSE];
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 ¬ TiogaIO.ToRope[displayNode];
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[displayNode.rope];
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.GetReal[$firstIndent];
leftIndent: REAL ~ nodeStyle.GetReal[$leftIndent] + extraIndent;
rightIndent: REAL ~ nodeStyle.GetReal[$rightIndent];
clippedLineWidth: REAL ~ PositiveMin[nodeStyle.GetReal[$lineLength], Scaled.Float[lineWidth]];
trimmedLineWidth: REAL ~ MAX[clippedLineWidth-leftIndent-rightIndent, 0];
SELECT nodeStyle.GetLineFormatting[] FROM
FlushLeft => {lineInfo.xOffset ¬ Scaled.FromReal[leftIndent];};
FlushRight => {lineInfo.xOffset ¬ Scaled.FromReal[MAX[Scaled.Float[lineWidth]-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 ¬ TiogaIO.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.GetInt[$columns], 1];
columnGap: REAL ~ GetStyleParam[nodeStyle, $columnGap, 36];
pageWidth: REAL ~ nodeStyle.GetReal[$pageWidth];
pageLength: REAL ~ nodeStyle.GetReal[$pageLength];
leftMargin: REAL ~ nodeStyle.GetReal[$leftMargin];
rightMargin: REAL ~ nodeStyle.GetReal[$rightMargin];
bindingMargin: REAL ~ nodeStyle.GetReal[$bindingMargin];
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.GetName[$style]];
box ¬ FormatBranchToBox[branch, lineWidth, nodeStyle];
NodeProps.PutProp[cdNode, $CenteredDisplayBox, box];
NodeProps.PutProp[cdNode, $Postfix, NodeProps.DoSpecs[$Postfix, Rope.Concat[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.GetReal[$firstIndent];
leftIndent: REAL ~ nodeStyle.GetReal[$leftIndent] + extraIndent;
rightIndent: REAL ~ nodeStyle.GetReal[$rightIndent];
clippedLineWidth: REAL ~ PositiveMin[nodeStyle.GetReal[$lineLength], Scaled.Float[lineWidth]];
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, 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: style.kind];
RETURN [val]
};
GetDepth: PROC [node: TextNode.Ref, nodeStyle: NodeStyle.Ref]
RETURNS [depth: REAL ¬ 0.0] ~ {
returns the depth of the centered display
rope: ROPE ~ node.rope;
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+Scaled.Floor[lineInfo.xOffset];
width ¬ lineInfo.xmax;
rightOfLine ¬ FALSE;
};
CharPosition: TEditFormat.CharPositionProc ~ {
x ¬ lineInfo.xmin+Scaled.Floor[lineInfo.xOffset];
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];
END.