CDOpsImpl.mesa (part of ChipNDale)
Copyright © 1983, 1986 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, July 12, 1983 10:56 am
Last edited by: Christian Jacobi, December 4, 1986 11:52:58 am PST
DIRECTORY
Atom,
CD,
CDBasics,
CDBasicsInline,
CDCells,
CDDrawQueue,
CDEvents,
CDInstances,
CDIO,
CDOps,
CDPrivate,
CDProperties,
CDRects,
CDSequencer,
CDSimpleOps,
CDValue,
Convert,
HashTable,
IO,
Process,
Rope,
SafeStorage,
TerminalIO,
UserProfile;
CDOpsImpl: CEDAR MONITOR
IMPORTS Atom, CD, CDBasics, CDBasicsInline, CDCells, CDDrawQueue, CDEvents, CDInstances, CDIO, CDOps, CDPrivate, CDProperties, CDRects, CDSimpleOps, CDValue, Convert, IO, HashTable, Process, Rope, SafeStorage, TerminalIO, UserProfile
EXPORTS CDOps
SHARES CD =
BEGIN
createEvent: CDEvents.EventRegistration=CDEvents.RegisterEventType[$CreateNewDesign];
resetDesignEvent: CDEvents.EventRegistration=CDEvents.RegisterEventType[$ResetDesign];
DelRec: TYPE = RECORD [
delayedList: LIST OF Remember←NIL,
length: INT ← 0
];
GetDelayedRef: PROC [design: CD.Design] RETURNS [REF DelRec] = {
WITH design.delayedRedrawsPriv SELECT FROM
dr: REF DelRec => RETURN [dr];
ENDCASE => {
dr: REF DelRec = NEW[DelRec←[NIL]];
design.delayedRedrawsPriv ← dr;
RETURN [dr]
};
};
Remember: TYPE = RECORD [area: CD.Rect, clear: BOOL];
InternalResetDesign: PROC [design: CD.Design] = {
--is local since it does not cause a redraw
dummy: CD.Object ← CDCells.CreateEmptyCell[];
dummy.bbox ← CDBasics.universe;
design.cdDirectoryPriv ← HashTable.Create[101, HashTable.RopeEqual, HashTable.HashRope];
design.unDoBuffers ← CD.InitPropRef[];
design^.actual ← LIST[CD.PushRec[
dummyCell: CDInstances.NewInst[ob: dummy],
mightReplace: NIL,
specific: NARROW[dummy.specific]
]];
};
CreateDesign: PUBLIC PROC [technology: CD.Technology] RETURNS [design: CD.Design] = {
IF technology=NIL THEN ERROR CD.Error[callingError, "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];
};
ResetDesign: PUBLIC PROC [design: CD.Design] = {
InternalResetDesign[design];
[] ← CDEvents.ProcessEvent[resetDesignEvent, design];
design.delayedRedrawsPriv ← NIL;
ImmediateRedraw[design];
};
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];
};
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] = {
IF ob#NIL THEN {
inst: CD.Instance ← NEW[CD.InstanceRep ← [ob: ob, trans: trans]];
IF SelectNewMode[design] THEN {
CDSimpleOps.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] = {
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[callingError, "Include of NIL application"];
IF inst.ob=NIL THEN ERROR CD.Error[callingError, "Include application with NIL 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 DelRec] = INLINE {
IF dl.length>20 THEN {
clear: BOOLFALSE;
r: CD.Rect ← CDBasics.empty;
FOR lst: LIST OF Remember ← 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[Remember[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 DelRec = GetDelayedRef[design];
IF dl.delayedList=NIL THEN {
dl.delayedList ← LIST[Remember[area: r, clear: eraseFirst]];
dl.length ← 1
}
ELSE {
list: LIST OF Remember ← 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 Remember ← 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[Remember[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 DelRec = GetDelayedRef[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] = {
--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 EXIT;
pr.drawChild[il.first, il.first.trans, pr];
}
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 EXIT;
pr.drawChild[w.first.dummyCell, [[0, 0], original], pr];
ENDLOOP;
--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 EXIT;
pr.drawChildSel[il.first, il.first.trans, pr];
};
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[cp.ir, CD.outlineLayer, pr];
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[w.first, w.first.trans, pr];
}
ENDLOOP;
};
pr.setGround[pr: pr, pushedOut: FALSE];
--the top level cell
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
IF CDBasicsInline.IntersectRI[pr.interestClip, w.first] THEN {
IF pr.stopFlag^ THEN EXIT;
w.first.ob.class.quickDrawMe[w.first, w.first.trans, pr];
IF pr.selections AND w.first.selected THEN
w.first.ob.class.showMeSelected[w.first, w.first.trans, pr];
};
ENDLOOP;
--the pushed in cells
IF design^.actual.rest#NIL THEN {
pr.drawOutLine[design^.actual.first.specific.ir, CD.outlineLayer, pr];
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 EXIT;
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"]};
};
ObjectRope: PUBLIC PROC [ob: CD.Object] RETURNS [Rope.ROPE] = {
IF ob=NIL THEN RETURN ["nil object"]
ELSE IF ob.class.describe#NIL THEN RETURN [ob.class.describe[ob]]
ELSE RETURN [Atom.GetPName[ob.class.objectType]]
};
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, verbosity: INT𡤀] RETURNS [r: Rope.ROPE] = {
IF inst=NIL THEN r ← "nil instance"
ELSE {
r ←
IF inst.ob=NIL THEN "nil object"
ELSE IF inst.ob.class.describeInst#NIL THEN inst.ob.class.describeInst[inst]
ELSE ObjectRope[inst.ob];
IF verbosity>0 THEN {
WITH CDProperties.GetInstanceProp[inst, $SignalName] SELECT FROM
n: Rope.ROPE => r ← Rope.Cat[r, " ", n];
a: ATOM => r ← Rope.Cat[r, " ", Atom.GetPName[a]];
ENDCASE => NULL;
};
};
RETURN [Rope.Cat["(", r, ")"]]
};
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 ← CDOps.ObjectRope[ob];
inst: CD.Instance => rope ← inst.ob.class.describeInst[inst];
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[callingError, "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[callingError, "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[callingError, "reorder finds bad instance"];
EXIT
}
ENDLOOP;
CDOps.SetInstList[design, il];
};
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, CDPrivate.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;
};
-- Finalization --
-- designs are finalized to break circularities involving objects or instances
DrawCollected: PROC [inst: CD.Instance, trans: CD.Transformation,pr: CD.DrawRef] = {
pr.drawRect[CDBasics.MapRect[inst.ob.bbox, trans], CD.errorLayer, pr]
};
ReadCollected: CD.InternalReadProc --PROC [] RETURNS [Object]-- = {
sz: CD.Position = CDIO.ReadPos[h];
ob: CD.Object = CDRects.CreateRect[sz, CD.errorLayer];
r: Rope.ROPE = "*design of this object has been destroyed; bug in creator program\n";
CDProperties.PutObjectProp[ob, $SignalName, r];
TerminalIO.PutRope[r];
RETURN [ob]
};
WriteCollected: CD.InternalWriteProc = {
CDIO.WritePos[h, CD.InterestSize[ob]];
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: HashTable.EachPairAction = {
WITH value SELECT FROM
ob: CD.Object => IF ob.class=NIL OR ob.class.inDirectory THEN {
ob.class ← gCollectedClass;
ob.layer ← CD.errorLayer;
ob.specific ← NIL;
ob.properties ← NIL;
};
ENDCASE => NULL;
};
FinalizeDesign: PROC [d: CD.Design] = {
d.properties ← NIL;
IF d.cdDirectoryPriv#NIL AND CDValue.Fetch[d, $KeepObjects]=NIL THEN
[] ← NARROW[d.cdDirectoryPriv, HashTable.Table].Pairs[DestroyEachObject];
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;
d.cdDirectoryPriv ← NIL;
d.cdValuePriv ← NIL;
d.cdSequencerPriv ← NIL;
d.cdDrawQueuePriv ← NIL;
d.delayedRedrawsPriv ← NIL;
d.unDoBuffers ← NIL;
};
FinalizerProcess: PROC[fooFQ: SafeStorage.FinalizationQueue] = {
DO
d: CD.Design = NARROW[SafeStorage.FQNext[fooFQ]];
FinalizeDesign[d];
ENDLOOP
};
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[$KeepObjects];
END.