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: BOOL ← FALSE,
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:
BOOL ←
TRUE]
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:
BOOL ←
FALSE]
--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:
BOOL ←
FALSE]
--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:
BOOL ←
FALSE]
--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:
BOOL ←
FALSE]
--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:
BOOL ←
FALSE] = {
moveSelf: BOOL ← FALSE;
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:
INTEGER ←
WITH 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: BOOL ← FALSE;
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:
BOOL ←
TRUE] = {
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:
BOOL ←
TRUE] = {
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:
BOOL ←
FALSE, paint:
BOOL ←
TRUE] = {
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[];
}.