CDSatellitesImpl:
CEDAR
PROGRAM
IMPORTS CDCells, CDDirectory, CDEvents, CDCommandOps, CDMenus, CDOps, CDPanelFonts, CDProperties, CDTexts, IO, PW, TerminalIO
EXPORTS CDSatellites =
BEGIN OPEN CDSatellites;
satellitesProp:
ATOM ←
PW.RegisterProp[$CDSatellites,
TRUE];
This property hangs on a master. Its value is a list of CD.Objects that are the master's satellites.
Public functions
SetSatellites:
PUBLIC
PROC [master:
CD.Instance, satellites: ObList ←
NIL] = {
CDProperties.PutPropOnInstance[master, satellitesProp, satellites];
};
AddSatellites:
PUBLIC
PROC [master:
CD.Instance, satellites: ObList ←
NIL] = {
s: ObList ← NARROW[CDProperties.GetPropFromInstance[master, satellitesProp]];
FOR l: ObList ← satellites, l.rest
WHILE l#
NIL
DO
s ← CONS[l.first, s]
ENDLOOP;
CDProperties.PutPropOnInstance[master, satellitesProp, s];
};
GetSatellites:
PUBLIC
PROC [master:
CD.Instance]
RETURNS [satellites: ObList ←
NIL] = {
satellites ← NARROW [CDProperties.GetPropFromInstance[master, satellitesProp]];
};
GetSatelliteRopes:
PUBLIC
PROC [masterProps:
CD.PropList]
RETURNS [ropes:
LIST
OF
ROPE ←
NIL] = {
FOR list: ObList ←
NARROW [CDProperties.GetPropFromList[masterProps, satellitesProp]], list.rest
WHILE list#
NIL
DO
rope: ROPE ← NARROW [list.first.specificRef, CDTexts.TextPtr].text;
ropes ← CONS [rope, ropes];
ENDLOOP;
};
EnforceConstraint:
PUBLIC
PROC [world:
LIST
OF
CD.Instance]
RETURNS [allOk:
BOOL] = {
allOk ← TRUE;
FOR iList:
LIST
OF
CD.Instance ← world, iList.rest
WHILE iList#
NIL
DO
oldObList: ObList ← NARROW [CDProperties.GetProp[iList.first, satellitesProp]];
newObList: ObList ← NIL;
thisOk: BOOL ← TRUE;
FOR oList: ObList ← oldObList, oList.rest
WHILE oList#
NIL
DO
IF ExistsInstance[oList.first, world]
THEN newObList ← CONS[oList.first, newObList]
ELSE thisOk ← FALSE
ENDLOOP;
IF ~thisOk
THEN {
CDProperties.PutProp[iList.first, satellitesProp, newObList]; allOk ← FALSE};
ENDLOOP;
};
Event Procs for reading and writing satellites from files
satelliteUniqueIDProp:
ATOM ←
PW.RegisterProp[$CDSatelliteUniqueID,
TRUE];
Group number (saved on file)
BeforeOutput: CDEvents.EventProc = {
id: INT ← 0;
AddIDOnWorld:
PROC [world:
CD.InstanceList] = {
FOR list:
CD.InstanceList ← world, list.rest
WHILE list#
NIL
DO
satellites: ObList ← GetSatellites[list.first];
ref: REF INT;
IF satellites=NIL THEN LOOP;
ref ← NEW [INT ← id]; id ← id + 1;
CDProperties.PutPropOnInstance[list.first, satelliteUniqueIDProp, ref];
FOR sats: ObList ← satellites, sats.rest
WHILE sats#
NIL
DO
CDProperties.PutPropOnObject[sats.first, satelliteUniqueIDProp, ref];
ENDLOOP;
ENDLOOP;
};
AddID: CDDirectory.EachEntryAction = {
IF ~CDCells.IsCell[ob] THEN RETURN;
AddIDOnWorld[NARROW [ob.specificRef, CD.CellPtr].contents];
};
[] ← CDDirectory.Enumerate[design, AddID];
AddIDOnWorld[CDOps.InstList[design]];
};
AfterInput: CDEvents.EventProc = {
UseIDOnWorld:
PROC [world:
CD.InstanceList] = {
FOR list:
CD.InstanceList ← world, list.rest
WHILE list#
NIL
DO
master: CD.Instance ← list.first;
satellites: ObList ← NIL;
refInt: REF INT ← NARROW [CDProperties.GetPropFromInstance[master, satelliteUniqueIDProp]];
IF refInt=NIL THEN LOOP;
FOR sats:
CD.InstanceList ← world, sats.rest
WHILE sats#
NIL
DO
sat: CD.Object ← sats.first.ob;
satRefInt: REF INT ← NARROW [CDProperties.GetPropFromObject[sat, satelliteUniqueIDProp]];
IF satRefInt=NIL OR satRefInt^#refInt^ THEN LOOP;
IF ~Member[satellites, sat] THEN satellites ← CONS [sat, satellites];
ENDLOOP;
SetSatellites[master, satellites];
ENDLOOP;
};
UseID: CDDirectory.EachEntryAction = {
IF ~CDCells.IsCell[ob] THEN RETURN;
UseIDOnWorld[NARROW [ob.specificRef, CD.CellPtr].contents];
};
[] ← CDDirectory.Enumerate[design, UseID];
UseIDOnWorld[CDOps.InstList[design]];
};
Event Proc for processing a cell change
AfterCellReplacement: CDEvents.EventProc = {
ob: CD.Object ← NARROW [x];
[] ← EnforceConstraint[NARROW[ob.specificRef, CD.CellPtr].contents];
};
Command Procs
Given a satellite or a master select the whole group.
SelectGroupCommand:
PROC [comm: CDSequencer.Command] = {
world: CD.InstanceList ← CDOps.InstList[comm.design];
multiple: BOOL;
selected: CD.Instance;
numSats: INT;
[selected, multiple] ← CDOps.SelectedInstance[comm.design];
IF ~SingleSelected[selected, multiple] THEN RETURN;
IF CDTexts.IsText[selected.ob]
THEN numSats ← SelectGroup[comm.design, world, FindMaster[world, selected.ob]]
ELSE numSats ← SelectGroup[comm.design, world, selected];
TerminalIO.WriteF["Group selected: %g satellites(s) in group\n", IO.int[numSats]];
};
Adds a single satellite to those already existing for a master.
AddSatelliteCommand:
PROC [comm: CDSequencer.Command] = {
selected: CD.Instance;
multiple: BOOL;
name: ROPE;
font: CDTexts.CDFont ← CDPanelFonts.CurrentFont[comm.design];
text: CD.Object;
[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 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 ← CDTexts.CreateText[name, font];
CDOps.AddAnObject[design: comm.design, ob: text, location: comm.pos, orientation: 0];
AddSatellites[selected, LIST[text]];
TerminalIO.WriteRope["Satellite added\n"];
};
Given zero or more text instances and exactly one non-text instance (the master) remove any previous satellites and add the ones currently selected.
SetSatellitesCommand:
PROC [comm: CDSequencer.Command] = {
world: CD.InstanceList ← CDOps.InstList[comm.design];
satellites: ObList ← NIL;
master: CD.Instance ← NIL;
FOR list:
CD.InstanceList ← world, list.rest
WHILE list#
NIL
DO
instance: CD.Instance ← list.first;
IF ~instance.selected THEN LOOP;
IF CDTexts.IsText[instance.ob]
THEN {
IF Member[satellites, instance.ob] THEN LOOP;
Look if some master in the world already has this satellite
IF FindMaster[world, instance.ob]#
NIL
THEN {
[] ← SelectGroup[comm.design, world, FindMaster[world, instance.ob]];
TerminalIO.WriteRope["\n** Some satellite is already in another group, which is now shown selected--not done.\n"];
RETURN;
};
satellites ← CONS [instance.ob, satellites];
}
ELSE {
IF master#
NIL
THEN {
TerminalIO.WriteRope["\n** More than one master selected--can't do it.\n"];
RETURN;
};
master ← instance;
};
ENDLOOP;
IF master=
NIL
THEN {
TerminalIO.WriteRope["\n** No master selected--can't do it.\n"];
RETURN;
};
SetSatellites[master, satellites];
TerminalIO.WriteRope["Satellites set\n"];
};
Check the satellite constraint and enforce it if it isn't satisfied
EnforceSatelliteConstraintCommand:
PROC [comm: CDSequencer.Command] = {
ok: BOOL ← EnforceConstraint[CDOps.InstList[comm.design]];
IF ok
THEN TerminalIO.WriteRope["Satellite constraint ok.\n"]
ELSE TerminalIO.WriteRope["Satellite constraint NOT ok--enforcing it.\n"];
};
Internal Utilities
ExistsInstance:
PROC [ob:
CD.Object, world:
LIST
OF
CD.Instance]
RETURNS [
BOOL] = {
FOR iList:
LIST
OF
CD.Instance ← world, iList.rest
WHILE iList#
NIL
DO
IF iList.first.ob=ob THEN RETURN [TRUE]
ENDLOOP;
RETURN [FALSE]
};
Member:
PROC [objects: ObList, object:
CD.Object]
RETURNS [
BOOL ←
FALSE] = {
WHILE objects#
NIL
DO
IF objects.first=object THEN RETURN [TRUE]; objects ← objects.rest;
ENDLOOP;
};
FindMaster:
PROC [world:
LIST
OF
CD.Instance, satellite:
CD.Object]
RETURNS [master:
CD.Instance ←
NIL] = {
FOR list:
CD.InstanceList ← world, list.rest
WHILE list#
NIL
DO
IF Member[GetSatellites[list.first], satellite] THEN RETURN [list.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
SelectGroup:
PROC [design:
CD.Design, world:
LIST
OF
CD.Instance, master:
CD.Instance]
RETURNS [numSats:
INT] = {
satellites: ObList;
numSats ← 0;
IF master=NIL THEN RETURN ELSE satellites ← GetSatellites[master];
FOR list:
CD.InstanceList ← world, list.rest
WHILE list#
NIL
DO
list.first.selected ← FALSE;
CDCommandOps.RedrawInstance[design, list.first];
ENDLOOP;
master.selected ← TRUE;
CDCommandOps.RedrawInstance[design, master];
FOR list:
CD.InstanceList ← world, list.rest
WHILE list#
NIL
DO
IF ~Member[satellites, list.first.ob] THEN LOOP;
list.first.selected ← TRUE; numSats ← numSats + 1;
CDCommandOps.RedrawInstance[design, list.first];
ENDLOOP;
};
Initialization
CDMenus.ImplementEntryCommand[menu: $OtherProgramMenu, entry: "Select Group (O-Left)", p: SelectGroupCommand, key: $SelectGroup];
CDMenus.ImplementEntryCommand[menu: $OtherProgramMenu, entry: "Add Satellite (O-Middle)", p: AddSatelliteCommand, key: $AddSatellite, queue: doQueueAndMark];
CDMenus.ImplementEntryCommand[menu: $OtherProgramMenu, entry: "Set Satellites (O-Right)", p: SetSatellitesCommand, key: $SetSatellites, queue: doQueueAndMark];
CDMenus.ImplementEntryCommand[menu: $OtherProgramMenu, entry: "Enforce Satellite Constraint", p: EnforceSatelliteConstraintCommand, key: $EnforceSatelliteConstraint, queue: doQueueAndMark];
CDEvents.RegisterEventProc[$BeforeOutput, BeforeOutput];
CDEvents.RegisterEventProc[$AfterInput, AfterInput];
CDEvents.RegisterEventProc[$AfterCellReplacement, AfterCellReplacement];
END.