CDExtrasImpl.mesa (part of ChipNDale)
Copyright © 1983, 1985 by Xerox Corporation. All rights reserved.
by Christian Jacobi, September 8, 1983 9:45 am
last edited by Christian Jacobi, May 8, 1985 12:43:13 pm PDT
DIRECTORY
Atom,
CD,
CDApplications,
CDCells,
CDSimpleOps,
CDDirectory,
CDExtras,
CDBasics,
CDMarks,
CDOps,
CDProperties,
IO,
Process,
Rope,
SymTab;
CDExtrasImpl:
CEDAR
MONITOR
IMPORTS Atom, CD, CDApplications, CDCells, CDSimpleOps, CDDirectory, CDBasics, CDMarks, CDOps, CDProperties, IO, Process, Rope, SymTab
EXPORTS CDExtras
SHARES CD =
BEGIN
CreateDummyObject:
PUBLIC
PROC[design:
CD.Design]
RETURNS [
CD.ObPtr] =
--create a dummy cell object which contains the whole design.
--On future changes of the design, the dummy object may or may not
--get obsolete.
BEGIN
cellOb: CD.ObPtr ← CDCells.CreateEmptyCell[];
cptr: CD.CellPtr ← NARROW[cellOb.specificRef];
FOR l:
LIST
OF
CD.PushRec ← design.actual, l.rest
WHILE l#
NIL
DO
cptr.contents ←
CONS[
NEW[
CD.Application ← [
ob: l.first.dummyCell.ob,
location: [0, 0],
orientation: CD.original,
selected: FALSE
]],
cptr.contents
];
ENDLOOP;
RETURN [cellOb]
END;
Pair: TYPE = RECORD [p: CDDirectory.EnumerateObjectsProc, x: REF, key: REF];
EnumerationEnteredWhileInProgress: ERROR = CODE;
entered: BOOL←FALSE;
Enter:
ENTRY
PROC [] =
BEGIN
IF entered THEN RETURN WITH ERROR EnumerationEnteredWhileInProgress;
entered←TRUE;
END;
Leave:
ENTRY
PROC [] =
BEGIN
entered←FALSE;
END;
EnumerateChildrenObjects:
PUBLIC
PROC[me:
CD.ObPtr, p: CDDirectory.EnumerateObjectsProc, x:
REF] =
--enumerate me and its children objects
BEGIN ENABLE UNWIND => Leave[];
pair: REF Pair ← NEW[Pair];
pair^ ← [p, x, pair];
Enter[];
MyEnum[me, pair];
Leave[];
END;
MyEnum:
PROC [me:
CD.ObPtr, x:
REF] =
BEGIN
pair: REF Pair ← NARROW[x];
v: REF ← Atom.GetPropFromList[me.properties, $CDExtrasImplsTouched];
IF v=pair.key THEN RETURN; -- already visited
--mark visited
me.properties ← Atom.PutPropOnList[me.properties, $CDExtrasImplsTouched, pair.key];
--enumerate my children first
IF me.p.inDirectory THEN CDDirectory.EnumerateChildObjects[me, MyEnum, x];
pair.p[me, pair.x]; -- call clients enumerator proc
END;
EnumerateDesignObjects:
PUBLIC
PROC [design:
CD.Design, p: CDDirectory.EnumerateObjectsProc, x:
REF] =
BEGIN ENABLE UNWIND => Leave[];
pair: REF Pair ← NEW[Pair];
EachCell: SymTab.EachPairAction
--[key: Key, val: Val] RETURNS [quit: BOOLEAN] -- =
BEGIN
quit ← FALSE;
MyEnum[NARROW[val, CD.ObPtr], pair]
END;
pair^ ← [p, x, pair];
Enter[];
[] ← SymTab.Pairs[design.cellDirectory, EachCell];
FOR l:
LIST
OF
CD.PushRec ← design.actual, l.rest
WHILE l#
NIL
DO
MyEnum[l.first.dummyCell.ob, pair];
IF l.first.mightReplace#NIL THEN MyEnum[l.first.mightReplace.ob, pair];
ENDLOOP;
Leave[];
END;
BoundingBox:
PUBLIC
PROC [design:
CD.Design]
RETURNS [r: CD.DesignRect ← CDBasics.empty] =
BEGIN
FOR l:
LIST
OF
CD.PushRec ← design.actual, l.rest
WHILE l#
NIL
DO
r ← CDBasics.Surround[r, CDApplications.BoundingRectO[
NARROW[l.first.dummyCell.ob.specificRef, CD.CellPtr].contents ]]
ENDLOOP;
END;
PushedCellName:
PUBLIC PROC [design:
CD.Design]
RETURNS [Rope.
ROPE] =
{
RETURN [
IF design=NIL OR design.actual=NIL THEN "erronous"
ELSE IF design.actual.rest=NIL THEN "top level"
ELSE IF design.actual.first.specific=NIL THEN "erronous"
ELSE IF design.actual.first.specific.name#NIL THEN design.actual.first.specific.name
ELSE "unnamed cell" ]
};
RPEachChildren: CDDirectory.EnumerateObjectsProc
--PROC [me: ObPtr, x: REF]-- =
BEGIN
Process.Yield[];
IF ~me.p.inDirectory
THEN
CDProperties.PutPropOnObject[onto: me, prop: x, val: NIL]
END;
RemoveProps:
PROC [design:
CD.Design, key:
REF] =
BEGIN
RPEachDirectoryEntry: CDDirectory.EachEntryAction =
BEGIN
CDProperties.PutPropOnObject[onto: ob, prop: key, val: NIL];
IF ob.p.inDirectory
THEN
CDDirectory.EnumerateChildObjects[me: ob, p: RPEachChildren, x: key];
END;
--RemoveProps
TRUSTED{Process.SetPriority[Process.priorityBackground]};
[] ← CDDirectory.Enumerate[design: design, action: RPEachDirectoryEntry];
END;
RemoveProperties:
PUBLIC PROC [design:
CD.Design, key:
REF] =
--tries to remove the propertiy "key" from all objects of "design";
--may be delayed or incomplete
--(only from objects, not applications...)
BEGIN
TRUSTED {Process.Detach[FORK RemoveProps[design, key]]}
END;
ToLambda:
PUBLIC
PROC [n:
CD.Number]
RETURNS [Rope.
ROPE] =
BEGIN
IF n
MOD
CD.lambda = 0
THEN
RETURN IO.PutFR[format: " %g", v1: IO.int[n/CD.lambda]]
ELSE {
r: Rope.ROPE ← " ";
IF n<0
THEN {
n ← ABS[n];
r ← " -";
};
RETURN [
IO.PutFR["%0g(%0g+%0g/%0g)",
IO.rope[r], IO.int[n/CD.lambda], IO.int[n MOD CD.lambda], IO.int[CD.lambda]
]];
}
END;
PopToTopLayer:
PUBLIC PROC [design:
CD.Design] =
--if "design" is pushed in, it will be popped out, either by flushing,
--replacing cells or creating new cells
BEGIN
WHILE design.actual.rest#
NIL
DO
IF NOT CDCells.PopFromCell[design, newcell] THEN EXIT
ENDLOOP;
IF design.actual.rest#NIL THEN ERROR
END;
Cellize:
PUBLIC PROC [design:
CD.Design, name: Rope.
ROPE←
NIL]
RETURNS [cell:
CD.ObPtr←
NIL, pos:
CD.DesignPosition] =
--makes a single "cell" of of the "design", removes all applications;
--pos: if "cell" is included at position "pos" in an empty design we would get "design" again
--if "design" is pushed in, it will be popped out, either by flushing,
--replacing cells or creating new cells
BEGIN
done: BOOL;
al: CD.ApplicationList;
PopToTopLayer[design];
CDSimpleOps.SelectAll[design];
IF (alOps.AppList[design])#
NIL
THEN {
IF al.rest=
NIL
AND
ISTYPE[al.first.ob.specificRef,
CD.CellPtr]
AND al.first.orientation=
CD.original
THEN {
cell ← al.first.ob;
IF name#NIL THEN [] ← CDDirectory.Rename[design, cell, name];
done ← TRUE
}
ELSE {
IF name=NIL THEN name ← design.name;
IF name=NIL THEN name ← "no named design";
[done, cell] ← CDCells.CreateCellSelected[design, name];
};
IF done
THEN {
pos ← CDOps.AppList[design].first.location;
}
ELSE cell ← NIL
};
CDOps.SetAppList[design, NIL]
END;
SkipAt:
PROC [n: Rope.
ROPE]
RETURNS [Rope.
ROPE] =
--skip everything after and inclusive first "@"
BEGIN
RETURN [Rope.Substr[base: n, len: Rope.SkipTo[s: n, skip: "@"]]]
END;
MergeIn:
PUBLIC
PROC [design:
CD.Design, from:
CD.Design, name: Rope.
ROPE←
NIL, fullDirectory:
BOOL←
TRUE]
RETURNS [ob:
CD.ObPtr←
NIL, pos:
CD.DesignPosition] =
--"from" is transfered to an object, and is included (transitive) to "design"'s directory
--"from" then may be resetted (preserving the rule: any object is in at most one directory)
--the caller is assumed to have the locks of both designs
--if "from" is pushed in, it's merged copy will be popped out, either by flushing,
--replacing or creating new cells
--"name" replaces "from"'s design name for the new created object, but it is a hint only
--"pos": if "ob" is included at position "pos" in an empty design we would get "from" again
--"fullDirectory": whether all objects of "from"'s directory are merged to design,
--or only those used by "from"'s top layer
--the "from"'s object's may change name to avoid conflicts with "design"'s directory
--ob gets nil if "from" is empty
--technologies must be compatible
BEGIN
m: CDMarks.MarkRange;
IncludeOneEntry: CDDirectory.EachEntryAction = {
--[name: Rope.ROPE, ob: CD.ObPtr] RETURNS [quit: BOOLLSE]--
IF fullDirectory
OR ob.marked=m
THEN {
IF NOT CDDirectory.Remove[design: from, name: name, expectObject: ob] THEN ERROR;
[] ← CDDirectory.Include[design: design, object: ob, alternateName: SkipAt[name]]
}
};
IF design=from THEN RETURN;
IF design.technology#from.technology
THEN
RETURN WITH ERROR CD.Error[callingError, "MergIn design has different technology"];
[ob, pos] ← Cellize[from, name];
IF ob#
NIL
THEN {
IF ~fullDirectory
THEN {
m ← CDMarks.GetNewMark[from];
CDMarks.MarkUnMarkedInclusiveChildren[from, ob, m];
};
[] ← CDDirectory.Enumerate[design: from, action: IncludeOneEntry];
IF ~fullDirectory THEN CDMarks.ReturnMark[from];
CDOps.ResetDesign[from];
}
ELSE ob ← NIL
END;
MergeInObjects:
PUBLIC
PROC [design:
CD.Design, from:
CD.Design, objects:
LIST
OF
CD.ObPtr] =
--"objects" which are in "from"'s directory are transferrerd (transitive) to "design"'s directory
--"from" then may be resetted (preserving the rule: any object is in at most one directory)
--the caller is assumed to have the locks of both designs
--the object's may change name to avoid conflicts with "design"'s directory
--technologies must be compatible
BEGIN
m: CDMarks.MarkRange = CDMarks.GetNewMark[from];
IncludeOneEntry: CDDirectory.EachEntryAction = {
--[name: Rope.ROPE, ob: CD.ObPtr] RETURNS [quit: BOOLLSE]--
IF ob.marked=m
THEN {
IF NOT CDDirectory.Remove[design: from, name: name, expectObject: ob] THEN ERROR;
[] ← CDDirectory.Include[design: design, object: ob, alternateName: SkipAt[name]]
}
};
IF design=from THEN RETURN;
IF design.technology#from.technology
THEN
RETURN WITH ERROR CD.Error[callingError, "MergInObjects design has different technology"];
FOR l:
LIST
OF
CD.ObPtr ← objects, l.rest
WHILE l#
NIL
DO
CDMarks.MarkUnMarkedInclusiveChildren[from, l.first, m];
ENDLOOP;
[] ← CDDirectory.Enumerate[design: from, action: IncludeOneEntry];
CDOps.ResetDesign[from];
END;
END.