CDCellsInteractionsImpl.mesa (part of ChipNDale)
Copyright © 1983, 1986, 1987 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, June 24, 1983 5:00 pm
Last Edited by: Christian Jacobi, May 21, 1987 11:39:26 am PDT
DIRECTORY
CD,
CDInstances,
CDCells,
CDCellsInteractions,
CDDirectory,
CDDirectoryOps,
PopUpMenus,
CDEvents,
CDBasics,
CDOps,
CDProperties,
CDSequencer,
IO,
Process USING [PauseMsec],
RefTab,
Rope,
TerminalIO;
CDCellsInteractionsImpl: CEDAR PROGRAM
IMPORTS CD, CDCells, CDInstances, CDDirectory, CDDirectoryOps, CDEvents, CDBasics, PopUpMenus, CDOps, CDProperties, CDSequencer, IO, Process, RefTab, Rope, TerminalIO
EXPORTS CDCellsInteractions
SHARES CD, CDDirectory =
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"];
};
GetNameForCell: PROC [design: CD.Design, cell: CD.Object] RETURNS [abort: BOOLFALSE] = {
name: Rope.ROPE;
TerminalIO.PutRope["cell created\n"];
DO
name ← TerminalIO.RequestRope["enter name for cell: "
! TerminalIO.UserAbort => GOTO Oops
];
IF Rope.IsEmpty[name] THEN {
TerminalIO.PutRope[" new cell is not included in directory\n"];
RETURN;
};
IF CDDirectory.Include[design, cell, name, FALSE] THEN {
TerminalIO.PutRopes[" cell named ", name, " and included in directory\n"];
RETURN;
};
TerminalIO.PutRopes[name, " does already exist\nnot accepted, please repeat\n"];
ENDLOOP;
EXITS Oops => abort ← TRUE
};
CreateCellSelected: PUBLIC PROC [design: CD.Design, interactiveName: BOOL] RETURNS [done: BOOLFALSE, cellInst: CD.Instance ← NIL] = {
selected, others: CD.InstanceList;
[selected: selected, others: others] ← CDInstances.SplitSelected[CDOps.InstList[design]];
IF selected=NIL THEN {
TerminalIO.PutRope["no selection! "];
RETURN [done: FALSE, cellInst: NIL];
};
cellInst ← NEW[CD.InstanceRep←[selected: TRUE,
trans: [CDBasics.BaseOfRect[CDInstances.BoundingRectI[selected]], original]
]];
cellInst.ob ← CDCells.CreateCellXTransformed[il: selected, cTrans: cellInst.trans];
IF interactiveName THEN
IF GetNameForCell[design: design, cell: cellInst.ob].abort THEN RETURN [FALSE, NIL];
CDOps.SetInstList[design, others];
CDOps.IncludeInstance[design, cellInst, TRUE]; -- redraw removes seletion
[] ← CDEvents.ProcessEvent[createEvent, design, cellInst.ob];
RETURN [done: TRUE, cellInst: cellInst]
};
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.PutRope[" no object\n"];
inst.ob=NIL OR inst.ob.specific=NIL => TerminalIO.PutRope[" bad object\n"];
ISTYPE[inst.ob.specific, CD.CellSpecific] => yes ← TRUE;
ENDCASE => TerminalIO.PutRopes[" object is not cell but ", CD.Describe[inst.ob, inst.properties, design], "\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;
};
desc: Rope.ROPE;
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.ExpandRecursed[old, design, design];
IF ob2=NIL OR ob2=old THEN {TerminalIO.PutRope["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.CellSpecific = NARROW[old.specific];
newCp: CD.CellSpecific = NARROW[dummy.specific];
dummyCellInst: CD.Instance;
dummy.bbox ← CDBasics.universe;
IF CheckRecursionForPush[design, old] THEN {
TerminalIO.PutRopes[" recursive push into ", CD.Describe[old, NIL, design], " not possible\n"];
RETURN [FALSE];
};
newCp^ ← oldCp^; --copies interest rect ....
newCp.ir ← CDBasics.MapRect[oldCp.ir, inst.trans];
newCp.contents ← ComposeToList[oldCp.contents, oldCp.sequence, inst.trans];
newCp.sequence ← NIL;
newCp.dummyCell ← TRUE;
desc ← IF CDDirectory.IsIncluded[design, inst.ob]
THEN desc ← CDDirectory.Name[inst.ob, design]
ELSE IO.PutFR["{not in directory [%g]}", [rope[CD.Describe[inst.ob, inst.properties, design]]]];
dummyCellInst ← NEW[CD.InstanceRep←[
ob: dummy,
trans: [],
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, desc: desc],
design^.actual
];
[] ← CDEvents.ProcessEvent[pushEvent, design];
PushForUnDo[design];
RETURN [done ← TRUE];
}
};
ComposeToList: PROC [il: CD.InstanceList←NIL, seq: CD.InstanceSequence←NIL, trans: CD.Transformation] RETURNS [cl: CD.InstanceList ← NIL] = {
FOR l: CD.InstanceList ← il, l.rest WHILE l#NIL DO
cl ← CONS[CDInstances.Composed[l.first, trans], cl];
ENDLOOP;
IF seq#NIL THEN
FOR n: NAT IN [0..seq.length) DO
cl ← CONS[CDInstances.Composed[seq[n], trans], cl];
ENDLOOP;
};
PushForUnDo: PROC [design: CD.Design] = {
--pushes undo state
old: CD.PropRef ← design.unDoBuffers;
design.unDoBuffers ← CD.InitPropRef[];
CDProperties.PutProp[design.unDoBuffers, $pushed, old];
};
PopForUnDo: PROC [design: CD.Design] = {
--rebuild undo state from before push
--(You can't undo pop)
new: CD.PropRef ← NIL;
old: CD.PropRef ← design.unDoBuffers;
IF old#NIL THEN
WITH CDProperties.GetListProp[old^, $pushed] SELECT FROM
used: CD.PropRef => new ← used
ENDCASE => NULL;
design.unDoBuffers ← (IF new#NIL THEN new ELSE CD.InitPropRef[]);
};
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];
PopForUnDo[design]
}
};
IncludeChildrenTransitive: PROC [ob: CD.Object, table: RefTab.Ref←NIL] = {
--Include children (transitive) of ob into table
--doesn't include ob itself
--stops including child's children if child is already included
EachChild: CDDirectory.EachObjectProc = {
IF me.class.composed THEN {
IF RefTab.Insert[table, me, $x] THEN IncludeChildrenTransitive[me, table];
};
};
IF table=NIL THEN table ← RefTab.Create[];
[] ← CDDirectory.EnumerateChildObjects[ob, EachChild, table];
};
IPopFromCell: PROC [design: CD.Design, m: CDCellsInteractions.Method, name: Rope.ROPE] RETURNS [done: BOOL�LSE] = {
currentInst, originalInst: CD.Instance;
currentCell, originalCell: CD.Object;
recursive: BOOLFALSE;
CheckRecursion: PROC [] RETURNS [BOOL] = {
table: RefTab.Ref ← RefTab.Create[];
IncludeChildrenTransitive[currentCell, table];
RETURN [ RefTab.Fetch[table, originalInst.ob].found ]
};
RemoveRemaindsFromExpands: 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 [] = {
TerminalIO.PutRope["flush\n"];
design^.actual ← design^.actual.rest;
CDOps.IncludeInstance[design, originalInst, FALSE];
RemoveRemaindsFromExpands[design, originalInst];
};
DoReplace: PROC [] = {
needsRes: BOOLFALSE;
currentCP: CD.CellSpecific ← NARROW[currentCell.specific];
originalCP: CD.CellSpecific ← NARROW[originalCell.specific];
IF originalCell.immutable THEN {
TerminalIO.PutRope["cant change an immutable object\n"];
DoFlush[];
};
[] ← CDEvents.ProcessEvent[beforeReplace, design, originalCell];
TerminalIO.PutRope["replace\n"];
CDProperties.CopyProps[currentCell.properties, originalCell, $Replace];
--HACK for CDDirectory--
CDDirectory.SetOwner[design, originalCell, TRUE];
design^.actual ← design^.actual.rest;
originalCP.contents ← currentCP.contents;
originalCP.sequence ← currentCP.sequence;
originalCP.ir ← currentCP.ir;
originalCP.specifiedIr ← currentCP.specifiedIr;
CDCells.ToSequenceMode[originalCell];
[] ← CDCells.ResizeCell[design, originalCell];
CDOps.IncludeInstance[design, originalInst, FALSE];
[] ← CDEvents.ProcessEvent[afterReplace, design, originalCell];
CDDirectory.PropagateChange[originalCell, design];
CDSequencer.MarkChangedIOOnly[design];
};
DoNewCell: PROC [interactive: BOOLFALSE, all: BOOLFALSE] RETURNS [done: BOOLTRUE] = {
oldName: Rope.ROPE ← CDDirectory.Name[originalCell, design];
IF all
THEN TerminalIO.PutRope["new cell and replace all instances\n"]
ELSE TerminalIO.PutRope["new cell for this instance\n"];
IF design.mutability#findOut THEN CDSequencer.MarkChanged[design];
CDCells.ToSequenceMode[currentCell];
IF interactive AND (~all OR oldName=NIL) THEN {
IF GetNameForCell[design, currentCell].abort THEN RETURN [done←FALSE];
};
currentInst.ob ← currentCell;
design^.actual ← design^.actual.rest;
design^.actual.first.specific.changed ← TRUE;
CDOps.IncludeInstance[design, currentInst, FALSE];
[] ← CDEvents.ProcessEvent[createEvent, design, currentCell];
CDDirectory.PropagateChange[currentCell, design];
IF all THEN {
IF oldName#NIL AND ~originalCell.class.xDesign THEN
[] ← CDDirectory.Rename[design: design, object: currentCell, newName: oldName, fiddle: FALSE, removeFirst: TRUE];
CDDirectory.ReplaceObject[design: design, old: originalCell, new: currentCell]
};
RemoveRemaindsFromExpands[design, originalInst]
};
menu: PopUpMenus.Menu ← fullPopMenu;
IF design^.actual.rest=NIL THEN {
TerminalIO.PutRope["not in cell\n"];
RETURN [FALSE]
};
originalInst ← design^.actual.first.mightReplace;
originalCell ← originalInst.ob;
TerminalIO.PutRopes["Pop from ", CD.Describe[originalCell, originalInst.properties, design], "\n"];
currentCell ← CDCells.CreateCellXTransformed[
il: CDOps.InstList[design],
ir: (IF design^.actual.first.specific.specifiedIr THEN design^.actual.first.specific.ir ELSE [0, 0, -1, -1]),
cTrans: originalInst.trans];
WITH originalCell.specific SELECT FROM
cp: CD.CellSpecific => {
curCp: CD.CellSpecific ← NARROW[currentCell.specific];
curCp.simplifyOn ← cp.simplifyOn;
curCp.drawBorder ← cp.drawBorder;
};
ENDCASE => NULL;
currentInst ← NEW[CD.InstanceRep ← [ob: currentCell, trans: originalInst.trans, 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.specific.changed) THEN {
DoFlush[]; RETURN [TRUE]
};
IF CDBasics.NonEmpty[currentCell.bbox] THEN {
IF m=newcell THEN RETURN [DoNewCell[interactive: FALSE]];
IF ~originalCell.immutable THEN recursive ← CheckRecursion[];--else check not necessary
IF recursive THEN {
TerminalIO.PutRope[" Original cell used inside, replace not possible\n"];
IF m=replace THEN RETURN [DoNewCell[interactive: FALSE, all: FALSE]];
menu ← partialPopMenu;
}
ELSE IF ~CDCells.IsCell[originalCell] OR originalCell.immutable THEN {
IF m=replace THEN RETURN [DoNewCell[interactive: FALSE, all: TRUE]];
menu ← expandPopMenu;
}
ELSE { --ok, normal case
IF m=replace THEN {
DoReplace[];
RETURN [TRUE]
};
}
}
ELSE { -- empty
TerminalIO.PutRope[" create empty cell not possible\n"];
IF m#interactive THEN {DoFlush[]; RETURN [TRUE]};
menu ← emptyPopMenu;
};
Process.PauseMsec[50];
SELECT PopUpMenus.Call[menu] FROM
$flush => DoFlush[];
$replace => DoReplace[];
$new => RETURN [DoNewCell[interactive: TRUE, all: FALSE]];
$newAll => RETURN [DoNewCell[interactive: TRUE, all: TRUE]];
ENDCASE => {TerminalIO.PutRope["skipped\n"]; RETURN [FALSE]};
RETURN [TRUE];
};
PopToTopLevel: PUBLIC PROC [design: CD.Design] = {
WHILE CDCells.IsPushedIn[design] DO
[] ← PopFromCell[design, newcell, "--wasPushedIn--"];
ENDLOOP;
};
MakeTopInstance: PUBLIC PROC [design: CD.Design] RETURNS [inst: CD.Instance←NIL] = {
done: BOOL; il: CD.InstanceList;
PopToTopLevel[design];
il ← CDOps.InstList[design];
IF il=NIL THEN RETURN [NIL];
IF il.rest=NIL THEN RETURN [il.first];
FOR w: CD.InstanceList ← il, w.rest WHILE w#NIL DO
w.first.selected ← TRUE
ENDLOOP;
[done, inst] ← CreateCellSelected[design, FALSE];
[] ← CDDirectory.Include[design, inst.ob, CD.DesignName[design]];
IF inst#NIL AND (~done OR inst.ob=NIL) THEN inst ← NIL;
};
Init[];
END.