CDOpsImpl.mesa (part of ChipNDale)
Copyright © 1983, 1987 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, July 12, 1983 10:56 am
Last edited by: Christian Jacobi, April 17, 1987 11:08:09 am PDT
DIRECTORY
Atom,
CD,
CDBasics,
CDBasicsInline,
CDCells,
CDDirectory,
CDDrawQueue,
CDEvents,
CDInstances,
CDImports,
CDIO,
CDOps,
CDOpsExtras,
CDPrivate,
CDProperties,
CDSequencer,
CDValue,
Convert,
IO,
Process,
RefTab,
Rope,
SafeStorage,
SymTab,
RuntimeError USING [UNCAUGHT],
TerminalIO,
UserProfile;
CDOpsImpl:
CEDAR
MONITOR
IMPORTS Atom, CD, CDBasics, CDBasicsInline, CDCells, CDDirectory, CDDrawQueue, CDEvents, CDInstances, CDIO, CDOps, CDPrivate, CDProperties, CDValue, Convert, IO, Process, RefTab, Rope, RuntimeError, SafeStorage, SymTab, TerminalIO, UserProfile
EXPORTS CDOps, CDOpsExtras
SHARES CD =
BEGIN
createEvent: CDEvents.EventRegistration=CDEvents.RegisterEventType[$CreateNewDesign];
resetDesignEvent: CDEvents.EventRegistration=CDEvents.RegisterEventType[$ResetDesign];
renameEvent: CDEvents.EventRegistration ← CDEvents.RegisterEventType[$RenameDesign];
DrawDesc: TYPE = RECORD [area: CD.Rect, clear: BOOL];
DelayedRedrawRec:
TYPE =
RECORD [
delayedList: LIST OF DrawDesc←NIL,
length: INT ← 0
];
GetDelayedRedrawRef:
PROC [design:
CD.Design]
RETURNS [
REF DelayedRedrawRec] = {
WITH design.delayedRedrawsPriv
SELECT
FROM
dr: REF DelayedRedrawRec => RETURN [dr];
ENDCASE => {
dr: REF DelayedRedrawRec = NEW[DelayedRedrawRec←[NIL]];
design.delayedRedrawsPriv ← dr;
RETURN [dr]
};
};
InternalResetDesign:
PROC [design:
CD.Design] = {
--is local since it does not cause a redraw
dummy: CD.Object ← CDCells.CreateEmptyCell[];
specific: CD.CellSpecific ← NARROW[dummy.specific];
specific.dummyCell ← TRUE;
dummy.bbox ← CDBasics.universe;
design.cdDirectory1 ← SymTab.Create[];
design.cdDirectory2 ← RefTab.Create[];
design.unDoBuffers ← CD.InitPropRef[];
design^.actual ←
LIST[
CD.PushRec[
dummyCell: CDInstances.NewInst[ob: dummy],
mightReplace: NIL,
specific: specific
]];
};
CreateDesign:
PUBLIC
PROC [technology:
CD.Technology]
RETURNS [design:
CD.Design] = {
IF technology=NIL THEN ERROR CD.Error[calling, "NIL technology"];
design ←
NEW[
CD.DesignRec←[
properties: CD.InitPropRef[],
technology: technology,
unDoBuffers: CD.InitPropRef[],
cdDirectoryPriv2: NEW[LONG POINTER ← NIL]
]];
IF finalizing THEN SafeStorage.EnableFinalization[design];
InternalResetDesign[design]; -- must not cause redraw since event not yet processed
[] ← CDEvents.ProcessEvent[createEvent, design];
};
SetMutability:
PUBLIC
PROC [design:
CD.Design, mutability:
CD.Mutability ← editable] = {
DoIt:
ENTRY
PROC [] = {
ENABLE UNWIND => NULL;
IF design.mutability#editable
AND design.mutability#findOut
THEN
RETURN WITH ERROR CD.Error[designMutability];
design.mutability ← mutability;
};
IF design=NIL THEN ERROR CD.Error[calling];
IF design.mutability=mutability THEN RETURN;
DoIt[];
[] ← CDEvents.ProcessEvent[mutabilityChange, design];
};
ResetDesign:
PUBLIC
PROC [design:
CD.Design] = {
InternalResetDesign[design];
[] ← CDEvents.ProcessEvent[resetDesignEvent, design];
design.delayedRedrawsPriv ← NIL;
ImmediateRedraw[design];
};
RenameDesign:
PUBLIC
PROC [design:
CD.Design, name: Rope.
ROPE]
RETURNS [done:
BOOL] = {
dont: BOOL; oldName: Rope.ROPE ← design.name;
IF Rope.IsEmpty[name] THEN RETURN [FALSE];
design.name ← name;
dont ← CDEvents.ProcessEvent[renameEvent, design, oldName, TRUE];
IF dont
THEN {
design.name ← oldName;
[] ← CDEvents.ProcessEvent[renameEvent, design, oldName, FALSE];
};
done ← ~dont
};
RealTopCell:
PUBLIC PROC [design:
CD.Design]
RETURNS [dummyCell:
CD.Object] = {
FOR l:
LIST
OF
CD.PushRec ← design^.actual, l.rest
DO
IF l.rest=NIL THEN RETURN [l.first.dummyCell.ob]
ENDLOOP
};
PushedTopCell:
PUBLIC PROC [design:
CD.Design]
RETURNS [dummyCell:
CD.Object] = {
RETURN [design^.actual.first.dummyCell.ob];
};
MakeImmutable:
PUBLIC
PROC [ob:
CD.Object]
RETURNS [done:
BOOL ←
TRUE] = {
done ← MakeImmutableAndSetNameHints[ob, NIL];
};
MakeImmutableAndSetNameHints:
PUBLIC
PROC [ob:
CD.Object, design:
CD.Design]
RETURNS [done:
BOOL ←
TRUE] = {
EachChild: CDDirectory.EachObjectProc = {
IF ~me.immutable
THEN {
IF ~MakeImmutableAndSetNameHints[me, design] THEN {done ← FALSE; quit ← TRUE}
};
};
IF ~ob.immutable
THEN {
IF ~ob.class.xDesign
THEN [] ← CDDirectory.EnumerateChildObjects[ob, EachChild]
ELSE
WITH ob.specific
SELECT
FROM
ip: CDImports.ImportSpecific => {
bo: CD.Object ← ip.boundOb;
IF bo#
NIL
THEN
done ← MakeImmutableAndSetNameHints[bo, ip.boundDesign];
};
ENDCASE => done ← FALSE;
IF done
THEN {
IF design#
NIL
THEN {
name: Rope.ROPE ~ CDDirectory.Name[ob, design];
IF ~Rope.IsEmpty[name] THEN CDProperties.PutObjectProp[ob, $Describe, name];
};
ob.immutable ← TRUE
}
}
};
--xx undelete and friends xxxxxxxxxxxxxxxxxx
remSize: CARDINAL = 20;
UndeleteBuffer:
TYPE =
RECORD[
a: ARRAY [0..remSize) OF REF ← ALL[NIL],
next: CARDINAL𡤀
];
Forgett:
PROC [what:
REF] = {
--helps the garbage collector and may prevent circularities through properties
WITH what
SELECT
FROM
il:
CD.InstanceList =>
FOR l:
CD.InstanceList ← il, l.rest
WHILE l#
NIL
DO
l.first.properties ← NIL; l.first.ob ← NIL;
ENDLOOP;
inst: CD.Instance => {inst.properties ← NIL; inst.ob ← NIL};
ENDCASE => NULL;
};
GetUndeleteBuffer:
PROC [d:
CD.Design, create:
BOOL←
TRUE]
RETURNS [buff:
REF UndeleteBuffer←
NIL] = {
WITH CDProperties.GetListProp[d.unDoBuffers^, $delete]
SELECT
FROM
dl: REF UndeleteBuffer => buff ← dl;
ENDCASE =>
IF create
THEN {
buff ← NEW[UndeleteBuffer];
CDProperties.PutProp[d.unDoBuffers, $delete, buff];
};
};
FlushRemember:
PUBLIC PROC [design:
CD.Design] = {
dl: REF UndeleteBuffer ← GetUndeleteBuffer[design, FALSE];
IF dl#
NIL
THEN {
dl.next ← (dl.next+remSize-1) MOD remSize;
DO
dl.next ← (dl.next+remSize-1) MOD remSize;
IF dl.a[dl.next]=NIL THEN EXIT;
Forgett[dl.a[dl.next]];
dl.a[dl.next] ← NIL;
ENDLOOP;
};
design.unDoBuffers ← CD.InitPropRef[];
};
GetRemembered:
PUBLIC PROC [design:
CD.Design]
RETURNS [
CD.InstanceList←
NIL] = {
GiveRemembered:
PROC [d:
CD.Design]
RETURNS [x:
REF] = {
dl: REF UndeleteBuffer ← GetUndeleteBuffer[d];
IF dl=NIL THEN RETURN [NIL];
dl.next ← (dl.next+remSize-1) MOD remSize;
x ← dl.a[dl.next];
dl.a[dl.next] ← NIL;
RETURN [x];
};
WITH GiveRemembered[design]
SELECT
FROM
il: CD.InstanceList => RETURN [il];
inst: CD.Instance => RETURN [LIST[inst]];
ENDCASE => RETURN [NIL];
};
Remember:
PUBLIC PROC [design:
CD.Design, what:
REF] = {
dl: REF UndeleteBuffer ← GetUndeleteBuffer[design, TRUE];
Forgett[dl.a[dl.next]];
dl.a[dl.next] ← what;
dl.next ← (dl.next+1) MOD remSize;
};
--xxxxxxxxxxxxxxxxxxxx
RemoveInstance:
PUBLIC
PROC [design:
CD.Design, inst:
CD.Instance, draw:
BOOL←
TRUE] = {
il: CD.InstanceList ← CDOps.InstList[design];
IF il#
NIL
THEN
IF il.first=inst THEN CDOps.SetInstList[design, il.rest]
ELSE
FOR l:
CD.InstanceList ← il, l.rest
WHILE l.rest#
NIL
DO
IF l.rest.first=inst THEN {l.rest←l.rest.rest; EXIT}
ENDLOOP;
IF draw THEN Redraw[design, CDInstances.InstRectO[inst]];
};
SelectNewMode:
PROC [design:
CD.Design]
RETURNS [
BOOL] = {
mode: INT = CDValue.FetchInt[boundTo: design, key: $CDxSelectNewMode, propagation: technology, ifNotFound: 0];
RETURN [mode=1]
};
IncludeObject:
PUBLIC
PROC [design:
CD.Design, ob:
CD.Object, trans:
CD.Transformation]
RETURNS [inst:
CD.Instance←
NIL]= {
IF ob=NIL THEN ERROR;
inst ← NEW[CD.InstanceRep ← [ob: ob, trans: trans]];
IF SelectNewMode[design]
THEN {
DeselectAll[design];
inst.selected ← TRUE;
};
IncludeInstance[design, inst];
};
FitObjectI:
PUBLIC PROC [ob:
CD.Object, location:
CD.Position ← [0, 0], orientation:
CD.Orientation ←
CD.Orientation[original]]
RETURNS [trans:
CD.Transformation] = {
trans.orient ← orientation;
trans.off ← CDBasics.SubPoints[location, CDBasics.BaseOfRect[CDBasics.MapRect[CD.InterestRect[ob], [[0, 0], orientation]]]];
};
IncludeObjectI:
PUBLIC
PROC[design:
CD.Design, ob:
CD.Object, location:
CD.Position, orientation:
CD.Orientation]
RETURNS [
CD.Instance]= {
RETURN [ IncludeObject[design, ob, FitObjectI[ob, location, orientation]] ];
};
IncludeInstance:
PUBLIC PROC [design:
CD.Design, inst:
CD.Instance, draw:
BOOL←
TRUE] = {
IF inst=NIL THEN ERROR CD.Error[calling, "bad instance"];
IF inst.ob=NIL THEN ERROR CD.Error[calling, "bad object"];
CDOps.SetInstList[design, CONS[inst, CDOps.InstList[design]]];
IF draw THEN Redraw[design, CDInstances.InstRectO[inst], TRUE];
};
IncludeInstanceList:
PUBLIC
PROC [design:
CD.Design, il:
CD.InstanceList, draw:
BOOL←
TRUE] = {
FOR list:
CD.InstanceList ← il, list.rest
WHILE list#
NIL
DO
IncludeInstance[design, list.first, draw];
ENDLOOP;
};
ImmediateRedraw:
PUBLIC
PROC [design:
CD.Design, r:
CD.RectOps.all, eraseFirst:
BOOL←
TRUE] = {
CDDrawQueue.InsertDrawCommand[design, CDDrawQueue.Request[IF eraseFirst THEN $redraw ELSE $draw, r]];
};
CheckForShorten:
PROC [dl:
REF DelayedRedrawRec] =
INLINE {
IF dl.length>20
THEN {
clear: BOOL ← FALSE;
r: CD.Rect ← CDBasics.empty;
FOR lst:
LIST
OF DrawDesc ← dl.delayedList, lst.rest
WHILE lst#
NIL
DO
clear ← clear OR lst.first.clear;
r ← CDBasics.Surround[r, lst.first.area]
ENDLOOP;
dl.delayedList ← LIST[DrawDesc[area: r, clear: clear]];
dl.length ← 1
};
};
Redraw:
PUBLIC
ENTRY
PROC [design:
CD.Design, r:
CD.Rect, eraseFirst:
BOOL←
TRUE] = {
ENABLE UNWIND => NULL;
IF design#
NIL
THEN {
dl: REF DelayedRedrawRec = GetDelayedRedrawRef[design];
IF dl.delayedList=
NIL
THEN {
dl.delayedList ← LIST[DrawDesc[area: r, clear: eraseFirst]];
dl.length ← 1
}
ELSE {
list: LIST OF DrawDesc ← dl.delayedList;
DO
--ASSERTION1: {list#NIL}
--ASSERTION2: {no list rectangle is completely covered by an other one}
IF CDBasics.Intersect[list.first.area, r]
THEN {
IF CDBasics.Inside[r, list.first.area]
THEN {
--r is contained somewhere; we dont include it
--it is unlikely that a small area is cleared and a big one not
list.first.clear ← list.first.clear OR eraseFirst;
--assertion2 => no other elements could be removed
RETURN
}
ELSE
IF CDBasics.Inside[list.first.area, r]
THEN {
--r contains an element; we remove this element and all others
-- which are contained in r
--it is unlikely that a small area is cleared and a big one not
remember: LIST OF DrawDesc ← list;
eraseFirst ← list.first.clear OR eraseFirst;
list.first.area ← r;
--remove all other element contained in r; to maintain assertion2
WHILE list.rest#
NIL
DO
assert3: list#NIL
IF CDBasics.Inside[list.rest.first.area, r]
THEN {
eraseFirst ← list.rest.first.clear OR eraseFirst;
list.rest ← list.rest.rest; --does not change list -> keeps assertion3
dl.length ← dl.length-1;
}
ELSE list ← list.rest --since list.rest#NIL -> keeps assertion3
ENDLOOP;
remember.first.clear ← eraseFirst;
RETURN
}
};
IF list.rest#NIL THEN list←list.rest
ELSE {
list.rest ← LIST[DrawDesc[area: r, clear: eraseFirst]];
dl.length ← dl.length+1;
CheckForShorten[dl];
RETURN
}
ENDLOOP;
} --of dl.delayedList#NIL
} --of design#NIL
};
DoTheDelayedRedraws:
PUBLIC
ENTRY
PROC [design:
CD.Design] = {
ENABLE UNWIND => NULL;
sq: REF DelayedRedrawRec;
sq ← GetDelayedRedrawRef[design];
UNTIL sq.delayedList=
NIL
DO
CDDrawQueue.InsertDrawCommand[design, CDDrawQueue.Request[(IF sq.delayedList.first.clear THEN $redraw ELSE $draw), sq.delayedList.first.area]];
sq.delayedList ← sq.delayedList.rest
ENDLOOP;
sq.length ← 0;
};
RedrawInstance:
PUBLIC
PROC [design:
CD.Design, inst:
CD.Instance←
NIL, erase:
BOOL←
TRUE] = {
IF inst#NIL THEN Redraw[design, CDInstances.InstRectO[inst], erase]
ELSE Redraw[design, CDBasics.universe, erase]
};
DrawDesign:
PUBLIC
PROC[design:
CD.Design, pr:
CD.DrawRef] = {
--selections of the top level cell
IF pr.selections
THEN
FOR il:
CD.InstanceList ← CDOps.InstList[design], il.rest
WHILE il#
NIL
DO
IF il.first.selected
AND CDBasicsInline.IntersectRI[pr.interestClip, il.first]
THEN {
IF pr.stopFlag^ THEN RETURN;
pr.drawChildSel[pr, il.first.ob, il.first.trans];
};
ENDLOOP;
--the top level cell
FOR il:
CD.InstanceList ← CDOps.InstList[design], il.rest
WHILE il#
NIL
DO
IF CDBasicsInline.IntersectRI[pr.interestClip, il.first]
THEN {
IF pr.stopFlag^ THEN RETURN;
pr.drawChild[pr, il.first.ob, il.first.trans, il.first.properties];
}
ENDLOOP;
--the pushed in cells
IF pr.environment
THEN
FOR w:
LIST
OF
CD.PushRec ← design^.actual.rest, w.rest
WHILE w#
NIL
DO
IF pr.stopFlag^ THEN RETURN;
pr.drawChild[pr, w.first.dummyCell.ob, [[0, 0], original]];
ENDLOOP;
};
QuickDrawDesign:
PUBLIC
PROC [design:
CD.Design, pr:
CD.DrawRef] = {
QuickDrawPushedCell:
PROC [cp:
CD.CellSpecific, pr:
CD.DrawRef] =
INLINE {
IF pr.borders AND cp.drawBorder THEN pr.drawOutLine[pr, cp.ir, CD.outlineLayer];
FOR w:
CD.InstanceList ← cp.contents, w.rest
WHILE w#
NIL
DO
IF CDBasicsInline.IntersectRI[pr.interestClip, w.first]
THEN {
IF pr.stopFlag^ THEN EXIT;
w.first.ob.class.quickDrawMe[pr, w.first.ob, w.first.trans, w.first.properties];
}
ENDLOOP;
};
pr.setGround[pr: pr, pushedOut: FALSE];
--top level selections
FOR w:
CD.InstanceList ← CDOps.InstList[design], w.rest
WHILE w#
NIL
DO
IF w.first.selected
AND CDBasicsInline.IntersectRI[pr.interestClip, w.first]
THEN {
IF pr.stopFlag^ THEN RETURN;
w.first.ob.class.quickDrawMe[pr, w.first.ob, w.first.trans, w.first.properties];
IF pr.selections
THEN
w.first.ob.class.showMeSelected[pr, w.first.ob, w.first.trans, w.first.properties];
};
ENDLOOP;
--top level non selections
FOR w:
CD.InstanceList ← CDOps.InstList[design], w.rest
WHILE w#
NIL
DO
IF ~w.first.selected
AND CDBasicsInline.IntersectRI[pr.interestClip, w.first]
THEN {
IF pr.stopFlag^ THEN RETURN;
w.first.ob.class.quickDrawMe[pr, w.first.ob, w.first.trans, w.first.properties];
};
ENDLOOP;
--the pushed in cells
IF design^.actual.rest#
NIL
THEN {
pr.drawOutLine[pr, design^.actual.first.specific.ir, CD.outlineLayer];
IF pr.environment
THEN {
pr.setGround[pr: pr, pushedOut: TRUE];
FOR w:
LIST
OF
CD.PushRec ← design^.actual.rest, w.rest
WHILE w#
NIL
DO
IF pr.stopFlag^ THEN RETURN;
QuickDrawPushedCell[w.first.specific, pr];
ENDLOOP;
};
};
};
SelectedInstance:
PUBLIC PROC [design:
CD.Design]
RETURNS [first:
CD.Instance←NIL, multiple:
BOOLLSE] = {
--first: returns ref to any selected application if there is one or more, otherwise nil.
--multiple: more than one application is selected
FOR w:
CD.InstanceList ← CDOps.InstList[design], w.rest
WHILE w#
NIL
DO
IF w.first.selected
THEN
IF first=NIL THEN first←w.first
ELSE {multiple←TRUE; RETURN}
ENDLOOP;
};
TheInstance:
PUBLIC
PROC [design:
CD.Design, text: Rope.
ROPE←
NIL]
RETURNS [inst:
CD.Instance←
NIL] = {
multiple: BOOL;
IF text#NIL THEN TerminalIO.PutRope[text];
[inst, multiple] ← SelectedInstance[design];
IF multiple THEN {TerminalIO.PutRope[" multiple selection; failed\n"]; RETURN [NIL]};
IF inst=NIL THEN {TerminalIO.PutRope[" no selection; failed\n"]; RETURN [NIL]};
IF inst.ob=NIL THEN {inst←NIL; TerminalIO.PutRope[" bad object; failed\n"]};
};
LayerRope:
PUBLIC
PROC [layer:
CD.Layer]
RETURNS [Rope.
ROPE] = {
uniqueKey: ATOM = CD.LayerKey[layer];
IF uniqueKey=NIL THEN RETURN ["bad layer"];
RETURN [Atom.GetPName[uniqueKey]]
};
InstRope:
PUBLIC
PROC [inst:
CD.Instance, design: CD.Design, verbosity:
INT𡤀]
RETURNS [r: Rope.
ROPE] = {
IF inst=NIL THEN r ← "nil instance"
ELSE r ← CD.Describe[inst.ob, inst.properties, design, verbosity];
};
ToRope:
PUBLIC
PROC [x:
REF, whenFailed:
REF←
NIL]
RETURNS [rope: Rope.
ROPE←
NIL] = {
WITH x
SELECT
FROM
r: Rope.ROPE => rope ← r;
rt: REF TEXT => rope ← Rope.FromRefText[rt];
ri: REF INT => rope ← Convert.RopeFromInt[ri^];
a: ATOM => rope ← Atom.GetPName[a];
l: CDPrivate.LayerRef => rope ← CDOps.LayerRope[l.number];
ob: CD.Object => rope ← CD.Describe[ob];
inst: CD.Instance => rope ← CD.Describe[inst.ob, inst.properties];
d: CD.Design => rope ← d.name;
t: CD.Technology => rope ← t.name;
rc: REF LONG CARDINAL => rope ← Convert.RopeFromCard[rc^];
ri: REF INTEGER => rope ← Convert.RopeFromInt[ri^];
ri: REF NAT => rope ← Convert.RopeFromInt[ri^];
rc: REF CARDINAL => rope ← Convert.RopeFromCard[rc^];
ENDCASE =>
SELECT whenFailed
FROM
NIL => rope ← NIL;
$Interactive => {
RopeNeeded: SIGNAL [ ref: REF REF ] = CODE;
refRef: REF REF = NEW[REF ← x];
TerminalIO.PutRope["please enter a ROPE using the debugger"];
SIGNAL RopeNeeded[refRef];
rope ← ToRope[refRef^ ! RopeNeeded => ERROR];
};
ENDCASE => rope ← ToRope[whenFailed];
};
LambdaRope:
PUBLIC
PROC [n:
CD.Number, lambda:
CD.Number𡤁]
RETURNS [Rope.
ROPE] = {
IF n MOD lambda = 0 THEN RETURN IO.PutFR1[" %g", IO.int[n/lambda]]
ELSE {
r: Rope.ROPE ← " (";
IF n<0 THEN {n ← ABS[n]; r ← " -("};
IF n/lambda>0 THEN r ← IO.PutFR["%0g%0g+", IO.rope[r], IO.int[n/lambda]];
RETURN [IO.PutFR["%0g%0g/%0g)", IO.rope[r], IO.int[n MOD lambda], IO.int[lambda]]];
}
};
ReOrderInstance:
PUBLIC
PROC [design:
CD.Design, inst:
CD.Instance] = {
--on return: design has exactly one occurrence of inst, and it is at the end.
--(includes inst if necessary and removes double occurences)
il: CD.InstanceList ← CDOps.InstList[design];
found: BOOL ← FALSE;
IF inst=NIL THEN ERROR CD.Error[calling, "Reorder of NIL application"];
WHILE il#NIL AND il.first=inst DO {found←TRUE; il ← il.rest} ENDLOOP;
IF il=
NIL
THEN {
IF found THEN il←LIST[inst]
ELSE ERROR CD.Error[calling, "Reorder of application not in design"];
}
ELSE
FOR l:
CD.InstanceList ← il, l.rest
DO
-- l#NIL AND l.first#inst holds at this point
WHILE l.rest#NIL AND l.rest.first=inst DO {found←TRUE; l.rest ← l.rest.rest} ENDLOOP;
IF l.rest=
NIL
THEN {
IF found THEN l.rest ← LIST[inst]
ELSE ERROR CD.Error[calling, "reorder finds bad instance"];
EXIT
}
ENDLOOP;
CDOps.SetInstList[design, il];
};
GetGrid:
PUBLIC
PROC [design:
CD.Design, hint:
REF←
NIL]
RETURNS [
CD.Number] = {
RETURN [CDPrivate.GetGrid[design, hint]]
};
PlaceInst:
PUBLIC
PROC [design:
CD.Design, ob:
CD.Object, hint:
REF←
NIL]
RETURNS [inst:
CD.Instance] = {
lambda: CD.Number ← design.technology.lambda;
space, w1, w2: CD.Number;
grid: CD.Number ← MAX[1, GetGrid[design, hint]];
bb: CD.Rect ← CDOps.BoundingBox[design];
IF ~CDBasics.NonEmpty[bb] THEN bb ← [0, 0, 0, 0];
w1 ← MAX[lambda*5, bb.x2-bb.x1];
w2 ← MAX[lambda*5, CD.InterestSize[ob].x];
space ← MIN[w1, w2]/8+MAX[w1, w2]/80+lambda*4;
inst ← CDInstances.NewInst[ob: ob,
trans: [[(bb.x2+space+grid-1)/grid*grid, (bb.y1+grid-1)/grid*grid]]
];
CDOps.IncludeInstance[design, inst];
};
BoundingBox:
PUBLIC
PROC [design:
CD.Design, onlySelected:
BOOL]
RETURNS [r:
CD.Rect ← CDBasics.empty] = {
IF onlySelected THEN r ← CDInstances.BoundingRectO[CDOps.InstList[design], TRUE]
ELSE
FOR l:
LIST
OF
CD.PushRec ← design.actual, l.rest
WHILE l#
NIL
DO
r ← CDBasics.Surround[r, CDInstances.BoundingRectO[
NARROW[l.first.dummyCell.ob.specific, CD.CellSpecific].contents ]]
ENDLOOP;
};
-- xxxxxxxxxxxxxxxx
DeselectAll:
PUBLIC PROC [design:
CD.Design, setSelection:
BOOL←
FALSE] = {
bb: CD.Rect ← CDBasics.empty; cnt: INT ← 0;
FOR w:
CD.InstanceList ← CDOps.InstList[design], w.rest
WHILE w#
NIL
DO
IF w.first.selected#setSelection
THEN {
cnt𡤌nt+1; bb ← CDBasics.Surround[bb, CDInstances.InstRectO[w.first]];
}
ENDLOOP;
FOR w:
CD.InstanceList ← CDOps.InstList[design], w.rest
WHILE w#
NIL
DO
IF w.first.selected#setSelection
THEN {
w.first.selected ← setSelection;
IF cnt>10 THEN RedrawInstance[design, w.first, ~setSelection]
};
ENDLOOP;
IF cnt<=10 THEN Redraw[design, bb, ~setSelection]
};
-- Finalization --
-- designs are finalized to break circularities involving objects or instances
Draw
Collected:
CD.DrawProc = {
pr.drawRect[pr, CDBasics.MapRect[ob.bbox, trans], CD.errorLayer]
};
ReadCollected:
CD.InternalReadProc = {
r: Rope.ROPE = "*design of this object has been destroyed; bug in creator program\n";
ob: CD.Object ← NEW[CD.ObjectRep←[class: gCollectedClass, layer: CD.errorLayer]];
IF
CDIO.VersionKey[h]<=16
THEN ob.bbox ← CDBasics.RectAt[[0, 0], CDIO.ReadPos[h]]
ELSE ob.bbox ← CDIO.ReadRect[h];
CDProperties.PutObjectProp[ob, $SignalName, r];
ob.immutable ← TRUE;
TerminalIO.PutRope[r];
RETURN [ob]
};
WriteCollected:
CD.InternalWriteProc = {
CDIO.WriteRect[h, ob.bbox];
TerminalIO.PutRope["*write object which has been destroyed\n"];
};
gCollectedClass:
CD.ObjectClass =
CD.RegisterObjectClass[$GCollected, [
drawMe: DrawCollected,
quickDrawMe: DrawCollected,
internalRead: ReadCollected,
internalWrite: WriteCollected,
description: "garbage object; design has been destroyed"
]];
DestroyEachObject: CDDirectory.EachObjectProc = {
IF ~me.immutable
AND (me.class=
NIL
OR me.class.composed)
THEN {
me.class ← gCollectedClass;
me.layer ← CD.errorLayer;
me.properties ← NIL;
--don't access specific; in error case, some procedure might be on its way
};
};
FinalizeDesign:
PROC [d:
CD.Design] = {
IF d.mutability=findOut
THEN
TRUSTED {
Process.Detach[
FORK
TerminalIO.PutF["%l**design %g garbage collected before mutability was set%l\n",
IO.rope["ib"],
IO.rope[CD.DesignName[d]],
IO.rope[" "]
]
];
};
IF d.mutability=editable
OR d.mutability=readonly
THEN {
IF d.cdDirectory1#
NIL
AND d.cdDirectory2#
NIL
AND CDValue.Fetch[d, $DontGC]=
NIL
THEN
[] ← CDDirectory.EnumerateDesign[design: d, proc: DestroyEachObject, dir: TRUE, top: TRUE, recurse: TRUE, dummy: TRUE];
WHILE d.actual#
NIL
DO
d.actual.first.dummyCell.properties ← NIL;
IF d.actual.first.mightReplace#NIL THEN d.actual.first.mightReplace.properties ← NIL;
d.actual ← d.actual.rest;
ENDLOOP;
};
};
FinalizerProcess:
PROC[fooFQ: SafeStorage.FinalizationQueue] = {
DO
d: CD.Design = NARROW[SafeStorage.FQNext[fooFQ]];
FinalizeDesign[d ! RuntimeError.UNCAUGHT => CONTINUE];
ENDLOOP
};
mutabilityChange: CDEvents.EventRegistration = CDEvents.RegisterEventType[$MutabilityChange];
finalizing: BOOL = UserProfile.Boolean["ChipNDale.DoFinalization", TRUE];
IF finalizing
THEN {
fooFQ: SafeStorage.FinalizationQueue = SafeStorage.NewFQ[];
IF Atom.GetProp[$ChipNDalePrivate, $FinalizationEnabled]=$TRUE
THEN
SafeStorage.ReEstablishFinalization[CODE[CD.DesignRec], 0, fooFQ]
ELSE {
SafeStorage.EstablishFinalization[CODE[CD.DesignRec], 0, fooFQ];
Atom.PutProp[$ChipNDalePrivate, $FinalizationEnabled, $TRUE];
};
TRUSTED {Process.Detach[FORK FinalizerProcess[fooFQ]]};
};
CDValue.RegisterKey[$DontGC];
END.