CDSatellitesImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Written by Pradeep Sindhu December 20, 1985 1:25:44 pm PST
This module is descended from the early implementation of satellites by Monier. It has been
rewritten a number of times since then by Sindhu and Serlet to find an implementation that
is comfortable to use both from programs and interactively. This last version is a complete
rewrite, one hopes for the last time!
Pradeep Sindhu March 26, 1986 3:21:40 pm PST
Bertrand Serlet August 17, 1986 0:49:00 am PDT
Christian Jacobi, July 15, 1986 1:59:30 pm PDT
Jean-Marc Frailong July 28, 1986 6:45:49 pm PDT
DIRECTORY
CD, CDCells, CDDirectory, CDEvents, CDInstances, CDMenus, CDOps, CDPanelFonts, CDProperties, CDSatellites, CDSequencer, CDTexts, IO, Rope, TerminalIO;
CDSatellitesImpl: CEDAR PROGRAM
IMPORTS CDCells, CDEvents, CDDirectory, CDInstances, CDMenus, CDOps, CDPanelFonts, CDProperties, CDSequencer, CDTexts, IO, Rope, TerminalIO
EXPORTS CDSatellites =
BEGIN OPEN CDSatellites;
satellitesProp: PUBLIC ATOM ← RegisterProp[$CDSatellitesSatList, TRUE];
This property hangs on a master. Its value is the list of the master's satellites.
maxGroupIdProp: PUBLIC ATOM ← RegisterProp[$CDSatellitesMaxGroupId, TRUE];
This property hangs on a design. Its value is the highest numbered satellite group in the design.
groupIdProp: PUBLIC ATOM ← RegisterProp[$CDSatellitesGroupId, TRUE];
This property hangs on each instance that is in a satellite group. Its value is the group's groupId.
commentProp: PUBLIC ATOM ← RegisterProp[$CDSatellitesComment, TRUE];
This property hangs on text instances that are comments, and therefore to be ignored.
prevNumMasters: NAT ← 0;
Public functions
GetSatellites: PUBLIC PROC [from: REF, filterComments: BOOLTRUE] RETURNS [sats: InstanceList] = {
IF from=NIL
THEN sats ← NIL
ELSE {
sats ← NARROW[CDProperties.GetProp[from, satellitesProp]];
IF filterComments THEN sats ← FilterComments[sats];
};
};
GetSatelliteRopes: PUBLIC PROC [from: REF, filterComments: BOOLTRUE] RETURNS [ropes: LIST OF ROPENIL] = {
FOR list: InstanceList ← GetSatellites[from, filterComments], list.rest WHILE list#NIL DO
rope: ROPENARROW [list.first.ob.specificRef, CDTexts.TextPtr].text;
IF CDProperties.GetProp[list.first, commentProp]=NIL THEN ropes ← CONS [rope, ropes];
ENDLOOP;
};
EnforceInvariants: PUBLIC PROC [design: CD.Design, world: InstanceList] RETURNS [oSats: InstanceList] = {
Node: TYPE = REF NodeRec;
NodeRec: TYPE = RECORD [
id: INT ← -1,
list: InstanceList ← NIL];
Table: TYPE = REF TableRec;
TableRec: TYPE = RECORD [
elements: SEQUENCE size: NAT OF LIST OF Node
];
masterTable: Table ← NEW[TableRec[TableSize[]]];
iSats: InstanceList;
maxGroupId: INT;
TableSize: PROC [] RETURNS [NAT] = INLINE {
FOR sizes: LIST OF NATLIST [7, 17, 31, 67, 253], sizes.rest WHILE sizes#NIL DO
IF prevNumMasters <= sizes.first THEN RETURN [sizes.first]
ENDLOOP;
RETURN [253]
};
Hash: PROC [i: INT] RETURNS [INT] = {
RETURN[i MOD masterTable.size]
};
Store: PROC [inst: CD.Instance] = {
id: INT ← GroupIdFast[inst];
hash: INT ← Hash[id];
FOR nl: LIST OF Node ← masterTable[hash], nl.rest WHILE nl#NIL DO
IF nl.first.id=id THEN {nl.first.list ← CONS[inst, nl.first.list]; RETURN};
ENDLOOP;
masterTable[hash] ← CONS[NEW[NodeRec ← [id: id, list: LIST[inst]]], masterTable[hash]];
};
Fetch: PROC [id: INT] RETURNS [InstanceList] = {
hash: INT ← Hash[id];
FOR nl: LIST OF Node ← masterTable[hash], nl.rest WHILE nl#NIL DO
IF nl.first.id=id THEN RETURN [nl.first.list];
ENDLOOP;
RETURN [NIL];
};
Check that maxGroupIdProp exists; if not, put it
IF CDProperties.GetProp[design, maxGroupIdProp]=NIL THEN CDProperties.PutProp[design, maxGroupIdProp, NEW [INT ← 0]];
Make masterTable and iSats; at the same time strip masters of their satellite lists
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
IF CDTexts.IsText[w.first.ob]
THEN {IF CDProperties.GetProp[w.first, groupIdProp]#NIL THEN iSats ← CONS [w.first, iSats]}
ELSE {IF CDProperties.GetProp[w.first, groupIdProp]#NIL THEN {Store[w.first]; CDProperties.PutProp[w.first, satellitesProp, NIL]}}
ENDLOOP;
For each satellite find its closest master and put it on that master's satellite list
FOR sl: InstanceList ← iSats, sl.rest WHILE sl#NIL DO
Closer: PROC [newMaster: CD.Instance] RETURNS [BOOL] = INLINE {
mRect, newMRect, sRect: CD.Rect;
IF master=NIL THEN RETURN [TRUE];
mRect ← CDInstances.InstRectO[master];
newMRect ← CDInstances.InstRectO[newMaster];
sRect ← CDInstances.InstRectO[sl.first];
RETURN[RectMinDist[newMRect, sRect] < RectMinDist[mRect, sRect]];
};
master: CD.Instance ← NIL;
satGId: INT ← GroupIdFast[sl.first];
FOR ml: InstanceList ← Fetch[satGId], ml.rest WHILE ml#NIL DO
IF satGId#GroupIdFast[ml.first] THEN LOOP;
IF Closer[ml.first] THEN master ← ml.first;
ENDLOOP;
IF master=NIL
THEN CDProperties.PutProp[sl.first, groupIdProp, NIL]
ELSE EnsureSatelliteOnList[master, sl.first];
ENDLOOP;
Now go through the masterTable and renumber groups with identical group Id's
maxGroupId ← NARROW [CDProperties.GetProp[design, maxGroupIdProp], REF INT]^;
prevNumMasters ← 0;
FOR i: INT IN [0..masterTable.size) DO
FOR nl: LIST OF Node ← masterTable[i], nl.rest WHILE nl#NIL DO
prevNumMasters ← prevNumMasters+1;
FOR ml: InstanceList ← nl.first.list.rest, ml.rest WHILE ml#NIL DO
maxGroupId ← maxGroupId+1;
CDProperties.PutProp[ml.first, groupIdProp, NEW [INT ← maxGroupId]];
FOR sl: InstanceList ← NARROW[CDProperties.GetProp[ml.first, satellitesProp]], sl.rest WHILE sl#NIL DO
CDProperties.PutProp[sl.first, groupIdProp, NEW [INT ← maxGroupId]];
ENDLOOP;
ENDLOOP;
ENDLOOP;
ENDLOOP;
CDProperties.PutProp[design, maxGroupIdProp, NEW [INT ← maxGroupId]];
Compute oSats and return it
oSats ← NIL;
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
IF CDTexts.IsText[w.first.ob] AND CDProperties.GetProp[w.first, groupIdProp]=NIL THEN oSats ← CONS [w.first, oSats]
ENDLOOP;
};
MakeInstanceSatellite: PUBLIC PROC [design: CD.Design, master: CD.Instance, text: CD.Instance] = {
EnsureSatelliteOnList[master, text];
IF CDProperties.GetProp[master, groupIdProp]=NIL THEN {
ref: REF INTNARROW[CDProperties.GetProp[design, maxGroupIdProp]];
ref^ ← ref^+1;
CDProperties.PutProp[master, groupIdProp, NEW [INT ← ref^]];
};
CDProperties.PutProp[text, groupIdProp, NEW[INT ← GroupId[master]]];
};
Event Procs for reading and writing satellites from files
DoEnforceDesignInvariants: CDEvents.EventProc = {
IF design=NIL THEN RETURN; -- sounds like a hack, but design is sometimes NIL!!
EnforceDesignInvariants[design];
};
EnforceDesignInvariants: PUBLIC PROC [design: CD.Design] = {
headPushRecs: LIST OF CD.PushRec;
EnforceForEachCell: CDDirectory.EachEntryAction = {
IF NOT CDCells.IsCell[ob] THEN RETURN;
CDProperties.PutObjectProp[ob, satellitesProp, EnforceInvariants[design, NARROW [ob.specificRef, CD.CellPtr].contents]]
};
[] ← CDDirectory.Enumerate[design, EnforceForEachCell];
headPushRecs ← design.actual;
WHILE headPushRecs.rest#NIL DO headPushRecs ← headPushRecs.rest ENDLOOP;
CDProperties.PutDesignProp[design, satellitesProp, EnforceInvariants[design, headPushRecs.first.specific.contents]];
};
Event Proc for processing a cell change
AfterCellReplacement: CDEvents.EventProc = {
ob: CD.Object ← NARROW [x];
oSats: InstanceList ← EnforceInvariants[design, NARROW[ob.specificRef, CD.CellPtr].contents];
CDProperties.PutObjectProp[ob, satellitesProp, oSats]
};
Command Procs
Given a satellite or a master select instance group.
SelectInstanceGroupCommand: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
multiple: BOOL;
selected: CD.Instance;
numSats: INT;
[] ← EnforceInvariants[comm.design, world];
[selected, multiple] ← CDOps.SelectedInstance[comm.design];
IF ~SingleSelected[selected, multiple] THEN RETURN;
IF CDProperties.GetProp[selected, groupIdProp]=NIL THEN {
TerminalIO.WriteRope["Selected instance is not in any instance group.\n"];
RETURN
};
IF CDTexts.IsText[selected.ob]
THEN numSats ← SelectInstanceGroup[comm.design, world, FindMaster[world, selected]]
ELSE numSats ← SelectInstanceGroup[comm.design, world, selected];
TerminalIO.WriteF["Instance group selected: %g satellites(s) in group.\n", IO.int[numSats]];
};
Adds a single instance satellite to those already existing for a master.
AddInstanceSatelliteCommand: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
multiple: BOOL;
selected: CD.Instance;
name: ROPE;
font: CDTexts.CDFont ← CDPanelFonts.CurrentFont[comm.design];
text: CD.Instance;
[] ← EnforceInvariants[comm.design, world];
[selected, multiple] ← CDOps.SelectedInstance[comm.design];
IF ~SingleSelected[selected, multiple] THEN RETURN;
IF CDTexts.IsText[selected.ob] THEN {
TerminalIO.WriteRope["\n** Selected instance is text--can't put a satellite on it.\n"];
RETURN;
};
name ← TerminalIO.RequestRope["Type instance satellite: "];
IF name=NIL THEN {TerminalIO.WriteRope["\n** Empty name--can't do it.\n"]; RETURN};
IF font=NIL THEN {TerminalIO.WriteRope["\n** No current font--can't do it.\n"]; RETURN};
text ← NEW [CD.InstanceRep ← [ob: CDTexts.CreateText[name, font], location: comm.pos]];
CDOps.IncludeInstance[comm.design, text];
MakeInstanceSatellite[comm.design, selected, text];
TerminalIO.WriteRope["Instance satellite added\n"];
};
Given zero or more text instances and exactly one non-text instance (the master) remove any previous instance satellites and add the ones currently selected.
SetInstanceSatellitesCommand: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
satellites: InstanceList ← NIL;
master: CD.Instance ← NIL;
[] ← EnforceInvariants[comm.design, world];
Find the master and put selected text onto the list of satellites
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
inst: CD.Instance ← w.first;
IF ~inst.selected THEN LOOP;
IF CDTexts.IsText[inst.ob]
THEN satellites ← CONS[inst, satellites]
ELSE {
IF master#NIL THEN {TerminalIO.WriteRope["\n** More than one master selected--can't do it.\n"]; RETURN};
master ← inst}
ENDLOOP;
IF master=NIL THEN {TerminalIO.WriteRope["\n** No master selected--can't do it.\n"]; RETURN};
Remove master's old satellites, if any
IF CDProperties.GetProp[master, satellitesProp]#NIL THEN
FOR sl: InstanceList ← NARROW [CDProperties.GetProp[master, satellitesProp]], sl.rest WHILE sl#NIL DO
UnmakeInstanceSatellite[world, sl.first];
ENDLOOP;
Remove each satellite from its previous master, if any, and put it on to the new one
FOR sl: InstanceList ← satellites, sl.rest WHILE sl#NIL DO
UnmakeInstanceSatellite[world, sl.first];
MakeInstanceSatellite[comm.design, master, sl.first];
ENDLOOP;
TerminalIO.WriteRope["Instance satellites set\n"];
};
SelectObjectGroupCommand: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
numSats: INT;
[] ← EnforceInvariants[comm.design, world];
numSats ← SelectObjectGroup[comm.design, world];
TerminalIO.WriteF["Object group selected: %g satellites(s) in group.\n", IO.int[numSats]];
};
AddObjectSatelliteCommand: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
oSats: InstanceList;
name: ROPE;
font: CDTexts.CDFont ← CDPanelFonts.CurrentFont[comm.design];
text: CD.Instance;
oSats ← EnforceInvariants[comm.design, world];
name ← TerminalIO.RequestRope["Type object satellite: "];
IF name=NIL THEN {TerminalIO.WriteRope["\n** Empty name--can't do it.\n"]; RETURN};
IF font=NIL THEN {TerminalIO.WriteRope["\n** No current font--can't do it.\n"]; RETURN};
text ← NEW [CD.InstanceRep ← [ob: CDTexts.CreateText[name, font], location: comm.pos]];
CDOps.IncludeInstance[comm.design, text];
oSats ← CONS[text, oSats];
CDProperties.PutObjectProp[comm.design.actual.first.dummyCell.ob, satellitesProp, oSats];
TerminalIO.WriteRope["Object satellite added\n"];
};
SetObjectSatellitesCommand: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
oSats: InstanceList ← NIL;
master: CD.Instance ← NIL;
[] ← EnforceInvariants[comm.design, world];
Put selected text onto oSats and remove it from iSats
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
inst: CD.Instance ← w.first;
IF inst.selected AND CDTexts.IsText[inst.ob] THEN {
oSats ← CONS[inst, oSats];
UnmakeInstanceSatellite[world, inst];
}
ENDLOOP;
CDProperties.PutObjectProp[comm.design.actual.first.dummyCell.ob, satellitesProp, oSats];
TerminalIO.WriteRope["Object satellites set\n"];
};
MakeComment: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
done: BOOLFALSE;
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
IF CDTexts.IsText[w.first.ob] AND w.first.selected THEN {
textPtr: CDTexts.TextPtr ← NARROW [w.first.ob.specificRef];
oldFontName: ROPE ← textPtr.cdFont.supposedName;
CDProperties.PutProp[w.first, commentProp, commentProp];
IF oldFontName=NIL THEN ERROR;
IF ~IsItalic[oldFontName] THEN ChangeFont[comm.design, w.first, Rope.Cat[oldFontName, "I"], textPtr.cdFont.scaleI];
done ← TRUE;
}
ENDLOOP;
TerminalIO.WriteRope[IF done THEN "Made comment\n" ELSE "No selected text\n"];
};
UnMakeComment: PROC [comm: CDSequencer.Command] = {
world: InstanceList ← CDOps.InstList[comm.design];
done: BOOLFALSE;
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
IF CDTexts.IsText[w.first.ob] AND w.first.selected THEN {
textPtr: CDTexts.TextPtr ← NARROW [w.first.ob.specificRef];
oldFontName: ROPE ← textPtr.cdFont.supposedName;
CDProperties.PutProp[w.first, commentProp, NIL];
IF oldFontName=NIL THEN ERROR;
IF IsItalic[oldFontName] THEN ChangeFont[comm.design, w.first, Rope.Substr[oldFontName, 0, Rope.Length[oldFontName]-1], textPtr.cdFont.scaleI];
done ← TRUE;
}
ENDLOOP;
TerminalIO.WriteRope[IF done THEN "UnMade comment\n" ELSE "No selected text\n"]
};
Internal Utilities
UnmakeInstanceSatellite: PROC [world: InstanceList, satellite: CD.Instance] = {
sats: InstanceList;
master: CD.Instance ← FindMaster[world, satellite];
CDProperties.PutProp[satellite, groupIdProp, NIL];
IF master=NIL OR CDProperties.GetProp[master, satellitesProp]=NIL THEN RETURN;
sats ← NARROW[CDProperties.GetProp[master, satellitesProp]];
FOR sl: InstanceList ← NARROW[CDProperties.GetProp[master, satellitesProp]], sl.rest WHILE sl#NIL DO
IF sl.first#satellite THEN sats ← CONS[sl.first, sats]
ENDLOOP;
CDProperties.PutProp[master, satellitesProp, sats]
};
FilterComments: PROC [sats: InstanceList] RETURNS [result: InstanceList ← NIL] = {
FOR l: InstanceList ← sats, l.rest WHILE l#NIL DO
IF CDProperties.GetProp[l.first, commentProp]=NIL THEN result ← CONS [l.first, result];
ENDLOOP;
};
GroupId: PROC [inst: CD.Instance] RETURNS [INT] = INLINE {
IF CDProperties.GetProp[inst, groupIdProp]#NIL
THEN RETURN[NARROW[CDProperties.GetProp[inst, groupIdProp], REF INT]^]
ELSE RETURN[-1];
};
GroupIdFast: PROC [inst: CD.Instance] RETURNS [INT] = INLINE {
RETURN[NARROW[CDProperties.GetProp[inst, groupIdProp], REF INT]^]
};
EnsureSatelliteOnList: PROC [master: CD.Instance, satellite: CD.Instance] = {
sats: InstanceList;
IF CDProperties.GetInstanceProp[master, satellitesProp]=NIL
THEN sats ← LIST[satellite]
ELSE {
sats ← NARROW[CDProperties.GetInstanceProp[master, satellitesProp], InstanceList];
IF ~Member[sats, satellite] THEN sats ← CONS[satellite, sats];
};
CDProperties.PutInstanceProp[master, satellitesProp, sats];
};
Returns true if inst is in the list insances
Member: PROC [insances: InstanceList, inst: CD.Instance] RETURNS [BOOLFALSE] = {
WHILE insances#NIL DO
IF insances.first=inst THEN RETURN [TRUE]; insances ← insances.rest;
ENDLOOP;
};
Returns true if some instance in intances has the same group Id as inst.
GroupMember: PROC [instances: InstanceList, inst: CD.Instance] RETURNS [BOOLFALSE] = {
instGId: INT ← GroupId[inst];
WHILE instances#NIL DO
IF GroupId[instances.first]=instGId THEN RETURN [TRUE]; instances ← instances.rest;
ENDLOOP;
};
FindMaster: PROC [world: InstanceList, satellite: CD.Instance] RETURNS [master: CD.Instance ← NIL] = {
satGId: INT ← GroupId[satellite];
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
IF satGId=GroupId[w.first] AND ~CDTexts.IsText[w.first.ob] THEN RETURN [w.first];
ENDLOOP;
};
SingleSelected: PROC [selected: CD.Instance, multiple: BOOL] RETURNS [BOOL] = {
IF selected=NIL THEN {
TerminalIO.WriteRope["\n** No current selection--can't do it.\n"];
RETURN[FALSE];
};
IF multiple THEN {
TerminalIO.WriteRope["\n** Multiple instances selected--can't do it.\n"];
RETURN[FALSE];
};
RETURN[TRUE];
};
Select the satellite group for master
SelectInstanceGroup: PROC [design: CD.Design, world: LIST OF CD.Instance, master: CD.Instance] RETURNS [numSats: INT] = {
satellites: InstanceList;
numSats ← 0;
IF master=NIL THEN RETURN ELSE satellites ← GetSatellites[master, FALSE];
master.selected ← TRUE;
CDOps.RedrawInstance[design, master];
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
IF ~Member[satellites, w.first] THEN LOOP;
w.first.selected ← TRUE; numSats ← numSats + 1;
CDOps.RedrawInstance[design, w.first];
ENDLOOP;
};
SelectObjectGroup: PROC [design: CD.Design, world: LIST OF CD.Instance] RETURNS [numSats: INT] = {
numSats ← 0;
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
w.first.selected ← FALSE;
CDOps.RedrawInstance[design, w.first, TRUE];
ENDLOOP;
FOR w: InstanceList ← world, w.rest WHILE w#NIL DO
IF ~CDTexts.IsText[w.first.ob] OR CDProperties.GetProp[w.first, groupIdProp]#NIL THEN LOOP;
w.first.selected ← TRUE; numSats ← numSats + 1;
CDOps.RedrawInstance[design, w.first];
ENDLOOP;
};
IntervalMinDist: PROC [i1min, i1max, i2min, i2max: INT] RETURNS [INT] = INLINE {
IF i1max<i2min THEN RETURN [i2min-i1max];
IF i1min>i2max THEN RETURN [i1min-i2max];
RETURN [0];
};
RectMinDist: PROC [rectA, rectB: CD.Rect] RETURNS [INT] = INLINE {
xMinDist: INT ← IntervalMinDist[rectA.x1, rectA.x2, rectB.x1, rectB.x2];
yMinDist: INT ← IntervalMinDist[rectA.y1, rectA.y2, rectB.y1, rectB.y2];
IF xMinDist=0
THEN IF yMinDist=0 THEN RETURN [0] ELSE RETURN [yMinDist]
ELSE IF yMinDist=0 THEN RETURN [xMinDist] ELSE RETURN [xMinDist+yMinDist]
};
RegisterProp: PROC [prop: ATOM, copy: BOOLFALSE] RETURNS [sameAtom: ATOM] = {
[] ← CDProperties.RegisterProperty[prop, $CDSatellites];
CDProperties.InstallProcs[prop, [makeCopy: IF copy THEN CDProperties.CopyVal ELSE CDProperties.DontCopy]];
sameAtom ← prop;
};
ChangeFont: PROC [design: CD.Design, text: CD.Instance, fontName: ROPE, scale: INT] = {
font: CDTexts.CDFont ← CDTexts.MakeFont[fontName, scale];
ob: CD.Object ← CDTexts.CreateText[
text: NARROW[text.ob.specificRef, CDTexts.TextPtr].text,
font: font,
layer: text.ob.layer];
CDOps.DelayedRedraw[design, CDInstances.InstRectO[text]];
IF ob#NIL THEN text.ob ← ob;
CDOps.DelayedRedraw[design, CDInstances.InstRectO[text], FALSE];
};
IsItalic: PROC [fontName: ROPE] RETURNS [BOOL] = {
RETURN [Rope.Find[fontName, "i", Rope.Length[fontName]-1, FALSE]#-1]
};
Initialization
[] ← CDMenus.CreateMenu["Satellites and Expressions Menu", $SatellitesMenu];
CDMenus.ImplementCommandToCallMenu[$SatellitesMenu, $SatellitesMenu];
CDMenus.ImplementEntryCommand[menu: $SatellitesMenu, entry: "Show Instance Satellites (I-Left)", p: SelectInstanceGroupCommand, key: $SelectInstanceGroup, queue: doQueueAndMark];
CDMenus.ImplementEntryCommand[menu: $SatellitesMenu, entry: "Add Instance Satellite (I-Middle)", p: AddInstanceSatelliteCommand, key: $AddInstanceSatellite, queue: doQueueAndMark];
CDMenus.ImplementEntryCommand[menu: $SatellitesMenu, entry: "Set Instance Satellites (I-Right)", p: SetInstanceSatellitesCommand, key: $SetInstanceSatellites, queue: doQueueAndMark];
CDMenus.ImplementEntryCommand[menu: $SatellitesMenu, entry: "Show Object Satellites (O-Left)", p: SelectObjectGroupCommand, key: $SelectObjectGroup, queue: doQueueAndMark];
CDMenus.ImplementEntryCommand[menu: $SatellitesMenu, entry: "Add Object Satellite (O-Middle)", p: AddObjectSatelliteCommand, key: $AddObjectSatellite, queue: doQueueAndMark];
CDMenus.ImplementEntryCommand[menu: $SatellitesMenu, entry: "Set Object Satellites (O-Right)", p: SetObjectSatellitesCommand, key: $SetObjectSatellites, queue: doQueueAndMark];
CDSequencer.ImplementCommand[key: $MakeComment, proc: MakeComment];
CDSequencer.ImplementCommand[key: $UnMakeComment, proc: UnMakeComment];
CDEvents.RegisterEventProc[$BeforeOutput, DoEnforceDesignInvariants];
CDEvents.RegisterEventProc[$AfterInput, DoEnforceDesignInvariants];
CDEvents.RegisterEventProc[$AfterCellReplacement, AfterCellReplacement];
CDEvents.RegisterEventProc[$InteractiveCreatedCell, AfterCellReplacement];
END.