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]; }; 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 ~ { 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 ~ { 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; }; Format: TEditFormat.FormatProc ~ { Inner: PROC ~ { formattedBox: TiogaImager.Box ฌ GetCDBox[node, lineWidth, nodeStyle]; IF formattedBox # NIL THEN { extents: Imager.Box ฌ formattedBox.bounds; lineInfo.artworkData ฌ formattedBox; lineInfo.xmin ฌ 0; lineInfo.ymin ฌ 0; lineInfo.xmax ฌ Ceiling[extents.xmax-extents.xmin]; lineInfo.ymax ฌ Ceiling[extents.ymax-extents.ymin]; { 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] ~ { 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]; 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] ~ { 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] ~ { 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 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 ~ { 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]; }; 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. ถ 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 Tioga Input Operations: Open/Close CenteredDisplay PROC [viewer: ViewerClasses.Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE]; PROC [viewer: ViewerClasses.Viewer _ NIL] RETURNS [recordAtom: BOOL _ TRUE, quit: BOOL _ FALSE]; Tioga Formatting Procs [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 compensate for Artwork origin 0,0 at bottom left, TiogaImager origin 0,0 at top right calculate position for appropriate formatting if nodeStyle is NIL, then we assume we have to format the node format branch for infinite depth then fix expansion to desired depth stolen unabashedly from TiogaImagerImpl, with simplification due to style.print returns the depth of the centered display Invariant: c = (IF iš œžœžœ8˜kK˜#K˜.˜Kšœ žœžœ ˜3Kšœ žœ,˜;Kšœ žœ!˜0Kšœ žœ"˜2Kšœ žœ"˜2Kšœ žœ#˜4Kšœžœ%˜8Kšœ žœžœ6˜LKšœžœ5˜TK˜—K˜—Kšœžœ2˜:šžœžœž˜K˜Kšžœ žœ˜—šžœžœžœ˜K˜šžœ žœžœ˜K˜2—K˜5K˜6K˜4K˜มK˜—K˜K˜—š œžœKžœ˜„Kšœ žœ#˜4Kšœ žœ0˜@Kšœ žœ#˜4KšœžœH˜^Kšœžœžœ-˜IK™DKšœžœ%˜9Kšœƒžœžœ˜’Kšœžœ˜*KšœLžœžœžœ˜†K˜K˜—š   œžœžœ žœžœžœ˜YK™OKšœžœ ˜K˜hKšžœ˜ K˜K˜—š œžœ0žœ žœ ˜]J™)Kšœžœ ˜Kšœžœ˜šžœ žœ˜Kšœžœ˜ Kš œžœžœžœ žœžœžœžœ˜Sšœžœ ˜Kšœžœžœžœ™5—Kšœ žœ˜Kšœ žœ˜š œžœ˜K˜ Kšžœžœ žœ˜#K˜Kšžœžœžœ žœ˜.K˜—š œžœžœžœ˜#K˜Kšžœžœ˜$K˜ K˜.K˜ šžœžœž˜K˜K˜K˜K˜"K˜%K˜Kšžœžœ˜—K˜K˜—š  œžœžœžœžœ˜*K–O[s1: ROPE, pos1: INT _ 0, s2: ROPE, pos2: INT _ 0, case: BOOL _ TRUE]šœžœ˜Kšžœžœ"žœ˜DK˜—Kšžœžœžœ žœ˜.šžœ ž˜Kšœ ก ˜K˜ šžœžœž˜K˜$Kšžœ˜!—Kšžœ˜—K˜—šžœ˜K˜K˜—K˜K˜—š   œžœžœžœžœ˜1Kšžœžœžœžœ ˜$Kšžœžœžœ˜Kšžœžœ˜K˜K˜—š  œžœžœžœžœ˜*K˜Kšžœžœ ˜K˜K˜—š  œžœžœžœžœ˜,K˜Kšžœžœ ˜K˜K˜—š œ˜$K˜K˜4K˜Kšœžœ˜K˜K˜—š  œ"˜.K˜1K˜K˜K˜—š  œ!˜,Kšžœ?˜EK˜K˜—š œ˜ Kšข9™9š œžœ˜Kšœžœ˜4K˜K˜FK˜—K˜K˜K˜——™šœ1žœ ˜TK˜K˜K˜ K˜K˜K˜K˜K˜—K˜2K˜WK˜VK˜VKšœUžœ˜[K–E[name: ATOM, proc: TEditInput.CommandProc, before: BOOL _ TRUE]šœWžœ˜]—K˜Kšžœ˜—…—2ชFฆ