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:
BOOL←
FALSE] = {
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:
BOOL ←
FALSE, 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:
BOOL ←
TRUE]
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:
BOOL ←
FALSE] = {
--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.
ROPE←
NIL]
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:
BOOLLSE] = {
currentInst, originalInst: CD.Instance;
currentCell, originalCell: CD.Object;
recursive: BOOL ← FALSE;
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: BOOL ← FALSE;
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:
BOOL←
FALSE, all:
BOOL←
FALSE]
RETURNS [done:
BOOL←
TRUE] = {
IF all
THEN TerminalIO.PutRope["new cell and replace all instances\n"]
ELSE TerminalIO.PutRope["new cell for this instance\n"];
CDCells.ToSequenceMode[currentCell];
IF interactive
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 ~CDCells.IsCell[originalCell]
AND all
THEN {
CDDirectory.ReplaceObject[design: design, old: originalCell, new: currentCell]
};
IF design.mutability#findOut THEN 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 ", 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.