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, December 17, 1986 11:22:51 am PST
DIRECTORY
CD,
CDInstances,
CDCells,
CDCellsInteractions,
CDDirectory,
CDDirectoryOps,
PopUpMenus,
CDEvents,
CDBasics,
CDMarks,
CDOps,
CDProperties,
CDSequencer,
Rope,
TerminalIO;
CDCellsInteractionsImpl: CEDAR PROGRAM
IMPORTS CD, CDCells, CDInstances, CDDirectory, CDDirectoryOps, CDEvents, CDBasics, CDMarks, PopUpMenus, CDOps, CDProperties, CDSequencer, 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"];
};
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.PutRope[" **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.PutRopes[CDDirectory.Name[cell], " included\n"];
RETURN [done←TRUE];
};
TerminalIO.PutRopes[name, " does already exist\nnot accepted, please repeat\n"];
ENDLOOP;
};
CreateCellSelected: PUBLIC PROC [design: CD.Design, name: Rope.ROPE NIL] RETURNS [done: BOOL FALSE, cellInst: CD.Instance ← 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];
};
cellOb: CD.Object;
sel: CD.InstanceList = RemoveSelectedFromWorld[design];
cellInst ← NEW[CD.InstanceRep←[selected: TRUE,
trans: [CDBasics.BaseOfRect[CDInstances.BoundingRectI[sel]], original]
]];
cellInst.ob ← CDCells.CreateCellXTransformed[il: sel, cTrans: cellInst.trans];
cellOb ← cellInst.ob;
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, cellInst: NIL]
};
}
ELSE [] ← CDDirectory.Include[design, cellOb, name];
CDOps.IncludeInstance[design, cellInst, TRUE]; -- redraw removes seletion
[] ← CDEvents.ProcessEvent[createEvent, design, cellOb];
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 ", CDOps.ObjectRope[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.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 ", CDOps.ObjectRope[old], " 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;
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],
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]
}
};
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;
CheckRecursionForPop: PROC [mark: CDMarks.MarkRange] = {
CDMarks.MarkUnMarkedInclusiveChildren[design, currentCell, mark];
recursive ← (originalInst.ob.marked=mark);
};
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];
};
HackSetOwner: PROC [ob: CD.Object, design: CD.Design] = {
CDProperties.PutObjectProp[ob, $OwnerDesign, design.cdDirectoryPriv2];
};
DoReplace: PROC [] = {
needsRes: BOOLFALSE;
currentCP: CD.CellSpecific ← NARROW[currentCell.specific];
originalCP: CD.CellSpecific ← NARROW[originalCell.specific];
[] ← CDEvents.ProcessEvent[beforeReplace, design, originalCell];
TerminalIO.PutRope["replace\n"];
CDProperties.CopyProps[currentCell.properties, originalCell, $Replace];
--HACK for CDDirectory--
HackSetOwner[originalCell, design];
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] = {
IF all THEN TerminalIO.PutRope["new cell and replace all instances\n"]
ELSE TerminalIO.PutRope["new cell for this instance\n"];
CDCells.ToSequenceMode[currentCell];
currentInst.ob ← currentCell;
design^.actual ← design^.actual.rest;
design^.actual.first.specific.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 ~CDCells.IsCell[originalCell] AND all THEN {
CDDirectory.ReplaceObject[design: design, old: originalCell, new: currentCell]
};
CDSequencer.MarkChanged[design];
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 ", CDOps.ObjectRope[originalCell], "\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];
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 {DoNewCell[interactive: FALSE]; RETURN [TRUE]};
CDMarks.DoWithMark[design, CheckRecursionForPop];
IF recursive THEN {
TerminalIO.PutRope[" 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.PutRope[" 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.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, design.name];
IF inst#NIL AND (~done OR inst.ob=NIL) THEN inst ← NIL;
};
Init[];
END.