DIRECTORY TIPUser, Cursors, RefTab, ImagerColor, Real, Atom, Convert, Rope, ImagerFont, ImagerBox, Spy, ImagerBackdoor, Commander, CommanderOps, IO, Imager, TreeGrapher, TreeGrapherViewer, ViewerClasses, ViewerOps, Vector2; TreeGrapherViewerImpl: CEDAR PROGRAM IMPORTS Rope, TIPUser, Cursors, RefTab, ImagerColor, Real, Atom, Convert, ImagerFont, ImagerBox, Spy, ImagerBackdoor, Commander, CommanderOps, IO, Imager, TreeGrapher, ViewerOps, Vector2 EXPORTS TreeGrapherViewer ~ BEGIN OPEN TreeGrapherViewer; ROPE: TYPE ~ Rope.ROPE; Context: TYPE ~ Imager.Context; Node: TYPE ~ TreeGrapher.Node; Direction: TYPE ~ TreeGrapher.Direction; Box: TYPE ~ Imager.Box; Color: TYPE ~ Imager.Color; Font: TYPE ~ Imager.Font; VEC: TYPE ~ Imager.VEC; BoxesOverlap: PROC [a, b: Box, v: VEC ¬ [0.0, 0.0]] RETURNS [BOOL] ~ { xmin: REAL ~ MAX[a.xmin, b.xmin+v.x]; ymin: REAL ~ MAX[a.ymin, b.ymin+v.y]; xmax: REAL ~ MIN[a.xmax, b.xmax+v.x]; ymax: REAL ~ MIN[a.ymax, b.ymax+v.y]; RETURN [xmin <= xmax AND ymin <= ymax] }; PaintNode: PROC [context: Context, bounds: Box, node: Node, data: ViewerData] RETURNS [BOOL] ~ { IF NOT BoxesOverlap[bounds, node.layout.treeBox] THEN RETURN [FALSE] ELSE { des: Direction ~ data.lp.descendantDirection; pred: Direction ~ SELECT des FROM $N => $S, $E => $W, $S => $N, $W => $E, ENDCASE => $N; pap: VEC ~ Vector2.Add[node.class.attachmentPoint[node, des], node.layout.origin]; papb: Box ~ [xmin: pap.x, ymin: pap.y, xmax: pap.x, ymax: pap.y]; FOR tail: LIST OF Node ¬ node.children, tail.rest UNTIL tail = NIL DO cap: VEC ~ Vector2.Add[tail.first.class.attachmentPoint[tail.first, pred], tail.first.layout.origin]; IF BoxesOverlap[bounds, ImagerBox.BoundPoint[papb, cap]] THEN { Imager.MaskVector[context, pap, cap]; }; ENDLOOP; IF BoxesOverlap[bounds, node.layout.bounds, node.layout.origin] THEN { WITH node.class.classData SELECT FROM classData: ClassData => { classData.paint[node, context] }; ENDCASE; }; RETURN [TRUE] }; }; PreOrder: PROC [node: Node, each: PROC [Node] RETURNS [BOOL]] = { IF NOT each[node] THEN RETURN; FOR tail: LIST OF Node ¬ node.children, tail.rest UNTIL tail = NIL DO PreOrder[tail.first, each]; ENDLOOP; }; TGPaint: ViewerClasses.PaintProc ~ { ENABLE UNCAUGHT => { Spy.SampleMyStack[]; ViewerOps.BlinkDisplay[]; CONTINUE }; data: ViewerData ~ NARROW[self.data]; bb: Box; Each: PROC [node: Node] RETURNS [BOOL] ~ { RETURN [PaintNode[context, bb, node, data]]; }; IF NOT clear THEN { Imager.SetGray[context, 0]; Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]]; Imager.SetGray[context, 1]; }; IF data.tree # NIL THEN { Imager.TranslateT[context, data.origin]; bb ¬ ImagerBox.BoxFromRectangle[ImagerBackdoor.GetBounds[context]]; PreOrder[data.tree, Each]; }; }; Percent: PROC [r: REAL] RETURNS [INTEGER] ~ { RETURN [Real.Round[MIN[MAX[r, 0], 1]*100]]; }; TGVScroll: ViewerClasses.ScrollProc ~ { ENABLE UNCAUGHT => { Spy.SampleMyStack[]; ViewerOps.BlinkDisplay[]; CONTINUE }; data: ViewerData ~ NARROW[self.data]; SELECT op FROM query => { dataSize: REAL ¬ data.tree.layout.treeBox.ymax-data.tree.layout.treeBox.ymin; viewerSize: REAL ¬ self.ch; topPos: REAL ¬ data.tree.layout.treeBox.ymax+data.origin.y; bottomPos: REAL ¬ topPos-dataSize; top ¬ Percent[(topPos-viewerSize)/MAX[dataSize, 1]]; bottom ¬ Percent[1.0+bottomPos/MAX[dataSize, 1]]; RETURN; }; up => { data.origin.y ¬ data.origin.y + amount; }; down => { data.origin.y ¬ data.origin.y - amount; }; thumb => { dataSize: REAL ¬ data.tree.layout.treeBox.ymax-data.tree.layout.treeBox.ymin; viewerSize: REAL ¬ self.ch; data.origin.y ¬ (amount/100.0)*dataSize+viewerSize-data.tree.layout.treeBox.ymax; }; ENDCASE; data.origin.y ¬ MAX[MIN[data.origin.y, self.ch-data.tree.layout.treeBox.ymin-1.0], 1.0-data.tree.layout.treeBox.ymax]; ViewerOps.PaintViewer[self, client]; }; TGHScroll: ViewerClasses.HScrollProc ~ { ENABLE UNCAUGHT => { Spy.SampleMyStack[]; ViewerOps.BlinkDisplay[]; CONTINUE }; data: ViewerData ~ NARROW[self.data]; SELECT op FROM query => { dataSize: REAL ¬ data.tree.layout.treeBox.xmax-data.tree.layout.treeBox.xmin; viewerSize: REAL ¬ self.cw; leftPos: REAL ¬ data.tree.layout.treeBox.xmin+data.origin.x; rightPos: REAL ¬ leftPos+dataSize; left ¬ Percent[-leftPos/MAX[dataSize, 1]]; right ¬ Percent[1.0-(rightPos-viewerSize)/MAX[dataSize, 1]]; RETURN; }; left => { data.origin.x ¬ data.origin.x - amount; }; right => { data.origin.x ¬ data.origin.x + amount; }; thumb => { dataSize: REAL ¬ data.tree.layout.treeBox.xmax-data.tree.layout.treeBox.xmin; data.origin.x ¬ -data.tree.layout.treeBox.xmin-(amount/100.0)*dataSize; }; ENDCASE; data.origin.x ¬ MAX[MIN[data.origin.x, self.cw-data.tree.layout.treeBox.xmin-1.0], 1.0-data.tree.layout.treeBox.xmax]; ViewerOps.PaintViewer[self, client]; }; NodeFromRope: PUBLIC PROC [label: ROPE, textFormat: TextFormat] RETURNS [Node] ~ { node: Node ~ NEW[TreeGrapher.NodeRep ¬ [ data: label, class: RopeClassFromTextFormat[textFormat], children: NIL ]]; RETURN [node] }; ropeClassCache: RefTab.Ref ~ RefTab.Create[]; RopeClassFromTextFormat: PROC [format: TextFormat] RETURNS [result: REF TreeGrapher.ClassRep] ~ { CheckCache: RefTab.UpdateAction ~ { IF found THEN { result ¬ NARROW[val] } ELSE { op ¬ store; new ¬ result ¬ NEW[TreeGrapher.ClassRep ¬ [ classData: NEW[ClassDataRep ¬ [more: format, paint: RopePaint, click: NIL]], bounds: RopeBounds, attachmentPoint: TreeGrapher.DefaultAttachmentPoint ]]; }; }; RefTab.Update[ropeClassCache, format, CheckCache]; }; RopeBounds: PROC [self: Node] RETURNS [Box] ~ { classData: ClassData ~ NARROW[self.class.classData]; format: TextFormat ~ NARROW[classData.more]; font: Font ~ format.font.first; label: ROPE ~ WITH self.data SELECT FROM rope: ROPE => rope, atom: ATOM => Atom.GetPName[atom], ENDCASE => "??"; bb: Box ¬ ImagerBox.BoundingBox[ ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[font, label]], ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[font, "Bg"]]]; bb.xmin ¬ bb.xmin - format.bearoff.x; bb.xmax ¬ bb.xmax + format.bearoff.x; bb.ymin ¬ bb.ymin - format.bearoff.y; bb.ymax ¬ bb.ymax + format.bearoff.y; RETURN [bb]; }; RopePaint: PROC [node: Node, context: Context] ~ { classData: ClassData ~ NARROW[node.class.classData]; format: TextFormat ~ NARROW[classData.more]; b: Box ¬ node.layout.bounds; b.xmin ¬ b.xmin + node.layout.origin.x; b.xmax ¬ b.xmax + node.layout.origin.x; b.ymin ¬ b.ymin + node.layout.origin.y; b.ymax ¬ b.ymax + node.layout.origin.y; Imager.SetGray[context, 0]; Imager.MaskBox[context, b]; Imager.SetGray[context, 1]; Imager.MaskVector[context, [b.xmin, b.ymin], [b.xmin, b.ymax]]; Imager.MaskVector[context, [b.xmax, b.ymin], [b.xmax, b.ymax]]; Imager.MaskVector[context, [b.xmin, b.ymin], [b.xmax, b.ymin]]; Imager.MaskVector[context, [b.xmax, b.ymax], [b.xmin, b.ymax]]; Imager.SetFont[context, format.font.first]; Imager.SetXY[context, node.layout.origin]; WITH node.data SELECT FROM rope: ROPE => Imager.ShowRope[context, rope]; atom: ATOM => Imager.ShowRope[context, Atom.GetPName[atom]]; ENDCASE => NULL; }; NodeFromText: PUBLIC PROC [lines: LIST OF ROPE, textFormat: TextFormat, fillSizes: LIST OF REAL ¬ NIL, clientData: REF ¬ NIL, click: ViewerClasses.ClickProc ¬ NIL] RETURNS [Node] ~ { node: Node ~ NEW[TreeGrapher.NodeRep ¬ [ data: NEW[TextDataRep ¬ [lines: lines, clientData: clientData, fillSizes: fillSizes]], class: TextClassFromTextFormat[textFormat, click], children: NIL ]]; RETURN [node] }; textClassCache: RefTab.Ref ~ RefTab.Create[]; TextClassFromTextFormat: PROC [format: TextFormat, click: ViewerClasses.ClickProc] RETURNS [result: REF TreeGrapher.ClassRep] ~ { CheckCache: RefTab.UpdateAction ~ { IF found AND NARROW[(result ¬ NARROW[val]).classData, ClassData].click = click THEN { result ¬ result } ELSE { op ¬ store; new ¬ result ¬ NEW[TreeGrapher.ClassRep ¬ [ classData: NEW[ClassDataRep ¬ [more: format, paint: TextPaint, click: click]], bounds: TextBounds, attachmentPoint: TreeGrapher.DefaultAttachmentPoint ]]; }; }; RefTab.Update[textClassCache, format, CheckCache]; }; TextBounds: PROC [self: Node] RETURNS [Box] ~ { classData: ClassData ~ NARROW[self.class.classData]; format: TextFormat ~ NARROW[classData.more]; fonts: LIST OF Font ¬ format.font; text: TextData ~ NARROW[self.data]; bb: Box ¬ ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[format.font.first, "Bg"]]; y: REAL ¬ 0.0; fillSize: REAL ¬ 0.0; FOR tail: LIST OF REAL ¬ text.fillSizes, tail.rest UNTIL tail = NIL DO fillSize ¬ fillSize + tail.first; ENDLOOP; bb.xmax ¬ bb.xmin; FOR tail: LIST OF ROPE ¬ text.lines, tail.rest UNTIL tail = NIL DO b: Box ¬ ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[fonts.first, tail.first]]; b.ymin ¬ b.ymin + y; b.ymax ¬ b.ymax + y; bb ¬ ImagerBox.BoundingBox[bb, b]; y ¬ y - format.skip; IF fonts.rest # NIL THEN fonts ¬ fonts.rest; ENDLOOP; bb.xmin ¬ bb.xmin - format.bearoff.x; bb.xmax ¬ bb.xmax + format.bearoff.x; bb.ymin ¬ bb.ymin - format.bearoff.y; bb.ymax ¬ bb.ymax + format.bearoff.y; bb.ymin ¬ MIN[bb.ymin, bb.ymax - fillSize]; RETURN [bb]; }; TextPaint: PROC [node: Node, context: Context] ~ { classData: ClassData ~ NARROW[node.class.classData]; format: TextFormat ~ NARROW[classData.more]; fonts: LIST OF Font ¬ format.font; text: TextData ~ NARROW[node.data]; b: Box ¬ node.layout.bounds; y: REAL ¬ 0.0; fillColors: LIST OF Color ¬ format.fillColors; b.xmin ¬ b.xmin + node.layout.origin.x; b.xmax ¬ b.xmax + node.layout.origin.x; b.ymin ¬ b.ymin + node.layout.origin.y; FOR tail: LIST OF REAL ¬ text.fillSizes, tail.rest UNTIL tail = NIL DO b.ymax ¬ b.ymin + tail.first; IF fillColors = NIL THEN { Imager.SetGray[context, 0] } ELSE { Imager.SetColor[context, fillColors.first]; fillColors ¬ fillColors.rest; }; Imager.MaskBox[context, b]; b.ymin ¬ b.ymax; ENDLOOP; b.ymax ¬ node.layout.bounds.ymax + node.layout.origin.y; IF b.ymax > b.ymin THEN { IF fillColors = NIL THEN Imager.SetGray[context, 0] ELSE Imager.SetColor[context, fillColors.first]; Imager.MaskBox[context, b]; }; b.ymin ¬ node.layout.bounds.ymin + node.layout.origin.y; Imager.SetColor[context, format.textColor]; Imager.SetFont[context, fonts.first]; FOR tail: LIST OF ROPE ¬ text.lines, tail.rest UNTIL tail = NIL DO Imager.SetXY[context, [node.layout.origin.x, node.layout.origin.y+y]]; Imager.ShowRope[context, tail.first]; y ¬ y - format.skip; IF fonts.rest # NIL AND tail # NIL THEN { fonts ¬ fonts.rest; Imager.SetFont[context, fonts.first]; }; ENDLOOP; Imager.SetGray[context, 1]; Imager.MaskVector[context, [b.xmin, b.ymin], [b.xmin, b.ymax]]; Imager.MaskVector[context, [b.xmax, b.ymin], [b.xmax, b.ymax]]; Imager.MaskVector[context, [b.xmin, b.ymin], [b.xmax, b.ymin]]; Imager.MaskVector[context, [b.xmax, b.ymax], [b.xmin, b.ymax]]; }; FindFormat: PUBLIC PROC [data: ViewerData, formatName: ATOM] RETURNS [result: TextFormat] ~ { Inner: RefTab.UpdateAction ~ { IF found THEN { result ¬ NARROW[val] } ELSE { op ¬ store; new ¬ result ¬ NEW[TextFormatRep ¬ [ font: LIST[Imager.FindFontScaled["xerox/tiogafonts/tioga10", 1]], textColor: Imager.black ]]; }; }; RefTab.Update[data.style, formatName, Inner]; }; TreeFromSExpr: PUBLIC PROC [sexpr: REF, data: ViewerData] RETURNS [Node] ~ { WITH sexpr SELECT FROM lora: LIST OF REF => { node: Node ¬ NodeFromRef[lora.first, data]; head: LIST OF Node ~ LIST[NIL]; last: LIST OF Node ¬ head; FOR tail: LIST OF REF ¬ lora.rest, tail.rest UNTIL tail = NIL DO last ¬ last.rest ¬ LIST[TreeFromSExpr[tail.first, data]]; ENDLOOP; node.children ¬ head.rest; RETURN [node] }; ENDCASE => RETURN[TreeFromSExpr[LIST[sexpr], data]]; }; NodeFromRef: PUBLIC PROC [ref: REF, data: ViewerData] RETURNS [Node] ~ { WITH ref SELECT FROM rope: ROPE => RETURN [NodeFromRope[rope, FindFormat[data, $plain]]]; atom: ATOM => RETURN [NodeFromRope[Atom.GetPName[atom], FindFormat[data, $plain]]]; refInt: REF INT => RETURN [NodeFromRope[Convert.RopeFromInt[refInt­], FindFormat[data, $plain]]]; lora: LIST OF REF => { head: LIST OF ROPE ~ LIST[NIL]; last: LIST OF ROPE ¬ head; fillSizes: LIST OF REAL ¬ NIL; format: ATOM ¬ $default; FOR tail: LIST OF REF ¬ lora, tail.rest UNTIL tail = NIL DO WITH tail.first SELECT FROM rope: ROPE => last ¬ last.rest ¬ LIST[rope]; atom: ATOM => last ¬ last.rest ¬ LIST[Atom.GetPName[atom]]; prop: LIST OF REF => { IF prop.rest # NIL THEN SELECT prop.first FROM $h => { last: LIST OF REAL ¬ NIL; FOR tail: LIST OF REF ¬ prop.rest, tail.rest UNTIL tail = NIL DO real: REAL ~ ( WITH tail.first SELECT FROM refInt: REF INT => REAL[refInt­], refReal: REF REAL => refReal­, ENDCASE => 0.0); new: LIST OF REAL ~ LIST[real]; IF fillSizes = NIL THEN fillSizes ¬ last ¬ new ELSE last ¬ last.rest ¬ new; ENDLOOP; }; $format => { WITH prop.rest.first SELECT FROM atom: ATOM => format ¬ atom; ENDCASE; }; ENDCASE; }; ENDCASE; ENDLOOP; RETURN [NodeFromText[lines: head.rest, textFormat: FindFormat[data, format], fillSizes: fillSizes, clientData: ref, click: data.click]]; }; ENDCASE => RETURN [NodeFromRope["?", FindFormat[data, $plain]]]; }; TGNotify: ViewerClasses.NotifyProc ~ { ENABLE UNCAUGHT => { Spy.SampleMyStack[]; ViewerOps.BlinkDisplay[]; CONTINUE }; WITH self.data SELECT FROM data: ViewerData => { button: ViewerClasses.MouseButton ¬ red; coords, shift, control, hit, mark: BOOL ¬ FALSE; mouse: TIPUser.TIPScreenCoords; FOR list: LIST OF REF ANY ¬ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => SELECT x FROM $Blue => button ¬ blue; $Control => control ¬ TRUE; $Hit => hit ¬ TRUE; $Mark => mark ¬ TRUE; $Red => button ¬ red; $Shift => shift ¬ TRUE; $Yellow => button ¬ yellow; ENDCASE => NULL; z: TIPUser.TIPScreenCoords => { coords ¬ TRUE; mouse ¬ z }; ENDCASE => ERROR; ENDLOOP; IF coords THEN { node: Node ¬ TreeGrapher.Resolve[data.tree, [mouse.mouseX-data.origin.x, mouse.mouseY-data.origin.y]]; IF node # NIL THEN { classData: ClassData ~ NARROW[node.class.classData]; cursor: ViewerClasses.CursorType ¬ SELECT TRUE FROM hit => hourGlass, mark => SELECT button FROM red => mouseRed, yellow => mouseYellow, blue => mouseBlue ENDCASE => activate, ENDCASE => bullseye; IF self.cursor # cursor THEN { Cursors.SetCursor[self.cursor ¬ cursor]; }; IF hit AND classData.click # NIL THEN { classData.click[parent: self, clientData: node, mouseButton: button, shift: shift, control: control]; }; } ELSE { IF self.cursor # none THEN { self.cursor ¬ none; Cursors.SetCursor[self.class.cursor]; }; }; }; }; ENDCASE; }; NewStyle: PROC RETURNS [RefTab.Ref] ~ { style: RefTab.Ref ~ RefTab.Create[]; Def: PROC [formatName: ATOM, fontName: ROPE, textColor: Color ¬ Imager.black, fillColors: LIST OF Color ¬ NIL] ~ { [] ¬ RefTab.Insert[style, formatName, NEW[TextFormatRep ¬ [ font: LIST[Imager.FindFontScaled[fontName, 1]], textColor: textColor, fillColors: fillColors, propList: NIL ]]]; }; Def[$plain, "xerox/tiogafonts/helvetica10"]; Def[$default, "xerox/tiogafonts/tioga10", Imager.black, LIST[ImagerColor.ColorFromRGB[[0.873,0.68,0.619]], ImagerColor.ColorFromRGB[[1,1,0.8]]]]; RETURN [style] }; NewViewerData: PUBLIC PROC RETURNS [ViewerData] ~ { data: ViewerData ~ NEW[ViewerDataRep ¬ [ tree: NIL, style: NewStyle[], lp: NEW[TreeGrapher.LayoutParametersRep ¬ []], origin: [0, 0] ]]; data.tree ¬ NodeFromRef[Rope.Flatten[" "], data]; TreeGrapher.DoLayout[data.tree, data.lp]; RETURN [data] }; TreeGrapherCommand: Commander.CommandProc ~ { stream: IO.STREAM ~ IO.RIS[cmd.commandLine]; sexpr: REF ~ IO.GetRefAny[stream]; data: ViewerData ~ NewViewerData[]; data.tree ¬ TreeFromSExpr[sexpr, data]; TreeGrapher.DoLayout[data.tree, data.lp]; data.origin ¬ [1.0-data.tree.layout.treeBox.xmin, 1.0-data.tree.layout.treeBox.ymin]; { v: ViewerClasses.Viewer ¬ ViewerOps.CreateViewer[flavor: viewerClass.flavor, info: [name: "Tree", hscrollable: TRUE, data: data]]; CommanderOps.PutProp[cmd, $TreeGrapherViewer, v]; }; }; TreeGrapherRotateCommand: Commander.CommandProc ~ { WITH CommanderOps.GetProp[cmd, $TreeGrapherViewer] SELECT FROM v: ViewerClasses.Viewer => { WITH v.data SELECT FROM data: ViewerData => { d1: Direction ~ data.lp.descendantDirection; d2: Direction ~ data.lp.siblingDirection; data.lp.descendantDirection ¬ SELECT d1 FROM $N => $E, $E => $S, $S => $W, $W => $N, ENDCASE => $N; data.lp.siblingDirection ¬ SELECT d2 FROM $N => $E, $E => $S, $S => $W, $W => $N, ENDCASE => $N; TreeGrapher.DoLayout[data.tree, data.lp]; ViewerOps.PaintViewer[v, all]; RETURN; }; ENDCASE; }; ENDCASE; CommanderOps.Failed["No Viewer!"]; }; TreeGrapherFlipCommand: Commander.CommandProc ~ { WITH CommanderOps.GetProp[cmd, $TreeGrapherViewer] SELECT FROM v: ViewerClasses.Viewer => { WITH v.data SELECT FROM data: ViewerData => { d1: Direction ~ data.lp.siblingDirection; data.lp.siblingDirection ¬ SELECT d1 FROM $N => $S, $E => $W, $S => $N, $W => $E, ENDCASE => $N; TreeGrapher.DoLayout[data.tree, data.lp]; ViewerOps.PaintViewer[v, all]; RETURN; }; ENDCASE; }; ENDCASE; CommanderOps.Failed["No Viewer!"]; }; viewerClass: ViewerClasses.ViewerClass ~ NEW[ViewerClasses.ViewerClassRec ¬ [ flavor: $TreeGrapher, scroll: TGVScroll, hscroll: TGHScroll, notify: TGNotify, tipTable: TIPUser.InstantiateNewTIPTable["TreeGrapher.tip"], paint: TGPaint ]]; ViewerOps.RegisterViewerClass[flavor: viewerClass.flavor, class: viewerClass]; Commander.Register["TreeGrapher", TreeGrapherCommand]; Commander.Register["TreeGrapherRotate", TreeGrapherRotateCommand]; Commander.Register["TreeGrapherFlip", TreeGrapherFlipCommand]; END.... Trash heap: Param: TYPE ~ REF ParamRep; ParamRep: TYPE ~ RECORD [ font: Font, bearoff: VEC, lp: REF LayoutParametersRep ]; defaultTextClass: REF TreeGrapher.ClassRep ~ NEW[TreeGrapher.ClassRep ¬ [ classData: NEW[TextStyleRep ¬ [ font: Imager.FindFontScaled["xerox/tiogafonts/tioga10", 1], ]], bounds: TextBounds, attachmentPoint: TreeGrapher.DefaultAttachmentPoint ]]; ¦ TreeGrapherViewerImpl.mesa Copyright Σ 1990, 1991 by Xerox Corporation. All rights reserved. Michael Plass, July 17, 1990 4:28 pm PDT PROC [self: Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL] RETURNS [quit: BOOL _ FALSE] PROC [self: Viewer, op: {query, up, down, thumb}, amount: INTEGER, shift, control: BOOL _ FALSE] RETURNS [top, bottom: INTEGER _ LAST[INTEGER]]; PROC [self: Viewer, op: {query, left, right, thumb}, amount: INTEGER, shift, control: BOOL _ FALSE] RETURNS [left, right: INTEGER _ LAST[INTEGER]]; Rope Nodes - a simple class PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation _ none, new: Val _ NIL]; Text Nodes - a slightly fancier class PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation _ none, new: Val _ NIL]; TreeFromSExpr PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation _ none, new: Val _ NIL]; Input PROC [self: Viewer, input: LIST OF REF ANY] Commands Κ•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Οeœ7™BKšœ(™(K™—šΟk œˆžœL˜ίK˜K˜—KšΟnœžœž˜$Kšžœˆžœ)˜ΊKšžœ˜šœžœžœ˜K˜Kšžœžœžœ˜Kšœ žœ˜Kšœžœ˜Kšœ žœ˜(Kšœžœ˜Kšœžœ˜Kšœžœ˜šžœžœ žœ˜K˜—š Ÿ œžœžœžœžœ˜FKšœžœžœ˜%Kšœžœžœ˜%Kšœžœžœ˜%Kšœžœžœ˜%Kšžœžœ˜&K˜K˜—šŸ œžœ?žœžœ˜`šžœžœ*˜0Kšžœžœžœ˜šžœ˜Kšœ-˜-Kšœžœžœ)žœ˜XKšœžœJ˜RKšœA˜Aš žœžœžœ!žœžœž˜EKšœžœ]˜ešžœ7žœ˜?K˜%Kšœ˜—Kšžœ˜—šžœ>žœ˜Fšžœžœž˜%Kšœ;˜;Kšžœ˜—Kšœ˜—Kšžœžœ˜ Kšœ˜——K˜—K˜š Ÿœžœžœžœžœ˜AKšžœžœ žœžœ˜š žœžœžœ!žœžœž˜EK˜Kšžœ˜—K˜—K˜šŸœ˜$KšœL™LKšœ™Kšžœžœ5žœ˜OKšœžœ ˜%K˜šŸœžœžœžœ˜*Kšžœ&˜,Kšœ˜—šžœžœžœ˜K˜K˜AK˜K˜—šžœ žœžœ˜K˜(K˜CK˜K˜—K˜K˜—š Ÿœžœžœžœžœ˜-Kšžœ žœžœ˜+K˜K˜—šŸ œ˜'Kšœ™Kšžœžœ5žœ˜OKšœžœ ˜%šžœž˜˜ Kšœ žœ?˜MKšœ žœ ˜Kšœžœ/˜;Kšœ žœ˜"Kšœ"žœ˜4Kšœžœ˜1Kšžœ˜K˜—˜K˜'K˜—˜ K˜'K˜—˜ Kšœ žœ?˜MKšœ žœ ˜K˜QK˜—Kšžœ˜—šœžœžœ˜&K˜+K˜#—K˜$K˜K˜—šŸ œ˜(Kšœ“™“Kšžœžœ5žœ˜OKšœžœ ˜%šžœž˜˜ Kšœ žœ?˜MKšœ žœ ˜Kšœ žœ/˜˜šžœžœž˜˜K˜,K˜)Kšœžœžœ)žœ˜cKšœžœžœ)žœ˜`K˜)K˜Kšžœ˜K˜—Kšžœ˜—K˜—Kšžœ˜—K˜"K˜K˜—šŸœ˜1šžœ/žœž˜>˜šžœžœž˜˜K˜)Kšœžœžœ)žœ˜`K˜)K˜Kšžœ˜K˜—Kšžœ˜—K˜—Kšžœ˜—K˜"K˜K˜—šœ)žœ!˜MK˜K˜K˜K˜Kšœ<˜—K˜Kšžœ˜K˜˜ Kšœžœžœ ˜šœ žœžœ˜K˜ Kšœ žœ˜ Kšœžœ˜K˜K˜—šœžœžœ˜Išœ žœ˜K˜;K˜K˜—K˜K˜3K˜K˜——K˜—…—E]?