CDSimpleOpsImpl.mesa (part of ChipNDale)
Copyright © 1983, 1985 by Xerox Corporation. All rights reserved.
by Christian Jacobi, July 12, 1983 10:56 am
last edited by Christian Jacobi, June 11, 1985 10:11:25 am PDT
DIRECTORY
CD,
CDInstances,
CDSimpleOps,
CDCommandOps,
CDEvents,
CDBasics,
CDOps,
CDOrient,
CDProperties,
CDRects,
CDStretchyExtras,
CDValue,
Rope USING [IsEmpty, ROPE],
TerminalIO;
CDSimpleOpsImpl: CEDAR PROGRAM
IMPORTS CD, CDInstances, CDBasics, CDCommandOps,
CDEvents, CDOps, CDOrient, CDProperties, CDRects, CDStretchyExtras, CDValue, Rope, TerminalIO
EXPORTS CDSimpleOps =
BEGIN
renameEvent: CDEvents.EventRegistration�vents.RegisterEventType[$RenameDesign];
RenameDesign: PUBLIC PROC [design: CD.Design, name: Rope.ROPE] =
--is only local name
BEGIN
dont: BOOL;
oldName: Rope.ROPE ← design.name;
IF Rope.IsEmpty[name] THEN {
TerminalIO.WriteRope[" not renamed\n"];
RETURN
};
design.name ← name;
dont ← CDEvents.ProcessEvent[renameEvent, design, oldName, TRUE];
IF dont THEN {
design.name ← oldName;
[] ← CDEvents.ProcessEvent[renameEvent, design, oldName, FALSE];
}
END;
Select: PUBLIC PROC [design: CD.Design, pos: CD.Position, verbose: BOOL] =
BEGIN
sel: CD.Instance ← NIL;
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
IF ~w.first.selected THEN {
IF CDInstances.PointToI[pos, w.first] THEN {sel ← w.first; EXIT}
};
ENDLOOP;
IF sel#NIL THEN {
sel.selected ← TRUE;
--reorder list, future select selects next lower application
CDOps.ReOrderInstance[design, sel];
CDCommandOps.RedrawInstance[design, sel];
IF verbose THEN CDCommandOps.WriteInfo[sel]
}
ELSE IF verbose THEN {
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
IF w.first.selected THEN {
IF CDInstances.PointToI[pos, w.first] THEN {
CDCommandOps.WriteInfo[w.first];
TerminalIO.WriteRope[" again"];
RETURN
}
};
ENDLOOP;
TerminalIO.WriteRope[" no pointed object"];
}
END;
DeSelect: PUBLIC PROC [design: CD.Design, pos: CD.Position, verbose: BOOL] =
BEGIN
inst: CD.Instance ← CDInstances.InstanceAt[CDOps.InstList[design], pos, TRUE];
IF inst#NIL THEN
BEGIN
IF ~inst.selected THEN ERROR;
inst.selected ← FALSE;
CDCommandOps.RedrawInstance[design, inst];
--reorder list, future select op finds next lower application
CDOps.ReOrderInstance[design, inst];
END;
IF verbose THEN CDCommandOps.WriteInfo[inst];
END;
BoundingRectAndCount: PROC[list: CD.InstanceList, selectedOnly: BOOLEANFALSE]
RETURNS [bound: CD.Rect�sics.empty, cnt: INT𡤀] =
BEGIN
FOR tem: LIST OF CD.Instance ← list, tem.rest WHILE tem#NIL DO
IF selectedOnly AND NOT tem.first.selected THEN LOOP;
cnt ← cnt+1;
bound ← CDBasics.Surround[bound, CDInstances.InstRectO[tem.first]]
ENDLOOP;
END;
DeselectAll: PUBLIC PROC [design: CD.Design] =
BEGIN
doItSingleWise: NAT = 4;
sel: CD.InstanceList = CDInstances.OnlySelected[CDOps.InstList[design]];
b: CD.Rect;
cnt: INT;
[b, cnt] ← BoundingRectAndCount[list: sel];
CDInstances.DeSelectList[sel];
IF cnt<=doItSingleWise THEN
FOR l: CD.InstanceList ← sel, l.rest WHILE l#NIL DO
CDCommandOps.RedrawInstance[design, l.first]
ENDLOOP
ELSE CDOps.DelayedRedraw[design, b];
END;
SelectAll: PUBLIC PROC [design: CD.Design] =
BEGIN
--dont care about redrawing too much
b: CD.Rect ← CDInstances.BoundingRectO[list: CDOps.InstList[design], selectedOnly: FALSE];
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
w.first.selected ← TRUE
ENDLOOP;
CDOps.DelayedRedraw[design, b, FALSE];
END;
AreaSelectTouched: PROC [design: CD.Design, area: CD.Rect] =
BEGIN
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
IF NOT w.first.selected
AND CDBasics.Intersect[area, CDInstances.InstRectI[w.first]] THEN {
w.first.selected ← TRUE;
CDCommandOps.RedrawInstance[design, w.first, FALSE];
};
ENDLOOP;
END;
AreaDeSelectTouched: PROC [design: CD.Design, area: CD.Rect] =
BEGIN
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
IF w.first.selected
AND CDBasics.Intersect[area, CDInstances.InstRectI[w.first]] THEN {
w.first.selected ← FALSE;
CDCommandOps.RedrawInstance[design, w.first];
};
ENDLOOP;
END;
AreaSelectContained: PROC [design: CD.Design, area: CD.Rect] =
BEGIN
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
IF CDBasics.Inside[CDInstances.InstRectI[w.first], area] THEN w.first.selected ← TRUE
ENDLOOP;
CDOps.DelayedRedraw[design, area, FALSE];
END;
AreaDeSelectContained: PROC [design: CD.Design, area: CD.Rect] =
BEGIN
FOR w: CD.InstanceList ← CDOps.InstList[design], w.rest WHILE w#NIL DO
IF CDBasics.Inside[CDInstances.InstRectI[w.first], area] THEN w.first.selected ← FALSE
ENDLOOP;
CDOps.DelayedRedraw[design, area];
END;
AreaSelect: PUBLIC PROC [design: CD.Design, area: CD.Rect, includePartial: BOOL] =
BEGIN
IF includePartial THEN AreaSelectTouched[design, area]
ELSE AreaSelectContained[design, area];
END;
AreaDeSelect: PUBLIC PROC [design: CD.Design, area: CD.Rect, includePartial: BOOL] =
BEGIN
IF includePartial THEN AreaDeSelectTouched[design, area]
ELSE AreaDeSelectContained[design, area];
END;
DeleteSelected: PUBLIC PROC [design: CD.Design, verbose: BOOL] =
BEGIN
count: INT ← 0;
toDelete, newContents: CD.InstanceList;
[selected: toDelete, others: newContents] ← CDInstances.SplitSelected[CDOps.InstList[design]];
CDOps.SetInstList[design, newContents];
FOR w: CD.InstanceList ← toDelete, w.rest WHILE w#NIL DO
count ← count+1;
CDCommandOps.RedrawInstance[design, w.first, TRUE];
ENDLOOP;
IF verbose THEN {
IF count#1 THEN {
TerminalIO.WriteInt[count];
TerminalIO.WriteRope[" objects"]
}
ELSE CDCommandOps.WriteInfo[toDelete.first]
};
RememberShortTime[design, toDelete];
END;
RedrawMoved: PROC [design: CD.Design, r: CD.Rect, offset: CD.Position] =
BEGIN
moved: CD.Rect ← CDBasics.MoveRect[r, offset];
IF CDBasics.Intersect[r, moved] THEN
CDOps.DelayedRedraw[design, CDBasics.Surround[r, moved], TRUE]
ELSE {
CDOps.DelayedRedraw[design, r];
CDOps.DelayedRedraw[design, moved]; -- use eraseFirst because also used by copy
}
END;
StretchyMoveMode: PROC[design: CD.Design] RETURNS [BOOL] =
BEGIN
mode: INT = CDValue.FetchInt[boundTo: design, key: $CDxStretchyMove, propagation: global, ifNotFound: 0];
RETURN [mode=1]
END;
MoveSelected: PUBLIC PROC [design: CD.Design, offset: CD.Position, stretchy: CDSimpleOps.StretchyMode] =
BEGIN
IF stretchy=yes OR ((stretchy=option) AND StretchyMoveMode[design]) THEN StretchyMoveSelected[design, offset]
ELSE SimpleMoveSelected[design, offset]
END;
SimpleMoveSelected: PUBLIC PROC [design: CD.Design, offset: CD.Position] =
BEGIN
sel: CD.InstanceList = CDInstances.OnlySelected[CDOps.InstList[design]];
bounding: CD.Rect = CDInstances.BoundingRectO[sel];
FOR w: CD.InstanceList ← sel, w.rest WHILE w#NIL DO
w.first.location ← CDBasics.AddPoints[w.first.location, offset];
ENDLOOP;
RedrawMoved[design, bounding, offset]
END;
CopySelected: PUBLIC PROC [design: CD.Design, offset: CD.Position] =
BEGIN
new: CD.InstanceList←NIL;
sel: CD.InstanceList = CDInstances.OnlySelected[CDOps.InstList[design]];
bounding: CD.Rect ← CDInstances.BoundingRectO[sel];
FOR w: CD.InstanceList ← sel, w.rest WHILE w#NIL DO
inst: CD.Instance ← NEW[CD.InstanceRep];
inst.ob ← w.first.ob;
inst.location ← CDBasics.AddPoints[w.first.location, offset];
inst.orientation ← w.first.orientation;
inst.properties ← CDProperties.CopyProps[w.first.properties];
inst.selected ← TRUE;
new ← CONS[inst, new];
w.first.selected ← FALSE;
ENDLOOP;
CDOps.IncludeInstanceList[design, new, FALSE];
RedrawMoved[design, bounding, offset];
END;
TransformSelected: PUBLIC PROC [design: CD.Design, transform: CD.Orientation] =
BEGIN
sel: CD.InstanceList = CDInstances.OnlySelected[CDOps.InstList[design]];
b: CD.Rect ← CDInstances.BoundingRectO[sel];
ib: CD.Rect = CDInstances.BoundingRectI[sel];
FOR w: CD.InstanceList ← sel, w.rest WHILE w#NIL DO
CDInstances.Transform[w.first, ib, transform]
ENDLOOP;
CDOps.DelayedRedraw[design, b];
b ← CDOrient.MapRect[
itemInCell: CDBasics.MoveRect[b, CDBasics.NegOffset[CDBasics.BaseOfRect[ib]]],
cellSize: CDBasics.SizeOfRect[ib],
cellInstOrient: transform,
cellInstPos: CDBasics.BaseOfRect[ib]
];
CDOps.DelayedRedraw[design, b, FALSE];
END;
BaseTransformSelected: PUBLIC PROC [design: CD.Design, transform: CD.Orientation, base: CD.Rect] =
BEGIN
sel: CD.InstanceList = CDInstances.OnlySelected[CDOps.InstList[design]];
b: CD.Rect ← CDInstances.BoundingRectO[sel];
newB: CD.Rect ← CDOrient.MapRect[
itemInCell: b,
cellSize: CDBasics.SizeOfRect[base],
cellInstOrient: transform,
cellInstPos: CDBasics.BaseOfRect[base]
];
FOR w: CD.InstanceList ← sel, w.rest WHILE w#NIL DO
CDInstances.Transform[w.first, base, transform]
ENDLOOP;
CDOps.DelayedRedraw[design, b];
CDOps.DelayedRedraw[design, newB]
END;
-- -- -- -- -- -- -- -- -- -- -- --
remSize: CARDINAL = 20;
RememberBuffer: TYPE = RECORD[
a: ARRAY [0..remSize) OF REFALL[NIL],
next: CARDINAL𡤀];
Forgett: PROC[class: REF] =
--helps the garbage collector,
--special may prevent circularities through properties
BEGIN
WITH class SELECT FROM
il: CD.InstanceList =>
FOR l: CD.InstanceList ← il, l.rest WHILE l#NIL DO
Forgett[l.first]
ENDLOOP;
inst: CD.Instance => {inst.properties ← NIL; inst.ob ← NIL};
ENDCASE => NULL;
END;
RememberShortTime: PROC [d: CD.Design, what: REF] =
BEGIN
dl: REF RememberBuffer;
IF d^.actual.first.deletedList=NIL THEN d^.actual.first.deletedList ← NEW[RememberBuffer];
dl ← NARROW[d^.actual.first.deletedList];
Forgett[dl.a[dl.next]];
dl.a[dl.next] ← what;
dl.next ← (dl.next+1) MOD remSize;
END;
GiveRemembered: PROC [d: CD.Design] RETURNS [REF] =
BEGIN
x: REF;
dl: REF RememberBuffer;
IF d^.actual.first.deletedList=NIL THEN RETURN [NIL];
dl ← NARROW[d^.actual.first.deletedList];
dl.next ← (dl.next+remSize-1) MOD remSize;
x ← dl.a[dl.next];
dl.a[dl.next] ← NIL;
RETURN [x];
END;
Undelete: PUBLIC PROC [design: CD.Design, n: INT𡤀] =
BEGIN
del: REF ← GiveRemembered[design];
IF del=NIL THEN RETURN;
WITH del SELECT FROM
il: CD.InstanceList => CDOps.IncludeInstanceList[design, il];
inst: CD.Instance => CDOps.IncludeInstance[design, inst]
ENDCASE => NULL;
END;
FlushDeletedCache: PUBLIC PROC [design: CD.Design] =
BEGIN
dl: REF RememberBuffer;
IF design^.actual.first.deletedList=NIL THEN RETURN;
dl ← NARROW[design^.actual.first.deletedList];
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
END;
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Primality: TYPE = {primary, secondary};
ChangeWireLength: PROC [design: CD.Design, inst: CD.Instance, amount: CD.Number] =
BEGIN
--??? for now CDRects.CreateRect is ONLY method to create wires
sz: CD.Position = CDBasics.SizeOfRect[CD.InterestRect[inst.ob]];
r: CD.Rect ← CDInstances.InstRectO[inst];
newOb: CD.Object←NIL;
IF NOT WireTyped[inst.ob] THEN ERROR;
newOb ← CDRects.CreateRect[CD.Position[x: sz.x, y: sz.y+amount], inst.ob.layer];
IF newOb=NIL THEN RETURN;
IF inst.ob.class#newOb.class THEN ERROR;
--this checks if old object was created with CDRects.CreateRect
inst.ob ← newOb;
r ← CDBasics.Surround[r, CDInstances.InstRectO[inst]];
CDOps.DelayedRedraw[design, r];
END;
StretchyMoveSP: PROC[design: CD.Design, offset: CD.Position, selectedApps, nonSelectedApps: CD.InstanceList] =
BEGIN
primApps: CD.InstanceList ← NIL;
primList, secondList, nonPrimList: StretchList ← NIL;
rect: CD.Rect;
rect ← CDInstances.BoundingRectO[selectedApps];
FOR l: CD.InstanceList ← nonSelectedApps, l.rest WHILE l#NIL DO
IF WireTyped[l.first.ob] THEN {
me: REF StretchRec←NEW[StretchRec←StretchRec[
ap: l.first,
conductRect: CDInstances.InstRectI[l.first],
horizontal: CDOrient.IncludesOddRot90[l.first.orientation] -- (length in y direction)
]];
IF CDBasics.Intersect[rect, me.conductRect] AND HasMatch[me, selectedApps, primary] THEN {primList ← CONS[me, primList]; primApps ← CONS[me.ap, primApps]}
ELSE nonPrimList ← CONS[me, nonPrimList]
};
ENDLOOP;
FOR l: StretchList ← nonPrimList, l.rest WHILE l#NIL DO
IF HasMatch[l.first, primApps, secondary] THEN secondList ← CONS[l.first, secondList]
ENDLOOP;
FOR l: StretchList ← secondList, l.rest WHILE l#NIL DO
FiddleWire[design, l.first, offset, secondary]
ENDLOOP;
FOR l: StretchList ← primList, l.rest WHILE l#NIL DO
FiddleWire[design, l.first, offset, primary]
ENDLOOP;
FOR l: CD.InstanceList ← selectedApps, l.rest WHILE l#NIL DO
MoveApplication[design, l.first, offset]
ENDLOOP;
END;
StretchyMoveSelected: PROC[design: CD.Design, offset: CD.Position] =
BEGIN
selectedApps, nonSelectedApps: CD.InstanceList ← NIL;
[selectedApps, nonSelectedApps] ← CDInstances.SplitSelected[CDOps.InstList[design]];
StretchyMoveSP[design, offset, selectedApps, nonSelectedApps]
END;
StretchList: TYPE = LIST OF REF StretchRec;
StretchRec: TYPE = RECORD [
ap: CD.Instance,
conductRect: CD.Rect, -- hint only
near: BOOLFALSE, -- match at near edge (relative to the origin)
far: BOOLFALSE, -- match at far edge (relative to the origin)
horizontal: BOOLFALSE
];
FiddleWire: PROC [design: CD.Design, wire: REF StretchRec, offset: CD.Position, class: Primality] =
BEGIN
--??? change slow case distinction, but now better is easy to understand
move: CD.Position←offset;
IF ~wire.near AND ~wire.far THEN RETURN;
IF class#primary THEN {IF wire.horizontal THEN move.y𡤀 ELSE move.x𡤀};
IF wire.near AND ~wire.far THEN {
IF class#primary THEN {IF wire.horizontal THEN move.y𡤀 ELSE move.x𡤀};
ChangeWireLength[design, wire.ap, -(IF wire.horizontal THEN offset.x ELSE offset.y)];
};
IF ~wire.near AND wire.far THEN {
IF wire.horizontal THEN {move.x𡤀} ELSE {move.y𡤀};
ChangeWireLength[design, wire.ap, (IF wire.horizontal THEN offset.x ELSE offset.y)];
};
IF wire.near AND wire.far THEN NULL;
MoveApplication[design, wire.ap, move]
END;
HasMatch: PROC [me: REF StretchRec, list: CD.InstanceList, class: Primality] RETURNS [BOOL] =
-- checks weather me has some match with any of list
BEGIN
near, far: BOOL;
nearEdge, farEdge: CD.Rect;
nearEdge ← farEdge ← me.conductRect; -- edges at near or far end of wire, parallel to width
IF ~CDOrient.IncludesOddRot90[me.ap.orientation] THEN {
farEdge.y1 ← me.conductRect.y2;
nearEdge.y2 ← me.conductRect.y1;
}
ELSE {
farEdge.x1 ← me.conductRect.x2;
nearEdge.x2 ← me.conductRect.x1;
me.horizontal ← TRUE;
};
FOR l: CD.InstanceList ← list, l.rest WHILE l#NIL DO
r: CD.Rect = CDInstances.InstRectI[l.first];
near ← CDBasics.Intersect[nearEdge, r];
far ← CDBasics.Intersect[farEdge, r];
IF near OR far THEN {
IF WireTyped[l.first.ob] THEN {
--??? Only CDRects.CreateRect for now
IF l.first.ob.layer#me.ap.ob.layer THEN near ← far ← FALSE
ELSE IF class#primary AND me.horizontal=CDOrient.IncludesOddRot90[l.first.orientation] THEN near ← far ← FALSE
}
ELSE IF ISTYPE[l.first.ob.specificRef, CD.CellPtr] THEN NULL
ELSE IF CDStretchyExtras.HasMatchProc[l.first.ob] THEN {
rect: CD.Rect = CDInstances.InstRectI[me.ap];
IF ~CDStretchyExtras.Match[l.first.ob, rect, me.ap.ob.layer, class=primary, me.horizontal] THEN near ← far ← FALSE
}
ELSE near ← far ← FALSE;
me.near ← me.near OR near;
me.far ← me.far OR far;
};
ENDLOOP;
RETURN [me.near OR me.far]
END;
MoveApplication: PROC [design: CD.Design, ap: CD.Instance, offset: CD.Position] =
BEGIN
r: CD.Rect ← CDInstances.InstRectO[ap];
ap.location ← CDBasics.AddPoints[ap.location, offset];
RedrawMoved[design, r, offset]
END;
WireTyped: PROC [ob: CD.Object] RETURNS [BOOL] = INLINE {RETURN [ob.class.wireTyped]};
END.