TreeGrapherViewerImpl.mesa
Copyright Ó 1990, 1991 by Xerox Corporation. All rights reserved.
Michael Plass, July 17, 1990 4:28 pm PDT
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 ~ {
PROC [self: Viewer, context: Imager.Context, whatChanged: REF, clear: BOOL]
RETURNS [quit: BOOL ← FALSE]
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 ~ {
PROC [self: Viewer, op: {query, up, down, thumb}, amount: INTEGER, shift, control: BOOL ← FALSE] RETURNS [top, bottom: INTEGER ← LAST[INTEGER]];
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 ~ {
PROC [self: Viewer, op: {query, left, right, thumb}, amount: INTEGER, shift, control: BOOL ← FALSE] RETURNS [left, right: INTEGER ← LAST[INTEGER]];
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];
};
Rope Nodes - a simple class
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 ~ {
PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation ← none, new: Val ← NIL];
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;
};
Text Nodes - a slightly fancier class
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 ~ {
PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation ← none, new: Val ← NIL];
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]];
};
TreeFromSExpr
FindFormat: PUBLIC PROC [data: ViewerData, formatName: ATOM] RETURNS [result: TextFormat] ~ {
Inner: RefTab.UpdateAction ~ {
PROC [found: BOOL, val: Val] RETURNS [op: UpdateOperation ← none, new: Val ← NIL];
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]]];
};
Input
TGNotify: ViewerClasses.NotifyProc ~ {
PROC [self: Viewer, input: LIST OF REF ANY]
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;
};
Commands
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
]];