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, August 20, 1987 2:03:20 pm 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];
beforeRenameEvent: CDEvents.EventRegistration ← CDEvents.RegisterEventType[$BeforeRenameDesign];
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 POINTERNIL]
]];
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] = {
oldName: Rope.ROPE ← design.name;
IF Rope.IsEmpty[name] THEN RETURN [FALSE];
done ← ~CDEvents.ProcessEvent[beforeRenameEvent, design, name, TRUE];
IF done THEN {
design.name ← name;
[] ← CDEvents.ProcessEvent[renameEvent, design, oldName, FALSE];
};
};
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: BOOLTRUE] = {
done ← MakeImmutableAndSetNameHints[ob, NIL];
};
MakeImmutableAndSetNameHints: PUBLIC PROC [ob: CD.Object, design: CD.Design] RETURNS [done: BOOLTRUE] = {
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 REFALL[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: BOOLTRUE] 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: BOOLTRUE] = {
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: BOOLTRUE] = {
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: BOOLTRUE] = {
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.Rect�Ops.all, eraseFirst: BOOLTRUE] = {
CDDrawQueue.InsertDrawCommand[design, CDDrawQueue.Request[IF eraseFirst THEN $redraw ELSE $draw, r]];
};
CheckForShorten: PROC [dl: REF DelayedRedrawRec] = INLINE {
IF dl.length>20 THEN {
clear: BOOLFALSE;
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: BOOLTRUE] = {
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: BOOLTRUE] = {
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: BOOL�LSE] = {
--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.ROPENIL] 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: REFNIL] RETURNS [rope: Rope.ROPENIL] = {
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: BOOLFALSE;
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: REFNIL] RETURNS [CD.Number] = {
RETURN [CDPrivate.GetGrid[design, hint]]
};
PlaceInst: PUBLIC PROC [design: CD.Design, ob: CD.Object, hint: REFNIL] 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: BOOLFALSE] = {
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
DrawCollected: 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.