CDCellsInteractionsImpl.mesa (part of ChipNDale)
Copyright © 1983, 1986 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, June 24, 1983 5:00 pm
Last Edited by: Christian Jacobi, September 1, 1986 4:40:27 pm PDT
DIRECTORY
CD,
CDInstances,
CDCells,
CDCellsInteractions,
CDDirectory,
CDDirectoryOps,
PopUpMenus,
CDEvents,
CDBasics,
CDMarks,
CDOps,
CDOrient,
CDProperties,
CDSequencer,
CDSequencerExtras,
Rope,
TerminalIO;
CDCellsInteractionsImpl: CEDAR PROGRAM
IMPORTS CDCells, CDInstances, CDDirectory, CDDirectoryOps, CDEvents, CDBasics, CDMarks, PopUpMenus, CDOps, CDOrient, CDProperties, CDSequencer, CDSequencerExtras, Rope, TerminalIO
EXPORTS CDCellsInteractions
SHARES CD =
BEGIN
EventRegistration: TYPE = CDEvents.EventRegistration;
beforeReplace: EventRegistration = CDEvents.RegisterEventType[$BeforeCellReplacement];
afterReplace: EventRegistration = CDEvents.RegisterEventType[$AfterCellReplacement];
pushEvent: EventRegistration = CDEvents.RegisterEventType[$AfterPush];
popEvent: EventRegistration = CDEvents.RegisterEventType[$AfterPop];
createEvent: EventRegistration = CDEvents.RegisterEventType[$InteractiveCreatedCell];
fullPopMenu: PopUpMenus.Menu = PopUpMenus.Create["Pop from cell", "select menu entry to go back outside"];
partialPopMenu: PopUpMenus.Menu = PopUpMenus.Create["Pop from cell", "replace is not possible! it would create recursion"];
emptyPopMenu: PopUpMenus.Menu = PopUpMenus.Create["Pop from cell: empty cell", "cell is empty; you can not create an empty cell"];
expandPopMenu: PopUpMenus.Menu = PopUpMenus.Create["Pop from expanded object"];
expandResultKey: REF = NEW[INT];
Init: PROC [] = {
[] ← PopUpMenus.Entry[fullPopMenu, "flush", NIL, $flush, "undo all modifications of cell"];
[] ← PopUpMenus.Entry[partialPopMenu, "flush", NIL, $flush, "undo all modifications of cell"];
[] ← PopUpMenus.Entry[emptyPopMenu, "flush", NIL, $flush, "undo all modifications of cell"];
[] ← PopUpMenus.Entry[fullPopMenu, "new cell", NIL, $new, "create a new cell; original cell is unmodified"];
[] ← PopUpMenus.Entry[partialPopMenu, "new cell", NIL, $new, "create a new cell; original cell is unmodified"];
[] ← PopUpMenus.Entry[fullPopMenu, "replace", NIL, $replace, "make edits permanent in all instances of cell"];
[] ← PopUpMenus.Entry[expandPopMenu, "flush", NIL, $flush, "undo all modifications of cell"];
[] ← PopUpMenus.Entry[expandPopMenu, "new cell this instance", NIL, $new, "create a new cell; original object is unmodified"];
[] ← PopUpMenus.Entry[expandPopMenu, "new cell and replace all instances", NIL, $newAll, "tries to forget original object"];
};
CreateCellObject: PROC [use: CD.InstanceList, orient: CD.Orientation ← CDOrient.original] RETURNS [CD.Object] = {
-- not yet included in design
ob: CD.Object = CDCells.CreateEmptyCell[];
cp: CD.CellPtr = NARROW[ob.specificRef];
gOutR: CD.Rect = CDInstances.BoundingRectO[use]; -- coordsys of use, non oriented
ob.size ← CDOrient.OrientedSize[CDBasics.SizeOfRect[gOutR], orient];
cp.contents ← CDInstances.DeComposedList[use, CDBasics.BaseOfRect[gOutR], ob.size, orient];
cp.ir ← cp.dIr ← CDInstances.BoundingRectI[cp.contents];
cp.simplifyOn ← 50.0/MAX[ob.size.y, 1];
RETURN [ob]
};
IncludeAndNameCell: PROC [design: CD.Design, cell: CD.Object, interactive: BOOLTRUE, allowAbort: BOOLFALSE] RETURNS [done: BOOL] = {
IF ~interactive THEN {
[] ← CDDirectory.Include[design, cell];
RETURN [done←TRUE];
};
--interactive
DO
fiddle: BOOLFALSE; aborted: BOOLFALSE; name: Rope.ROPE;
name ← TerminalIO.RequestRope["enter object name: "
! TerminalIO.UserAbort => {aborted ← TRUE; fiddle ← TRUE; CONTINUE}
];
IF aborted THEN {
TerminalIO.WriteRope[" **name input aborted\n"];
IF allowAbort THEN RETURN [done←FALSE];
};
IF Rope.IsEmpty[name] THEN {
fiddle ← TRUE;
name ← "-no name";
};
IF Rope.Fetch[name, name.Length[]-1]='@ THEN {
fiddle ← TRUE;
name ← Rope.Substr[name, 0, name.Length[]-1];
};
IF CDDirectory.Include[design, cell, name, fiddle] THEN {
TerminalIO.WriteRopes[CDDirectory.Name[cell], " included\n"];
RETURN [done←TRUE];
};
TerminalIO.WriteRopes[name, " does already exist\nnot accepted, please repeat\n"];
ENDLOOP;
};
CreateCellSelected: PUBLIC PROC [design: CD.Design, name: Rope.ROPE NIL] RETURNS [done: BOOL FALSE, cellOb: CD.Object ← NIL] = {
RemoveSelectedFromWorld: PROC [design: CD.Design] RETURNS [remove: CD.InstanceList] = {
-- removes the selected instances from design and returns them
keep: CD.InstanceList;
[others: keep, selected: remove] ← CDInstances.SplitSelected[CDOps.InstList[design]];
CDOps.SetInstList[design, keep];
};
sel: CD.InstanceList = RemoveSelectedFromWorld[design];
inst: CD.Instance = NEW[CD.InstanceRep];
b: CD.Rect = CDInstances.BoundingRectO[sel];
cp: CD.CellPtr;
inst.ob ← cellOb ← CreateCellObject[use: sel];
cp ← NARROW[cellOb.specificRef];
cp.name ← name;
inst.location ← CDBasics.BaseOfRect[b];
inst.selected ← TRUE;
IF NOT CDBasics.NonEmpty[b] THEN {
TerminalIO.WriteRope["no empty cell\n"];
RETURN [done: FALSE, cellOb: NIL]
};
IF name=NIL THEN {
IF ~IncludeAndNameCell[design: design, cell: cellOb, allowAbort: TRUE, interactive: TRUE].done THEN {
--undo the command
CDOps.IncludeInstanceList[design, sel, FALSE];
RETURN [done: FALSE, cellOb: NIL]
};
}
ELSE [] ← CDDirectory.Include[design, cellOb];
CDOps.IncludeInstance[design, inst, TRUE]; -- redraw removes seletion
[] ← CDEvents.ProcessEvent[createEvent, design, cellOb];
RETURN [done: TRUE, cellOb: cellOb]
};
PushInCellInstance: PUBLIC PROC [design: CD.Design, inst: CD.Instance, convertIfNecessary: BOOLTRUE] RETURNS [done: BOOL FALSE] = {
IsCellInstance: PROC [inst: CD.Instance] RETURNS [yes: BOOL FALSE] = {
--verbose if inst is not a cell
SELECT TRUE FROM
inst=NIL => TerminalIO.WriteRope[" no object\n"];
inst.ob=NIL OR inst.ob.specificRef=NIL => TerminalIO.WriteRope[" bad object\n"];
ISTYPE[inst.ob.specificRef, CD.CellPtr] => yes ← TRUE;
ENDCASE => TerminalIO.WriteRopes[" object is not cell but ", CDOps.ObjectInfo[inst.ob], "\n"];
};
CheckRecursionForPush: PROC [design: CD.Design, ob: CD.Object] RETURNS [yes: BOOLFALSE] = {
--Returns "pushing into ob would cause recursion"
FOR l: LIST OF CD.PushRec ← design.actual, l.rest WHILE l#NIL DO
IF l.first.mightReplace#NIL AND l.first.mightReplace.ob=ob THEN RETURN [yes ← TRUE]
ENDLOOP;
};
old: CD.Object ← inst.ob; --the original object; or if not a cell: the original conversion
WHILE old#NIL AND ~CDCells.IsCell[old] AND convertIfNecessary DO
ob2: CD.Object ← CDDirectory.ExpandComplete[old, design, design];
IF ob2=NIL OR ob2=old THEN {TerminalIO.WriteRope["expand failed\n"]; RETURN [FALSE]};
old ← ob2;
CDProperties.PutProp[inst, expandResultKey, ob2];
ENDLOOP;
IF CDCells.IsCell[old] THEN {
dummy: CD.Object = CDCells.CreateEmptyCell[];
oldCp: CD.CellPtr = NARROW[old.specificRef];
newCp: CD.CellPtr = NARROW[dummy.specificRef];
dummyCellInst: CD.Instance;
dummy.size ← CDBasics.highposition;
IF CheckRecursionForPush[design, old] THEN {
TerminalIO.WriteRopes[" recursive push into ", CDOps.ObjectInfo[old], " not possible\n"];
RETURN [FALSE];
};
newCp^ ← oldCp^; --copies interest rect ....
newCp.ir ← CDOrient.MapRect[
itemInCell: oldCp.ir,
cellSize: inst.ob.size,
cellInstOrient: inst.orientation,
cellInstPos: inst.location
];
newCp.dIr ← CDOrient.MapRect[
itemInCell: oldCp.dIr,
cellSize: inst.ob.size,
cellInstOrient: inst.orientation,
cellInstPos: inst.location
];
newCp.contents ← CDInstances.ComposedList[
il: newCp.contents,
cellPos: inst.location,
cellSize: inst.ob.size,
cellOrient: inst.orientation
];
dummyCellInst ← NEW[CD.InstanceRep←[
ob: dummy,
selected: TRUE,
properties: CDProperties.DCopyProps[inst.properties]
]];
CDProperties.CopyProps[inst.ob.properties, dummy];
CDOps.RemoveInstance[design, inst];
design^.actual ← CONS[
CD.PushRec[dummyCell: dummyCellInst, specific: newCp, mightReplace: inst],
design^.actual
];
CDProperties.PutObjectProp[dummy, $dummyCell, $TRUE];
[] ← CDEvents.ProcessEvent[pushEvent, design];
RETURN [done ← TRUE];
}
};
PopFromCell: PUBLIC PROC [design: CD.Design, m: CDCellsInteractions.Method←interactive, name: Rope.ROPENIL] RETURNS [done: BOOL] = {
done ← IPopFromCell[design, m, name];
IF done THEN [] ← CDEvents.ProcessEvent[popEvent, design];
};
IPopFromCell: PROC [design: CD.Design, m: CDCellsInteractions.Method, name: Rope.ROPE] RETURNS [done: BOOL�LSE] = {
currentRect, originalRect: CD.Rect; --cd coords
currentInst, originalInst: CD.Instance;
currentCell, originalCell: CD.Object;
currentCP: CD.CellPtr;
recursive: BOOLFALSE;
CheckRecursionForPop: PROC [mark: CDMarks.MarkRange] = {
CDMarks.MarkUnMarkedInclusiveChildren[design, currentCell, mark];
recursive ← (originalInst.ob.marked=mark);
};
RemoveExpandedStuff: PROC [design: CD.Design, originalInst: CD.Instance] = {
--if originalInst wasn't a cell, tries to remove results of original expansion
IF ~CDCells.IsCell[originalInst.ob] THEN
WITH CDProperties.GetProp[originalInst, expandResultKey] SELECT FROM
ob: CD.Object => {
CDProperties.PutProp[originalInst, expandResultKey, NIL];
[] ← CDDirectoryOps.RemoveIfUnused[design, ob];
};
ENDCASE => NULL;
};
DoFlush: PROC [] = {
b: BOOL ← design^.actual.first.indirectlyChanged;
TerminalIO.WriteRope["flush\n"];
design^.actual ← design^.actual.rest;
design^.actual.first.indirectlyChanged ← TRUE;
IF b THEN design^.actual.first.indirectlyChanged ← TRUE;
CDOps.IncludeInstance[design, originalInst, FALSE];
RemoveExpandedStuff[design, originalInst];
};
HackSetOwner: PROC [ob: CD.Object, design: CD.Design] = {
CDProperties.PutObjectProp[ob, $OwnerDesign, design.reserved];
};
Offset: PROC [newSize, oldSize: CD.Position] RETURNS [offset: CD.Position] = {
fakeOrigin: CD.Position; --world coordinates
fakeOrigin ← CDOrient.MapPoint[
pointInCell: [0, 0],
cellSize: newSize,
cellInstOrient: currentInst.orientation,
cellInstPos: currentInst.location
].pointInWorld;
offset ← CDOrient.DeMapPoint[
pointInWorld: fakeOrigin,
cellSize: oldSize,
cellInstOrient: originalInst.orientation,
cellInstPos: originalInst.location
].pointInCell;
};
DoReplace: PROC [] = {
needsRep: BOOLFALSE;
originalCP: CD.CellPtr ← NARROW[originalCell.specificRef];
[] ← CDEvents.ProcessEvent[beforeReplace, design, originalCell];
TerminalIO.WriteRope["replace\n"];
CDProperties.CopyProps[currentCell.properties, originalCell, $Replace];
--HACK for CDDirectory--
HackSetOwner[originalCell, design];
design^.actual ← design^.actual.rest;
design^.actual.first.indirectlyChanged ← TRUE;
needsRep ← originalRect#currentRect OR originalCP.ir#currentCP.ir OR currentCP.dIr#originalCP.dIr OR originalCP.useDIr#currentCP.useDIr;
originalCP.contents ← currentCP.contents;
originalCP.useDIr ← currentCP.useDIr;
originalCP.ir ← currentCP.ir;
IF needsRep THEN {
oldSize: CD.Position ← originalCell.size;
newSize: CD.Position ← CDOrient.OrientedSize[CDBasics.SizeOfRect[currentRect], originalInst.orientation];
offset: CD.Position ← Offset[newSize, oldSize];
originalCell.size ← newSize;
originalCP.origin ← CDBasics.SubPoints[originalCP.origin, offset];
IF ~originalCP.useDIr THEN
originalCP.ir ← CDBasics.MoveRect[originalCP.ir, CDBasics.NegOffset[offset]];
CDDirectory.RepositionObject[design: design,
ob: originalCell,
oldSize: oldSize,
baseOff: offset
];
};
originalInst.location ← currentInst.location;
CDOps.IncludeInstance[design, originalInst, FALSE];
[] ← CDEvents.ProcessEvent[afterReplace, design, originalCell];
CDDirectory.PropagateChange[originalCell, design];
CDSequencerExtras.MarkChangedIOOnly[design];
};
DoNewCell: PROC [interactive: BOOLFALSE, all: BOOLFALSE] = {
IF all THEN TerminalIO.WriteRope["new cell and replace all instances\n"]
ELSE TerminalIO.WriteRope["new cell for this instance\n"];
currentInst.ob ← currentCell;
design^.actual ← design^.actual.rest;
design^.actual.first.indirectlyChanged ← TRUE;
design^.actual.first.changed ← TRUE;
IF ~IncludeAndNameCell[design, currentCell, interactive, FALSE].done THEN ERROR;
CDOps.IncludeInstance[design, currentInst, FALSE];
[] ← CDEvents.ProcessEvent[createEvent, design, currentCell];
CDDirectory.PropagateChange[currentCell, design];
IF ~currentCP.useDIr THEN {
offset: CD.Position ← Offset[currentCell.size, originalCell.size];
currentCP.ir ← CDBasics.MoveRect[currentCP.ir, CDBasics.NegOffset[offset]];
};
IF ~CDCells.IsCell[originalCell] AND all THEN {
CDDirectory.ReplaceObject[design: design, old: originalCell, new: currentCell]
};
CDSequencer.MarkChanged[design];
CDProperties.PutObjectProp[currentCell, $dummyCell, NIL];
RemoveExpandedStuff[design, originalInst]
};
menu: PopUpMenus.Menu ← fullPopMenu;
IF design^.actual.rest=NIL THEN {
TerminalIO.WriteRope["not in cell\n"];
RETURN [FALSE]
};
originalInst ← design^.actual.first.mightReplace;
originalCell ← originalInst.ob;
originalRect ← CDOrient.RectAt[originalInst.location, originalCell.size, originalInst.orientation];
TerminalIO.WriteRopes["Pop from ", CDOps.ObjectInfo[originalCell], "\n"];
CDInstances.DeSelectList[CDOps.InstList[design]];
currentRect ← CDInstances.BoundingRectO[CDOps.InstList[design]]; -- design coordinates
currentCell ← CreateCellObject[CDOps.InstList[design], originalInst.orientation];
currentCP ← NARROW[currentCell.specificRef];
currentCP.useDIr ← design^.actual.first.specific.useDIr;
IF ~currentCP.useDIr THEN
currentCP.ir ← CDOrient.DeMapRect[
itemInWorld: design^.actual.first.specific.ir,
cellSize: originalCell.size,
cellInstOrient: originalInst.orientation,
cellInstPos: originalInst.location
].itemInCell;
currentInst ← NEW[CD.InstanceRep ← [
ob: currentCell,
location: CDBasics.BaseOfRect[currentRect],
orientation: originalInst.orientation,
selected: TRUE,
properties: CDProperties.DCopyProps[design^.actual.first.dummyCell.properties]
]];
CDProperties.CopyProps[design^.actual.first.dummyCell.ob.properties, currentCell];
IF m=flush OR (m=interactive AND ~design^.actual.first.changed) THEN {
DoFlush[]; RETURN [TRUE]
};
IF CDBasics.NonEmpty[currentRect] THEN {
IF m=newcell THEN {DoNewCell[interactive: FALSE]; RETURN [TRUE]};
CDMarks.DoWithMark[design, CheckRecursionForPop];
IF recursive THEN {
TerminalIO.WriteRope[" Original cell used inside, replace not possible\n"];
IF m=replace THEN {DoNewCell[interactive: FALSE, all: FALSE]; RETURN [TRUE]};
menu ← partialPopMenu;
}
ELSE IF ~CDCells.IsCell[originalCell] THEN {
IF m=replace THEN {DoNewCell[interactive: FALSE, all: TRUE]; RETURN [TRUE]};
menu ← expandPopMenu;
}
ELSE { --ok, normal case
IF m=replace THEN {
DoReplace[];
RETURN [TRUE]
};
}
}
ELSE { -- empty
TerminalIO.WriteRope[" create empty cell not possible\n"];
IF m#interactive THEN {DoFlush[]; RETURN [TRUE]};
menu ← emptyPopMenu;
};
SELECT PopUpMenus.Call[menu] FROM
$flush => DoFlush[];
$replace => DoReplace[];
$new => DoNewCell[interactive: TRUE, all: FALSE];
$newAll => DoNewCell[interactive: TRUE, all: TRUE];
ENDCASE => {TerminalIO.WriteRope["skipped\n"]; RETURN [FALSE]};
RETURN [TRUE];
};
Init[];
END.