AbuttersImpl.Mesa
Eric Nickell April 16, 1986 5:07:31 pm PST
Pier, May 23, 1985 3:05:08 pm PDT
Mike Spreitzer January 9, 1987 9:25:16 pm PST
Eric Nickell, August 28, 1986 5:37:08 am PDT
DIRECTORY Abutters, Atom, Imager, MessageWindow, MJSContainers, Process, Rope, ViewerClasses, ViewerLocks, ViewerOps;
AbuttersImpl: CEDAR MONITOR
LOCKS a USING a: Abutter
IMPORTS Atom, MessageWindow, MJSContainers, Process, Rope, ViewerLocks, ViewerOps
EXPORTS Abutters
INVARIANT
cn.scheduled iff we're planning to consider moving/stretching n.
n.scheduled iff n reachable from a.queue.
n.scheduled if n.predecessor.scheduled.
a.tracked describes shape to which a last layed out.
cn.tracked describes where cn last put.
An Abutter's Viewer LOCKs are never requested inside its Abutter LOCK.
Viewers restrictions:
In a PaintProc, we can't reposition any viewers; we can only paint.
In v's AdjustProc, we can reposition v's children, but can't mess with v.
= {OPEN Abutters;
Abutter: TYPE = REF AbutterRep;
AbutterRep: PUBLIC TYPE = MONITORED RECORD [
class: AbutterClass,
v: Viewer,
tracked: Size ← [],
cr: CompiledRules ← ALL[NIL],
queue: Queue ← ALL[NIL],
sizeSetters: ARRAY Axis OF NodeList ← ALL[NIL],
clientData: REF ANY
];
Axis: TYPE = {horizontal, vertical};
Size: TYPE = RECORD [cw, ch: INTEGER ← 0];
CompiledRules: TYPE = ARRAY Edge OF RootNode;
Queue: TYPE = ARRAY Edge OF NodeList;
NodeList: TYPE = LIST OF Node;
Node: TYPE = REF NodeRep;
ChildNodeList: TYPE = LIST OF ChildNode;
ChildNode: TYPE = REF NodeRep[child];
RootNode: TYPE = REF NodeRep[root];
NodeRep: TYPE = RECORD [
e: Edge,
scheduled, setSize: BOOLFALSE,
spaceAfter: INTEGER ← 0,
successors: ChildNodeList ← NIL,
predecessor: Node ← NIL,
variant: SELECT kind: * FROM
root => [],
child => [
v: Viewer,
spaceBefore: INTEGER,
stretch: BOOL,
tracked: Box ← [],
opposite: ChildNode ← NIL
],
ENDCASE
];
Box: TYPE = RECORD [wx, wy, ww, wh: INTEGER ← 0];
axisKeys: ARRAY Axis OF ATOM = [
horizontal: $horizontalAbutterNode,
vertical: $verticalAbutterNode
];
edgeKeys: ARRAY Edge OF ATOM = [$left, $right, $top, $bottom];
oppositeEdge: ARRAY Edge OF Edge = [
left: right,
right: left,
top: bottom,
bottom: top];
esgn: ARRAY Edge OF [-1 .. 1] = [
left: 1,
right: -1,
top: 1,
bottom: -1];
Next: ARRAY Edge OF Edge;
edgeAxis: ARRAY Edge OF Axis = [
left: horizontal,
right: horizontal,
top: vertical,
bottom: vertical];
classProp: ATOM ← Atom.MakeAtom["Mike Spreitzer @ April 24, 1986 10:29:33 pm PST"];
RegisterClass: PUBLIC PROC [viewerFlavor: ATOM, class: AbutterClassPrivate] RETURNS [same: ATOM] = {
mjsClass: MJSContainers.MJSContainerClass = NEW [MJSContainers.MJSContainerClassRep ← [
paint: Paint,
destroy: class.destroy,
copy: class.copy,
set: class.set,
get: class.get,
init: Init,
save: class.save,
caption: class.caption,
adjust: NoteSizeChanged,
childAdjust: NoteAbutterChildSize,
menu: class.menu,
icon: class.icon]];
MJSContainers.RegisterClass[same ← viewerFlavor, mjsClass];
Atom.PutProp[atom: viewerFlavor, prop: classProp, val: NEW [AbutterClassPrivate ← class]];
};
GetClass: PUBLIC PROC [viewerFlavor: ATOM] RETURNS [class: AbutterClass] = {
class ← NARROW[Atom.GetProp[viewerFlavor, classProp]];
};
vanilla: PUBLIC ATOM ← RegisterClass[
$VanillaAbutter,
[save: Save]];
Create: PUBLIC PROC [viewerFlavor: ATOM, info: ViewerClasses.ViewerRec ← [], paint: BOOLTRUE] RETURNS [abutter: Abutter] = {
realFlavor: ATOM = IF viewerFlavor # NIL THEN viewerFlavor ELSE vanilla;
class: AbutterClass = GetClass[realFlavor];
info.data ← abutter ← NEW [AbutterRep ← [class: class, clientData: info.data]];
abutter.cr[left] ← NEW [NodeRep.root ← [e: left, variant: root[] ]];
abutter.cr[right] ← NEW [NodeRep.root ← [e: right, variant: root[] ]];
abutter.cr[top] ← NEW [NodeRep.root ← [e: top, variant: root[] ]];
abutter.cr[bottom] ← NEW [NodeRep.root ← [e: bottom, variant: root[] ]];
IF MJSContainers.Create[realFlavor, info, paint] # abutter.v THEN ERROR;
};
Init: PROC [self: Viewer] --ViewerClasses.InitProc-- = {
a: Abutter = NARROW[MJSContainers.GetClientData[self]];
a.v ← self;
IF a.class.init # NIL THEN a.class.init[self];
};
Save: PROC [self: Viewer, force: BOOLFALSE] --ViewerOps.SaveProc-- = {
a: Abutter = NARROW[MJSContainers.GetClientData[self]];
FOR v: Viewer ← self.child, v.sibling UNTIL v = NIL DO
IF force
THEN -- Copied from part of ViewerOpsImplB.SaveAllEdits-- {
IF (v.newVersion OR v.newFile) AND v.class.save # NIL THEN
v.class.save[v, force ! ANY => CONTINUE];
v.newVersion ← v.newFile ← FALSE;
IF v.icon=dirtyDocument THEN v.icon ← document;
IF v.link#NIL THEN FOR t: Viewer ← v.link, t.link UNTIL t=v DO
t.newVersion ← t.newFile ← FALSE;
ENDLOOP;
}
ELSE {
ViewerOps.SaveViewer[v];
};
ENDLOOP;
};
Paint: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOLFALSE] --ViewerClasses.PaintProc-- = {
a: Abutter = NARROW[MJSContainers.GetClientData[self]];
Inner: ENTRY PROC [a: Abutter] = {
ENABLE UNWIND => NULL;
CheckUnion: INTERNAL PROC [nl: ChildNodeList] = {
FOR nl ← nl, nl.rest WHILE nl # NIL DO
n: ChildNode ← nl.first;
SELECT n.e FROM
left, right => IF n.v.ww # n.tracked.ww OR n.v.wx # n.tracked.wx THEN Sched[a, n];
top, bottom => IF n.v.wh # n.tracked.wh OR n.v.wy # n.tracked.wy THEN Sched[a, n];
ENDCASE => ERROR;
CheckUnion[n.successors];
ENDLOOP;
};
[] ← CheckEdges[a];
FOR e: Edge IN Edge DO CheckUnion[a.cr[e].successors] ENDLOOP;
IF quit ← (a.queue # ALL[NIL]) THEN TRUSTED {Process.Detach[FORK DeQueue[a, TRUE, TRUE, TRUE, FALSE]]};
};
Inner[a];
};
NoteSizeChanged: PROC [self: Viewer] RETURNS [adjusted: BOOLFALSE] --ViewerClasses.AdjustProc-- = {
a: Abutter ← NARROW[MJSContainers.GetClientData[self]];
Inner: ENTRY PROC [a: Abutter] = {
ENABLE UNWIND => NULL;
[] ← CheckEdges[a]};
viewerToPaint: Viewer;
paintColumn: BOOL;
Inner[a];
[viewerToPaint, paintColumn] ← ReallyDeQueue[a, FALSE, FALSE, TRUE, TRUE];
adjusted ← viewerToPaint # NIL OR paintColumn;
};
NoteAbutterChildSize: PROC [parent, child: Viewer] RETURNS [viewerToPaint: Viewer ← NIL, paintColumn: BOOLFALSE] --MJSContainers.ChildAdjustProc-- = {
a: Abutter = QuaAbutter[parent];
WithLock: ENTRY PROC [a: Abutter] = {
ENABLE UNWIND => NULL;
FOR axis: Axis IN Axis DO
n: ChildNode = Find[child, axis];
IF n # NIL AND n.tracked # [child.wx, child.wy, child.wh, child.ww] THEN Sched[a, n];
ENDLOOP;
FOR edge: Edge IN Edge DO
n: ChildNode = EdgeFind[child, edge];
IF n # NIL AND n.tracked # [child.wx, child.wy, child.wh, child.ww] THEN Sched[a, n];
ENDLOOP;
};
WithLock[a];
[viewerToPaint, paintColumn] ← ReallyDeQueue[a, FALSE, FALSE, FALSE, FALSE];
};
CheckEdges: INTERNAL PROC [a: Abutter] RETURNS [diff: BOOL] = {
diff ← FALSE;
IF a.v.cw # a.tracked.cw THEN {Sched[a, a.cr[right]]; diff ← TRUE};
IF a.v.ch # a.tracked.ch THEN {Sched[a, a.cr[bottom]]; diff ← TRUE};
};
Sched: INTERNAL PROC [a: Abutter, n: Node] = {
Mark: PROC [n: Node] = {
n.scheduled ← TRUE;
FOR nl: ChildNodeList ← n.successors, nl.rest WHILE nl # NIL DO
Mark[nl.first];
ENDLOOP;
};
IF n.scheduled THEN RETURN;
a.queue[n.e] ← CONS[n, a.queue[n.e]];
Mark[n];
};
DeQueue: PROC [a: Abutter, mayPaint, mustPaint, paintIfResize, forkSelfOps: BOOL] =
{[] ← ReallyDeQueue[a, mayPaint, mustPaint, paintIfResize, forkSelfOps]};
ReallyDeQueue: PROC [a: Abutter, mayPaint, mustPaint, paintIfResize, forkSelfOps: BOOL] RETURNS [viewerToPaint: Viewer, paintColumn: BOOL] = {
WithViewerLock: PROC = {[viewerToPaint, paintColumn] ← DeQueueWithAbutterLock[a, mayPaint, mustPaint, paintIfResize, forkSelfOps]};
IF forkSelfOps OR a.sizeSetters = ALL[NIL] OR a.v.iconic THEN ViewerLocks.CallUnderWriteLock[WithViewerLock, a.v] ELSE ViewerLocks.CallUnderColumnLock[WithViewerLock, a.v.column];
};
itLimit: INTEGER ← 20;
DeQueueWithAbutterLock: PROC [a: Abutter, mayPaint, mustPaint, paintIfResize, forkSelfOps: BOOL] RETURNS [viewerToPaint: Viewer ← NIL, paintColumn: BOOLFALSE] = {
moveSelf: BOOLFALSE;
selfWidth, selfHeight: INTEGER;
WithLock: ENTRY PROC [a: Abutter] = {
ENABLE UNWIND => NULL;
Adjust: INTERNAL PROC [n: ChildNode, org: INTEGER] = {
near: INTEGER ← org + esgn[n.e]*n.spaceBefore;
far: INTEGER;
To: INTERNAL PROC [x, y, w, h: INTEGER] = {
ow: INTEGER ← n.v.ww;
oh: INTEGER ← n.v.wh;
dw: BOOL ← w # ow;
dh: BOOL ← h # oh;
diff--erence on other axis--: BOOL;
axis: Axis;
on: Node ← NIL;
viewerToPaint ← a.v;
ViewerOps.MoveViewer[n.v, x, y, w, h, FALSE];
SELECT n.e FROM
left, right => {diff ← n.v.wh # oh; axis ← vertical};
top, bottom => {diff ← n.v.ww # ow; axis ← horizontal};
ENDCASE => ERROR;
IF diff AND (on ← Find[n.v, axis]) # NIL THEN Sched[a, on];
};
d: INTEGER;
IF n.stretch AND n.successors # NIL THEN ERROR;
n.scheduled ← FALSE;
SELECT n.e FROM
left => IF (d ← near - n.v.wx) # 0 THEN {
IF n.stretch
THEN To[near, n.v.wy, n.v.ww-d, n.v.wh]
ELSE To[near, n.v.wy, n.v.ww, n.v.wh]};
right => IF (d ← near - (n.v.wx+n.v.ww)) # 0 THEN {
IF n.stretch
THEN To[n.v.wx, n.v.wy, n.v.ww+d, n.v.wh]
ELSE To[n.v.wx+d, n.v.wy, n.v.ww, n.v.wh]};
top => IF (d ← near - n.v.wy) # 0 THEN {
IF n.stretch
THEN To[n.v.wx, near, n.v.ww, n.v.wh-d]
ELSE To[n.v.wx, near, n.v.ww, n.v.wh]};
bottom => IF (d ← near - (n.v.wy+n.v.wh)) # 0 THEN {
IF n.stretch
THEN To[n.v.wx, n.v.wy, n.v.ww, n.v.wh+d]
ELSE To[n.v.wx, n.v.wy+d, n.v.ww, n.v.wh]};
ENDCASE => ERROR;
n.tracked ← [n.v.wx, n.v.wy, n.v.ww, n.v.wh];
IF n.opposite # NIL THEN {o: ChildNode ← n.opposite;
IF o.v # n.v THEN ERROR;
o.tracked ← n.tracked;
};
far ← SELECT n.e FROM
left => n.v.wx+n.v.ww,
right => n.v.wx,
top => n.v.wy+n.v.wh,
bottom => n.v.wy,
ENDCASE => ERROR;
FOR succs: ChildNodeList ← n.successors, succs.rest WHILE succs # NIL DO
Adjust[succs.first, far];
ENDLOOP;
};
empties: [0 .. 4] ← 0;
itCount: INTEGER ← 0;
scroll: INTEGER = MJSContainers.ScrollOffset[a.v];
hscroll: INTEGER ~ MJSContainers.HScrollOffset[a.v];
rootOrgs: ARRAY Edge OF INTEGER = [
left: 0 + hscroll,
right: a.v.cw + hscroll,
top: 0 + scroll,
bottom: a.v.ch + scroll];
FOR e: Edge ← FIRST[Edge], Next[e] WHILE empties < 4 DO
IF itLimit <= (itCount ← itCount + 1) THEN {
TRUSTED {Process.Detach[FORK TooMany[a]]};
EXIT;
};
IF a.queue[e] # NIL THEN {
rootOrg: INTEGER = rootOrgs[e];
nl: NodeList ← a.queue[e];
a.queue[e] ← NIL;
empties ← 0;
FOR nl ← nl, nl.rest WHILE nl # NIL DO
n: Node = nl.first;
IF n.e # e THEN ERROR;
IF n.scheduled THEN {
WITH n SELECT FROM
cn: ChildNode => {
org: INTEGERWITH cn.predecessor SELECT FROM
rn: RootNode => rootOrg,
cn: ChildNode => SELECT e FROM
left => cn.v.wx+cn.v.ww,
right => cn.v.wx,
top => cn.v.wy+cn.v.wh,
bottom => cn.v.wy,
ENDCASE => ERROR,
ENDCASE => ERROR;
Adjust[cn, org];
};
rn: RootNode => {
SELECT e FROM
left, top => NULL;
right => a.tracked.cw ← a.v.cw;
bottom => a.tracked.ch ← a.v.ch;
ENDCASE => ERROR;
n.scheduled ← FALSE;
FOR cnl: ChildNodeList ← n.successors, cnl.rest WHILE cnl # NIL DO
Adjust[cnl.first, rootOrg];
ENDLOOP;
};
ENDCASE => ERROR;
};
ENDLOOP;
}
ELSE {
empties ← empties + 1
};
ENDLOOP;
IF a.sizeSetters # ALL[NIL] THEN {
goalSizes: ARRAY Axis OF INTEGER ← [a.v.cw, a.v.ch];
outerSizes: ARRAY Axis OF INTEGER;
FOR axis: Axis IN Axis DO
IF a.sizeSetters[axis] # NIL THEN {
goal: INTEGER ← 0;
FOR nl: NodeList ← a.sizeSetters[axis], nl.rest WHILE nl # NIL DO
n: Node = nl.first;
ro: INTEGER = rootOrgs[n.e];
size: INTEGER = SELECT n.e FROM
left => WITH n SELECT FROM
rn: RootNode => rn.spaceAfter,
cn: ChildNode => cn.v.wx+cn.v.ww+cn.spaceAfter - ro,
ENDCASE => ERROR,
right => WITH n SELECT FROM
rn: RootNode => rn.spaceAfter,
cn: ChildNode => ro - cn.v.wx + cn.spaceAfter,
ENDCASE => ERROR,
top => WITH n SELECT FROM
rn: RootNode => rn.spaceAfter,
cn: ChildNode => cn.v.wy+cn.v.wh+cn.spaceAfter - ro,
ENDCASE => ERROR,
bottom => WITH n SELECT FROM
rn: RootNode => rn.spaceAfter,
cn: ChildNode => ro - cn.v.wy + cn.spaceAfter,
ENDCASE => ERROR,
ENDCASE => ERROR;
goal ← MAX[goal, size];
ENDLOOP;
goalSizes[axis] ← goal;
};
ENDLOOP;
outerSizes[horizontal] ← goalSizes[horizontal] + a.v.ww - a.v.cw;
outerSizes[vertical] ← goalSizes[vertical] + a.v.wh - a.v.ch;
SELECT a.v.parent FROM
=NIL => IF resized ← outerSizes[vertical] # a.v.openHeight THEN {
a.v.openHeight ← outerSizes[vertical];
paintColumn ← TRUE;
viewerToPaint ← NIL;
};
#NIL => IF resized ← outerSizes # [a.v.ww, a.v.wh] THEN {
IF forkSelfOps THEN TRUSTED {
Process.Detach[FORK MoveChild[a.v, [a.v.wx, a.v.wy, outerSizes[horizontal], outerSizes[vertical]], paintIfResize]];
IF paintIfResize THEN {paintColumn ← FALSE; viewerToPaint ← NIL};
}
ELSE {
moveSelf ← TRUE;
selfWidth ← outerSizes[horizontal];
selfHeight ← outerSizes[vertical];
};
};
ENDCASE => ERROR;
};
};
resized: BOOLFALSE;
IF mustPaint AND NOT mayPaint THEN ERROR;
IF mayPaint AND NOT paintIfResize THEN ERROR;
WithLock[a];
IF moveSelf THEN {
viewerToPaintBecauseOfNote: Viewer;
ViewerOps.MoveViewer[a.v, a.v.wx, a.v.wy, selfWidth, selfHeight, FALSE];
viewerToPaint ← a.v.parent;
[viewerToPaintBecauseOfNote, paintColumn] ← MJSContainers.NoteChildSize[a.v];
IF viewerToPaintBecauseOfNote # NIL THEN viewerToPaint ← viewerToPaintBecauseOfNote;
};
IF (mayPaint OR (paintIfResize AND resized)) AND (mustPaint OR paintColumn OR viewerToPaint # NIL) THEN {
IF paintColumn THEN {
IF forkSelfOps THEN TRUSTED {Process.Detach[FORK ViewerOps.ComputeColumn[a.v.column, TRUE]]} ELSE ViewerOps.ComputeColumn[a.v.column, TRUE];
mustPaint ← paintColumn ← FALSE;
viewerToPaint ← NIL;
};
IF mustPaint AND viewerToPaint = NIL THEN viewerToPaint ← a.v;
IF viewerToPaint # NIL THEN {
IF forkSelfOps THEN TRUSTED {Process.Detach[FORK ViewerOps.PaintViewer[viewerToPaint, all]]} ELSE ViewerOps.PaintViewer[viewerToPaint, all];
viewerToPaint ← NIL;
};
};
};
MoveChild: PROC [v: Viewer, to: Box, paint: BOOL] = {
paintViewer: Viewer;
paintColumn: BOOL;
ViewerOps.MoveViewer[v, to.wx, to.wy, to.ww, to.wh, FALSE];
[paintViewer, paintColumn] ← MJSContainers.NoteChildSize[v];
IF paint AND NOT (paintViewer # NIL OR paintColumn) THEN paintViewer ← v.parent;
IF paint THEN {
IF paintColumn THEN ViewerOps.ComputeColumn[v.column, TRUE]
ELSE ViewerOps.PaintViewer[paintViewer, all];
};
};
TooMany: PROC [a: Abutter] = {
MessageWindow.Append[
message: Rope.Cat["Too many iterations solving layout of ", a.v.name],
clearFirst: TRUE];
};
QuaViewer: PUBLIC PROC [a: Abutter] RETURNS [v: Viewer] =
{v ← a.v};
QuaAbutter: PUBLIC PROC [v: Viewer] RETURNS [a: Abutter] =
{a ← NARROW[MJSContainers.GetClientData[v]]};
ViewerIsAbutter: PUBLIC PROC [v: Viewer] RETURNS [b: BOOL] = {
data: REF ANY;
IF NOT MJSContainers.IsMJSContainer[v] THEN RETURN[FALSE];
data ← MJSContainers.GetClientData[v];
b ← data # NIL AND ISTYPE[data, Abutter]};
IsAbutter: PUBLIC PROC [ra: REF ANY] RETURNS [b: BOOL] =
{b ← ra # NIL AND ISTYPE[ra, Abutter]};
Narrow: PUBLIC PROC [ra: REF ANY] RETURNS [a: Abutter] =
{a ← NARROW[ra]};
GetClientData: PUBLIC PROC [a: Abutter] RETURNS [cd: REF ANY] = {
cd ← a.clientData;
};
GetClientDataFromViewer: PUBLIC PROC [v: Viewer] RETURNS [cd: REF ANY] = {
cd ← QuaAbutter[v].clientData;
};
ScrollOffset: PUBLIC PROC [a: Abutter] RETURNS [offTop: INTEGER] =
{offTop ← MJSContainers.ScrollOffset[a.v]};
HScrollOffset: PUBLIC PROC [a: Abutter] RETURNS [offLeft: INTEGER] =
{offLeft ← MJSContainers.HScrollOffset[a.v]};
SetLayout: PUBLIC PROC [a: Abutter, rules: Rules, paint: BOOLTRUE] = {
WithLock: ENTRY PROC [a: Abutter] = {
ENABLE UNWIND => NULL;
DoSeries: INTERNAL PROC [e: Edge, s: Series, parent: Node] = {
FOR sl: LIST OF SeriesElement ← s.rigid, sl.rest WHILE sl # NIL DO
parent ← AddChild[a, parent, sl.first.viewer, e, sl.first.spaceBefore, FALSE];
ENDLOOP;
WITH s SELECT FROM
x: Series[none] => IF x.setParentSize THEN MakeSizeSetter[a, parent, edgeAxis[e], x.spaceAfter];
x: Series[parallel] => {
FOR sl: Parallel ← x.p, sl.rest WHILE sl # NIL DO
DoSeries[e, sl.first, parent];
ENDLOOP;
};
x: Series[stretch] => parent ← AddChild[a, parent, x.se.viewer, e, x.se.spaceBefore, TRUE];
ENDCASE => ERROR;
};
a.sizeSetters ← ALL[NIL];
a.queue ← ALL[NIL];
FOR e: Edge IN Edge DO
Forget[a.cr[e].successors];
a.cr[e].successors ← NIL;
ENDLOOP;
FOR e: Edge IN Edge DO DoSeries[e, rules[e], a.cr[e]] ENDLOOP;
};
WithLock[a];
DeQueue[a, paint, paint, paint, FALSE];
};
Forget: INTERNAL PROC [nl: ChildNodeList] = {
FOR nl ← nl, nl.rest WHILE nl # NIL DO
v: Viewer = nl.first.v;
FOR axis: Axis IN Axis DO
ViewerOps.AddProp[v, axisKeys[axis], NIL];
ENDLOOP;
FOR edge: Edge IN Edge DO
ViewerOps.AddProp[v, edgeKeys[edge], NIL];
ENDLOOP;
Forget[nl.first.successors];
ENDLOOP;
};
MakeSizeSetter: INTERNAL PROC [a: Abutter, n: Node, axis: Axis, spaceAfter: INTEGER] = {
n.setSize ← TRUE;
n.spaceAfter ← spaceAfter;
a.sizeSetters[axis] ← CONS[n, a.sizeSetters[axis]];
};
SetSizeBy: PUBLIC PROC [a: Abutter, child: Viewer, edge: Edge, space: INTEGER ← 0, paint: BOOLTRUE] = {
WithLock: ENTRY PROC [a: Abutter] = {
ENABLE UNWIND => NULL;
axis: Axis = edgeAxis[edge];
n: Node = SELECT child FROM
=parentSide => a.cr[edge],
#parentSide => Find[child, axis],
ENDCASE => ERROR;
MakeSizeSetter[a, n, axis, space];
};
WithLock[a];
DeQueue[a, paint, paint, paint, FALSE];
};
AddChild: INTERNAL PROC [a: Abutter, parent: Node, v: Viewer, e: Edge, space: INTEGER, stretch: BOOL] RETURNS [child: ChildNode] = {
axis: Axis ← edgeAxis[e];
opp: ChildNode ← IF stretch THEN EdgeFind[v, oppositeEdge[e]] ELSE NIL;
IF Find[v, axis] # NIL THEN ERROR;
IF EdgeFind[v, e] # NIL THEN ERROR;
child ← NEW [NodeRep.child ← [
e: e,
scheduled: parent.scheduled,
predecessor: parent,
variant: child[
v: v,
spaceBefore: space,
stretch: stretch,
tracked: [0, 0, 0, 0],
opposite: opp
]
]];
IF opp # NIL THEN {
IF opp.opposite # NIL THEN ERROR;
opp.opposite ← child};
parent.successors ← CONS[child, parent.successors];
Sched[a, child];
SELECT stretch FROM
FALSE => ViewerOps.AddProp[v, axisKeys[axis], child];
TRUE => ViewerOps.AddProp[v, edgeKeys[e], child];
ENDCASE => ERROR;
};
Find: INTERNAL PROC [v: Viewer, axis: Axis] RETURNS [n: ChildNode] =
{n ← NARROW[ViewerOps.FetchProp[v, axisKeys[axis]]]};
EdgeFind: INTERNAL PROC [v: Viewer, e: Edge] RETURNS [n: ChildNode] =
{n ← NARROW[ViewerOps.FetchProp[v, edgeKeys[e]]]};
Abut: PUBLIC PROC [a: Abutter, child1, child2: Viewer, edge: Edge, space: INTEGER ← 0, stretch: BOOLFALSE, paint: BOOLTRUE] = {
WithLock: ENTRY PROC [a: Abutter] = {
ENABLE UNWIND => NULL;
parent: Node = SELECT child1 FROM
=parentSide => a.cr[edge],
#parentSide => Find[child1, edgeAxis[edge]],
ENDCASE => ERROR;
IF parent = NIL THEN ERROR;
[] ← AddChild[a, parent, child2, edge, space, stretch];
};
WithLock[a];
DeQueue[a, paint, paint, paint, FALSE];
};
parentSide: PUBLIC Viewer ← NEW [ViewerClasses.ViewerRec];
Start: PROC = {
FOR e: Edge IN [FIRST[Edge] .. LAST[Edge]) DO
Next[e] ← e.SUCC;
ENDLOOP;
Next[LAST[Edge]] ← FIRST[Edge];
};
Start[];
}.