DIRECTORY
ViewerConstraints, ViewerConstraintsPrivate,
Process USING [Detach],
RefTab USING [Create, Fetch, Ref, Store],
ViewerEvents USING [EventProc, RegisterEventProc],
ViewerOps USING [AddProp, EstablishViewerPosition, FetchProp, PaintViewer];
OPEN ViewerConstraints, ViewerConstraintsPrivate;
ConstraintClass: PUBLIC TYPE ~ ViewerConstraintsPrivate.ConstraintClass;
ConstraintClassRep: PUBLIC TYPE ~ ViewerConstraintsPrivate.ConstraintClassRep;
Constraint: PUBLIC TYPE ~ ViewerConstraintsPrivate.Constraint;
ConstraintRep: PUBLIC TYPE ~ ViewerConstraintsPrivate.ConstraintRep;
Error: PUBLIC ERROR [type: ErrorType, reason: ROPE] ~ CODE;
CreateConstraintClass:
PUBLIC PROC [enforce: EnforceProc, nFrom, nTo:
NAT, classData:
REF ←
NIL]
RETURNS [class: ConstraintClass] ~ {
IF nTo=0 THEN ERROR Error[invalidConstraintClass, "Must have output edges being constrained."];
class ←
NEW[ConstraintClassRep ← [
nFrom: nFrom,
nTo: nTo,
enforce: enforce,
classData: classData
]];
};
constraintProp: ARRAY EdgeType OF ATOM ~ [$LeftAffectsConstraints, $RightAffectsConstraints, $TopAffectsConstraints, $BottomAffectsConstraints];
edgeControlledByProp: ARRAY EdgeType OF ATOM ~ [$LeftControlledBy, $RightControlledBy, $TopControlledBy, $BottomControlledBy];
EstablishConstraint:
PUBLIC PROC [class: ConstraintClass, fetch: InitProc, instanceData:
REF ←
NIL]
RETURNS [constraint: Constraint] ~ {
ancestor: Viewer;
c: Constraint ~
NEW[ConstraintRep ← [
class: class,
from: NEW[ValuesRep[class.nFrom]],
to: NEW[ValuesRep[class.nTo]],
eFrom: NEW[EdgeSequenceRep[class.nFrom]],
eTo: NEW[EdgeSequenceRep[class.nTo]],
instanceData: instanceData
]];
FOR i:
NAT
IN [0..class.nTo)
DO
c.eTo[i] ← fetch[i: i, from: FALSE];
IF i=0 THEN ancestor ← Ancestor[c.eTo[0].v];
IF Ancestor[c.eTo[i].v]#ancestor THEN ERROR Error[invalidConstraint, "All edges must belong to viewers with a common ancestor."];
ViewerOps.AddProp[viewer: c.eTo[i].v, prop: edgeControlledByProp[c.eTo[i].et], val: c];
ENDLOOP;
FOR i:
NAT
IN [0..class.nFrom)
DO
constraintList: LIST OF Constraint;
c.eFrom[i] ← fetch[i: i, from: TRUE];
IF Ancestor[c.eFrom[i].v]#ancestor THEN ERROR Error[invalidConstraint, "All edges must belong to viewers with a common ancestor."];
constraintList ← CONS[c, NARROW[ViewerOps.FetchProp[viewer: c.eFrom[i].v, prop: constraintProp[c.eFrom[i].et]]]];
ViewerOps.AddProp[viewer: c.eFrom[i].v, prop: constraintProp[c.eFrom[i].et], val: constraintList];
ENDLOOP;
{
cl: LIST OF Constraint ← NARROW[ViewerOps.FetchProp[viewer: ancestor, prop: $ConstraintSystem]];
OrderConstraints[cl ← CONS[c, cl]];
ViewerOps.AddProp[viewer: ancestor, prop: $ConstraintSystem, val: cl];
};
RETURN [c];
};
BreakConstraint:
PUBLIC
PROC [constraint: Constraint] ~ {
ancestor: Viewer ~ Ancestor[constraint.eTo[0].v];
cl: LIST OF Constraint ~ NARROW[ViewerOps.FetchProp[viewer: ancestor, prop: $ConstraintSystem]];
SELECT
TRUE
FROM
cl=NIL => ERROR;
cl.first=constraint => ViewerOps.AddProp[viewer: ancestor, prop: $ConstraintSystem, val: cl.rest];
ENDCASE =>
FOR each:
LIST
OF Constraint ← cl, each.rest
UNTIL each.rest=
NIL
DO
IF each.rest.first=constraint THEN each.rest ← each.rest.rest;
ENDLOOP;
FOR k:
NAT
IN [0..constraint.eTo.n)
DO
ViewerOps.AddProp[viewer: constraint.eTo[k].v, prop: edgeControlledByProp[constraint.eTo[k].et], val: NIL]; --It's safe to simply remove the property from the propList, since it could only have been controlled by one constraint anyhow.
ENDLOOP;
FOR k:
NAT
IN [0..constraint.eFrom.n)
DO
cl: LIST OF Constraint ← NARROW[ViewerOps.FetchProp[viewer: constraint.eTo[k].v, prop: constraintProp[constraint.eTo[k].et]]];
SELECT
TRUE
FROM
cl=NIL => {};
cl.first=constraint => ViewerOps.AddProp[viewer: constraint.eTo[k].v, prop: constraintProp[constraint.eTo[k].et], val: cl.rest];
ENDCASE => {
--Don't need to reset the property ... just eliminate the offending party from the list
FOR each:
LIST
OF Constraint ← cl, each.rest
UNTIL each.rest=
NIL
DO
IF each.rest.first=constraint THEN each.rest ← each.rest.rest;
ENDLOOP;
};
ENDLOOP;
};
BreakConstraintsToEdge:
PUBLIC PROC [e: Edge] ~ {
c: Constraint ~ NARROW[ViewerOps.FetchProp[viewer: e.v, prop: edgeControlledByProp[e.et]]];
cl: LIST OF Constraint ~ NARROW[ViewerOps.FetchProp[viewer: e.v, prop: constraintProp[e.et]]];
IF c#NIL THEN BreakConstraint[c];
FOR each:
LIST
OF Constraint ← cl, each.rest
UNTIL each=
NIL
DO
BreakConstraint[each.first];
ENDLOOP;
};
Suspect:
PUBLIC PROC [e: Edge] ~ {
constraintList: LIST OF Constraint ← NARROW[ViewerOps.FetchProp[viewer: e.v, prop: constraintProp[e.et]]];
FOR each:
LIST
OF Constraint ← constraintList, each.rest
UNTIL each=
NIL
DO
each.first.check ← TRUE;
ENDLOOP;
{
--Note that we need to paint the edge's viewer's parent
ancestor: Viewer ~ Ancestor[e.v];
parent: Viewer ~ IF e.v.parent=NIL THEN e.v ELSE e.v.parent;
toPaint: RefTab.Ref ← NARROW[ViewerOps.FetchProp[viewer: ancestor, prop: $ConstraintToPaint]];
IF toPaint=NIL THEN ViewerOps.AddProp[viewer: ancestor, prop: $ConstraintToPaint, val: toPaint ← RefTab.Create[]];
[] ← RefTab.Store[x: toPaint, key: parent, val: NIL];
};
};
WhatNeedsPainting:
PUBLIC
PROC [v: Viewer, clear:
BOOL ←
TRUE]
RETURNS [l:
LIST
OF Viewer ←
NIL] ~ {
Check:
PROC [viewer: Viewer] ~ {
IF RefTab.Fetch[toPaint, viewer].found THEN l ← CONS[viewer, l]
ELSE
FOR each: Viewer ← viewer.child, each.sibling
UNTIL each=
NIL
DO
Check[each];
ENDLOOP;
};
ancestor: Viewer ~ Ancestor[v];
toPaint: RefTab.Ref ~ NARROW[ViewerOps.FetchProp[viewer: ancestor, prop: $ConstraintToPaint]];
IF toPaint=NIL THEN RETURN[NIL];
Check[ancestor];
};
PaintConstraintSystem:
PUBLIC
ENTRY
PROC [v: Viewer, fork:
BOOL ←
TRUE] ~ {
ENABLE UNWIND => {};
IF fork
THEN
TRUSTED {
Process.Detach[FORK PaintConstraintSystem[v, FALSE]];
}
ELSE {
EnforceConstraintSystem[v];
FOR each:
LIST
OF Viewer ← WhatNeedsPainting[v], each.rest
UNTIL each=
NIL
DO
ViewerOps.PaintViewer[viewer: each.first, hint: all];
ENDLOOP;
};
};
EnforceConstraintSystem:
PUBLIC PROC [v: Viewer] ~ {
cl: LIST OF Constraint ~ NARROW[ViewerOps.FetchProp[viewer: Ancestor[v], prop: $ConstraintSystem]];
EnforceConstraints[cl];
};
Watch for deleted Viewers
ViewerDeleted: ViewerEvents.EventProc = {
[viewer: ViewerClasses.Viewer, event: ViewerEvents.ViewerEvent, before: BOOL] RETURNS [abort: BOOL ← FALSE]
FOR et: EdgeType
IN EdgeType
DO
BreakConstraintsToEdge[[viewer, et]];
ENDLOOP;
};
[] ← ViewerEvents.RegisterEventProc[proc: ViewerDeleted, event: destroy, before: FALSE];