CDCommandsImpl.mesa (part of Chipndale)
Copyright © 1984, 1983, by Xerox Corporation. All rights reserved.
by Christian Jacobi July 12, 1983 10:56 am
last edited by Christian Jacobi November 5, 1984 10:19:43 am PST
DIRECTORY
CD,
CDApplications,
CDCommands,
CDCommandOps,
CDEvents,
CDBasics,
CDOps,
CDOrient,
CDProperties,
CDRects,
CDStretchyExtras,
CDValue,
Rope USING [IsEmpty, ROPE],
TerminalIO;
CDCommandsImpl: CEDAR PROGRAM
IMPORTS CDApplications, CDBasics, CDCommandOps,
CDEvents, CDOps, CDOrient, CDProperties, CDRects, CDStretchyExtras, CDValue, Rope, TerminalIO
EXPORTS CDCommands =
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.DesignPosition, verbose: BOOL] =
BEGIN
sel: CD.ApplicationPtr ← NIL;
FOR w: CD.ApplicationList ← CDOps.AppList[design], w.rest WHILE w#NIL DO
IF ~w.first.selected THEN {
IF CDApplications.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.ReOrderApplication[design, sel];
CDCommandOps.RedrawApplication[design, sel];
IF verbose THEN CDCommandOps.WriteInfo[sel]
}
ELSE IF verbose THEN {
FOR w: CD.ApplicationList ← CDOps.AppList[design], w.rest WHILE w#NIL DO
IF w.first.selected THEN {
IF CDApplications.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.DesignPosition, verbose: BOOL] =
BEGIN
aptr: CD.ApplicationPtr ← CDApplications.AplicationAt[CDOps.AppList[design], pos, TRUE];
IF aptr#NIL THEN
BEGIN
IF ~aptr.selected THEN ERROR;
aptr.selected ← FALSE;
CDCommandOps.RedrawApplication[design, aptr];
--reorder list, future select op finds next lower application
CDOps.ReOrderApplication[design, aptr];
END;
IF verbose THEN CDCommandOps.WriteInfo[aptr];
END;
BoundingRectAndCount: PROC[list: CD.ApplicationList, selectedOnly: BOOLEANFALSE]
RETURNS [bound: CD.DesignRect�sics.empty, cnt: NAT𡤀] =
BEGIN
FOR tem: LIST OF CD.ApplicationPtr ← list, tem.rest WHILE tem#NIL DO
IF selectedOnly AND NOT tem.first.selected THEN LOOP;
cnt ← cnt+1;
bound ← CDBasics.Surround[bound, CDApplications.ARectO[tem.first]]
ENDLOOP;
END;
DeselectAll: PUBLIC PROC [design: CD.Design] =
BEGIN
doItSingleWise: NAT = 4;
sel: CD.ApplicationList = CDApplications.OnlySelected[CDOps.AppList[design]];
b: CD.DesignRect;
cnt: NAT;
[b, cnt] ← BoundingRectAndCount[list: sel];
CDApplications.DeSelectList[sel];
IF cnt<=doItSingleWise THEN
FOR l: CD.ApplicationList ← sel, l.rest WHILE l#NIL DO
CDCommandOps.RedrawApplication[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 ← CDApplications.BoundingRect[list: CDOps.AppList[design], selectedOnly: FALSE];
FOR w: CD.ApplicationList ← CDOps.AppList[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.DesignRect] =
BEGIN
FOR w: CD.ApplicationList ← CDOps.AppList[design], w.rest WHILE w#NIL DO
IF NOT w.first.selected
AND CDBasics.Intersect[area, CDApplications.ARectI[w.first]] THEN {
w.first.selected ← TRUE;
CDCommandOps.RedrawApplication[design, w.first, FALSE];
};
ENDLOOP;
END;
AreaDeSelectTouched: PROC [design: CD.Design, area: CD.DesignRect] =
BEGIN
FOR w: CD.ApplicationList ← CDOps.AppList[design], w.rest WHILE w#NIL DO
IF w.first.selected
AND CDBasics.Intersect[area, CDApplications.ARectI[w.first]] THEN {
w.first.selected ← FALSE;
CDCommandOps.RedrawApplication[design, w.first];
};
ENDLOOP;
END;
AreaSelectContained: PROC [design: CD.Design, area: CD.DesignRect] =
BEGIN
FOR w: CD.ApplicationList ← CDOps.AppList[design], w.rest WHILE w#NIL DO
IF CDBasics.Inside[CDApplications.ARectI[w.first], area] THEN w.first.selected ← TRUE
ENDLOOP;
CDOps.DelayedRedraw[design, area, FALSE];
END;
AreaDeSelectContained: PROC [design: CD.Design, area: CD.DesignRect] =
BEGIN
FOR w: CD.ApplicationList ← CDOps.AppList[design], w.rest WHILE w#NIL DO
IF CDBasics.Inside[CDApplications.ARectI[w.first], area] THEN w.first.selected ← FALSE
ENDLOOP;
CDOps.DelayedRedraw[design, area];
END;
AreaSelect: PUBLIC PROC [design: CD.Design, area: CD.DesignRect, includePartial: BOOL] =
BEGIN
IF includePartial THEN AreaSelectTouched[design, area]
ELSE AreaSelectContained[design, area];
END;
AreaDeSelect: PUBLIC PROC [design: CD.Design, area: CD.DesignRect, 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.ApplicationList;
[selected: toDelete, others: newContents] ← CDApplications.SplitSelected[CDOps.AppList[design]];
CDOps.SetAppList[design, newContents];
FOR w: CD.ApplicationList ← toDelete, w.rest WHILE w#NIL DO
count ← count+1;
CDCommandOps.RedrawApplication[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;
DeletePointed: PUBLIC PROC [design: CD.Design, pos: CD.DesignPosition, verbose: BOOL] =
BEGIN
app: CD.ApplicationPtr ← CDApplications.AplicationAt[CDOps.AppList[design], pos];
IF app#NIL THEN {
CDOps.RemoveApplication[design, app, TRUE];
RememberShortTime[design, app];
};
IF verbose THEN CDCommandOps.WriteInfo[app];
END;
RedrawMoved: PROC [design: CD.Design, r: CD.DesignRect, offset: CD.DesignPosition] =
BEGIN
moved: CD.DesignRect ← 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.DesignPosition, stretchy: CDCommands.StretchyMode] =
BEGIN
IF stretchy=yes OR ((stretchy=option) AND StretchyMoveMode[design]) THEN StretchyMoveSelected[design, offset]
ELSE SimpleMoveSelected[design, offset]
END;
MovePointed: PUBLIC PROC [design: CD.Design, offset: CD.DesignPosition, pos: CD.DesignPosition, stretchy: CDCommands.StretchyMode] =
BEGIN
IF stretchy=yes OR ((stretchy=option) AND StretchyMoveMode[design]) THEN StretchyMovePointed[design, offset, pos]
ELSE SimpleMovePointed[design, offset, pos]
END;
SimpleMoveSelected: PUBLIC PROC [design: CD.Design, offset: CD.DesignPosition] =
BEGIN
sel: CD.ApplicationList = CDApplications.OnlySelected[CDOps.AppList[design]];
bounding: CD.DesignRect = CDApplications.BoundingRect[sel];
FOR w: CD.ApplicationList ← sel, w.rest WHILE w#NIL DO
w.first.location ← CDBasics.AddPoints[w.first.location, offset];
ENDLOOP;
RedrawMoved[design, bounding, offset]
END;
SimpleMovePointed: PUBLIC PROC [design: CD.Design, offset: CD.DesignPosition, pos: CD.DesignPosition] =
BEGIN
app: CD.ApplicationPtr = CDApplications.AplicationAt[CDOps.AppList[design], pos];
IF app#NIL THEN {
bounding: CD.DesignRect = CDApplications.ARectO[app];
app.location ← CDBasics.AddPoints[app.location, offset];
RedrawMoved[design, bounding, offset]
}
END;
CopySelected: PUBLIC PROC [design: CD.Design, offset: CD.DesignPosition] =
BEGIN
new: CD.ApplicationList←NIL;
sel: CD.ApplicationList = CDApplications.OnlySelected[CDOps.AppList[design]];
bounding: CD.DesignRect ← CDApplications.BoundingRect[sel];
FOR w: CD.ApplicationList ← sel, w.rest WHILE w#NIL DO
aptr: CD.ApplicationPtr ← NEW[CD.Application];
aptr.ob ← w.first.ob;
aptr.location ← CDBasics.AddPoints[w.first.location, offset];
aptr.orientation ← w.first.orientation;
aptr.properties ← CDProperties.CopyProps[w.first.properties];
aptr.selected ← TRUE;
new ← CONS[aptr, new];
w.first.selected ← FALSE;
ENDLOOP;
CDOps.IncludeApplicationList[design, new, FALSE];
RedrawMoved[design, bounding, offset];
END;
CopyPointed: PUBLIC PROC [design: CD.Design, offset: CD.DesignPosition, pos: CD.DesignPosition] =
BEGIN
app: CD.ApplicationPtr = CDApplications.AplicationAt[CDOps.AppList[design], pos];
IF app#NIL THEN {
bounding: CD.DesignRect = CDApplications.ARectO[app];
aptr: CD.ApplicationPtr = NEW[CD.Application];
aptr.ob ← app.ob;
aptr.location ← CDBasics.AddPoints[app.location, offset];
aptr.orientation ← app.orientation;
aptr.properties ← CDProperties.CopyProps[app.properties];
CDOps.IncludeApplication[design, aptr, FALSE];
RedrawMoved[design, bounding, offset];
};
END;
TransformSelected: PUBLIC PROC [design: CD.Design, transform: CD.Orientation] =
BEGIN
DrawIt: PROC[r: CD.Rect] = {CDOps.DelayedRedraw[design, r]};
sel: CD.ApplicationList = CDApplications.OnlySelected[CDOps.AppList[design]];
b: CD.DesignRect = CDApplications.BoundingRect[sel];
FOR w: CD.ApplicationList ← sel, w.rest WHILE w#NIL DO
CDApplications.Transform[w.first, b, transform]
ENDLOOP;
CDOps.DelayedRedraw[design, b];
IF CDOrient.IncludesOddRot90[transform] THEN {
CDBasics.DecomposeRect[r: CDOrient.OrientedRect[b, transform], test: b, outside: DrawIt];
};
END;
TransformPointed: PUBLIC PROC [design: CD.Design, transform: CD.Orientation, pos: CD.DesignPosition] =
BEGIN
app: CD.ApplicationPtr ← CDApplications.AplicationAt[CDOps.AppList[design], pos];
b: CD.DesignRect;
IF app=NIL THEN RETURN;
b ← CDApplications.ARectO[app];
FOR w: CD.ApplicationList ← CDOps.AppList[design], w.rest WHILE w#NIL DO
IF w.first=app THEN {CDApplications.Transform[w.first, b, transform]; EXIT}
ENDLOOP;
CDOps.DelayedRedraw[design, b];
IF CDOrient.IncludesOddRot90[transform] THEN
CDOps.DelayedRedraw[design, CDOrient.OrientedRect[b, transform]];
END;
BaseTransformSelected: PUBLIC PROC [design: CD.Design, transform: CD.Orientation, base: CD.DesignRect] =
BEGIN
sel: CD.ApplicationList = CDApplications.OnlySelected[CDOps.AppList[design]];
b: CD.DesignRect ← CDApplications.BoundingRect[sel];
newB: CD.DesignRect ← CDOrient.MapRect[
itemInCell: b,
cellSize: CDBasics.SizeOfRect[base],
cellInstOrient: transform,
cellInstPos: CDBasics.BaseOfRect[base]
];
FOR w: CD.ApplicationList ← sel, w.rest WHILE w#NIL DO
CDApplications.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[p: REF] =
--helps the garbage collector,
--special may prevent circularities through properties
BEGIN
WITH p SELECT FROM
al: CD.ApplicationList =>
FOR l: CD.ApplicationList ← al, l.rest WHILE l#NIL DO
Forgett[l.first]
ENDLOOP;
app: CD.ApplicationPtr => {app.properties ← NIL; app.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] =
BEGIN
del: REF ← GiveRemembered[design];
IF del=NIL THEN RETURN;
WITH del SELECT FROM
al: CD.ApplicationList => CDOps.IncludeApplicationList[design, al];
app: CD.ApplicationPtr => CDOps.IncludeApplication[design, app]
ENDCASE => NULL;
END;
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Primality: TYPE = {primary, secondary};
ChangeWireLength: PROC [design: CD.Design, aptr: CD.ApplicationPtr, amount: CD.DesignNumber] =
BEGIN
--??? for now CDRects.CreateRect is ONLY method to create wires
sz: CD.DesignPosition = CDBasics.SizeOfRect[aptr.ob.p.insideRect[aptr.ob]];
r: CD.DesignRect ← CDApplications.ARectO[aptr];
newOb: CD.ObPtr←NIL;
IF NOT WireTyped[aptr.ob] THEN ERROR;
newOb ← CDRects.CreateRect[CD.DesignPosition[x: sz.x, y: sz.y+amount], aptr.ob.level];
IF newOb=NIL THEN RETURN;
IF aptr.ob.p#newOb.p THEN ERROR;
--this checks if old object was created with CDRects.CreateRect
aptr.ob ← newOb;
r ← CDBasics.Surround[r, CDApplications.ARectO[aptr]];
CDOps.DelayedRedraw[design, r];
END;
StretchyMoveSP: PROC[design: CD.Design, offset: CD.DesignPosition, selectedApps, nonSelectedApps: CD.ApplicationList] =
BEGIN
primApps: CD.ApplicationList ← NIL;
primList, secondList, nonPrimList: StretchList ← NIL;
rect: CD.DesignRect;
rect ← CDApplications.BoundingRect[selectedApps];
FOR l: CD.ApplicationList ← nonSelectedApps, l.rest WHILE l#NIL DO
IF WireTyped[l.first.ob] THEN {
me: REF StretchRec←NEW[StretchRec←StretchRec[
ap: l.first,
conductRect: CDApplications.ARectI[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.ApplicationList ← selectedApps, l.rest WHILE l#NIL DO
MoveApplication[design, l.first, offset]
ENDLOOP;
END;
StretchyMoveSelected: PROC[design: CD.Design, offset: CD.DesignPosition] =
BEGIN
selectedApps, nonSelectedApps: CD.ApplicationList ← NIL;
[selectedApps, nonSelectedApps] ← CDApplications.SplitSelected[CDOps.AppList[design]];
StretchyMoveSP[design, offset, selectedApps, nonSelectedApps]
END;
StretchyMovePointed: PROC[design: CD.Design, offset: CD.DesignPosition, pos: CD.DesignPosition] =
BEGIN
selectedApps, nonSelectedApps: CD.ApplicationList ← NIL;
[selectedApps, nonSelectedApps] ← CDApplications.SplitPointed[CDOps.AppList[design], pos];
StretchyMoveSP[design, offset, selectedApps, nonSelectedApps]
END;
StretchList: TYPE = LIST OF REF StretchRec;
StretchRec: TYPE = RECORD [
ap: CD.ApplicationPtr,
conductRect: CD.DesignRect, -- 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.DesignPosition, p: Primality] =
BEGIN
--??? change slow case distinction, but now better is easy to understand
move: CD.DesignPosition←offset;
IF ~wire.near AND ~wire.far THEN RETURN;
IF p#primary THEN {IF wire.horizontal THEN move.y𡤀 ELSE move.x𡤀};
IF wire.near AND ~wire.far THEN {
IF p#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.ApplicationList, p: Primality] RETURNS [BOOL] =
-- checks weather me has some match with any of list
BEGIN
near, far: BOOL;
nearEdge, farEdge: CD.DesignRect;
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.ApplicationList ← list, l.rest WHILE l#NIL DO
r: CD.DesignRect = CDApplications.ARectI[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.level#me.ap.ob.level THEN near ← far ← FALSE
ELSE IF p#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.DesignRect = CDApplications.ARectI[me.ap];
IF ~CDStretchyExtras.Match[l.first.ob, rect, me.ap.ob.level, p=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.ApplicationPtr, offset: CD.DesignPosition] =
BEGIN
r: CD.DesignRect ← CDApplications.ARectO[ap];
ap.location ← CDBasics.AddPoints[ap.location, offset];
RedrawMoved[design, r, offset]
END;
WireTyped: PROC [ob: CD.ObPtr] RETURNS [BOOL] = INLINE {RETURN [ob.p.wireTyped]};
END.