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; ArtworkCenteredImpl: CEDAR PROGRAM IMPORTS Atom, Convert, EditSpan, Imager, IO, MessageWindow, NodeProps, NodeStyle, NodeStyleOps, ProcessProps, PutGet, 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 _ 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 ~ { 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; }; 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.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] ~ { 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]; 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] ~ { 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] ~ { 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 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 ~ { 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 by Xerox Corporation. All rights reserved. Rick Beach, July 15, 1987 10:06:34 pm PDT 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šœ œœ˜-Kšœ œ,˜;Kšœ œ˜)Kšœ œ˜+Kšœ œ˜+Kšœ œ˜-Kšœœ˜1Kšœ œœ6˜LKšœœ5˜TKšœ˜—K˜—Jšœœ2˜:šœœ˜Kšœ˜Kšœ œ˜—šœœœ˜K˜šœ œœ˜Kšœ2˜2—K˜2Kšœ6˜6Kšœ4˜4Kšœพ˜พKšœ˜—K˜K˜—šŸœœKœ˜„Jšœ œ˜-Jšœ œ)˜9Jšœ œ˜-Kšœœ9˜OKšœœœ-˜IKšœD™DKšœœ%˜9Kšœˆœ œœ˜ฆKšœœ˜*KšœLœœœ˜†K˜K˜—š Ÿ œœœ œœœ˜YKšœO™OKšœœ ˜Kšœ\œ œœ ˜ƒKšœ˜ Kšœ˜K˜—šŸœœ0œ œ ˜]Jšœ)™)Jšœœ˜$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šœ˜—šœ˜Jšœ˜Kšœ˜—Jšœ˜J˜—š Ÿ œœœœœ˜1Kšœœœœ ˜$Kšœœœ˜Kšœœ˜Kšœ˜K˜—š Ÿœœœœœ˜*Kšœ˜Kšœœ ˜Kšœ˜K˜—š Ÿœœœœœ˜,Kšœ˜Kšœœ ˜Kšœ˜K˜—šŸœ˜$Jšœ˜Jšœ2˜2Jšœ˜Jšœœ˜Jšœ˜J˜—šŸ œ"˜.Jšœ/˜/Jšœ˜Jšœ˜J˜—šŸ œ!˜,Jšœ?˜EJšœ˜J˜—šŸœ˜ Kšก9™9šŸœœ˜Kšœœ˜4K˜K˜FK˜—K˜K˜K˜——™šœ1œ ˜TJšœ˜J˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜—Kšœ2˜2KšœW˜WKšœV˜VKšœV˜VKšœUœ˜[K–E[name: ATOM, proc: TEditInput.CommandProc, before: BOOL _ TRUE]šœWœ˜]—K˜Kšœ˜—…—2nH