DIRECTORY CD, CDCells, CDDirectory, CDEvents, CDCommandOps, CDInstances, CDMenus, CDOps, CDPanelFonts, CDProperties, CDSatellites, CDSequencer, CDTexts, IO, PW, TerminalIO; CDSatellitesImpl: CEDAR PROGRAM IMPORTS CDEvents, CDCommandOps, CDDirectory, CDInstances, CDMenus, CDOps, CDPanelFonts, CDProperties, CDTexts, IO, PW, TerminalIO EXPORTS CDSatellites = BEGIN OPEN CDSatellites; satellitesProp: PUBLIC ATOM _ PW.RegisterProp[$CDSatellites, TRUE]; maxGroupIdProp: PUBLIC ATOM _ PW.RegisterProp[$CDSatellitesMaxGroupId, TRUE]; groupIdProp: PUBLIC ATOM _ PW.RegisterProp[$CDSatellitesGroupId, TRUE]; prevNumMasters: NAT _ 0; AddSatellite: PUBLIC PROC [design: CD.Design, master: CD.Instance, satellite: CD.Instance] = { EnsureSatelliteOnList[master, satellite]; IF CDProperties.GetProp[master, groupIdProp]=NIL THEN { ref: REF INT _ NARROW[CDProperties.GetProp[design, maxGroupIdProp]]; ref^ _ ref^+1; CDProperties.PutProp[master, groupIdProp, NEW [INT _ ref^]]; }; CDProperties.PutProp[satellite, groupIdProp, NEW[INT _ GroupId[master]]]; }; RemoveSatellite: PUBLIC 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] }; GetSatellites: PUBLIC PROC [master: CD.Instance] RETURNS [satellites: InstanceList _ NIL] = { satellites _ NARROW [CDProperties.GetPropFromInstance[master, satellitesProp]]; }; GetSatelliteRopes: PUBLIC PROC [masterProps: CD.PropList] RETURNS [ropes: LIST OF ROPE _ NIL] = { FOR list: InstanceList _ NARROW [CDProperties.GetPropFromList[masterProps, satellitesProp]], list.rest WHILE list#NIL DO rope: ROPE _ NARROW [list.first.ob.specificRef, CDTexts.TextPtr].text; ropes _ CONS [rope, ropes]; ENDLOOP; }; EnforceInvariants: PUBLIC PROC [design: CD.Design, world: InstanceList] RETURNS [ok: BOOL _ TRUE] = { 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[]]]; satelliteList: InstanceList; maxGroupId: INT; TableSize: PROC [] RETURNS [NAT] = INLINE { FOR sizes: LIST OF NAT _ LIST [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]; }; IF CDProperties.GetProp[design, maxGroupIdProp]=NIL THEN CDProperties.PutProp[design, maxGroupIdProp, NEW [INT _ 0]]; 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 satelliteList _ CONS [w.first, satelliteList]} ELSE {IF CDProperties.GetProp[w.first, groupIdProp]#NIL THEN {Store[w.first]; CDProperties.PutProp[w.first, satellitesProp, NIL]}} ENDLOOP; FOR sl: InstanceList _ satelliteList, 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; 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]] }; BeforeOutput: CDEvents.EventProc = { [] _ EnforceInvariants[design, CDOps.InstList[design]] }; AfterInput: CDEvents.EventProc = { EnforceForEachCell: CDDirectory.EachEntryAction = { [] _ EnforceInvariants[design, NARROW[ob.specificRef, CD.CellPtr].contents] }; [] _ CDDirectory.Enumerate[design, EnforceForEachCell]; [] _ EnforceInvariants[design, CDOps.InstList[design]] }; AfterCellReplacement: CDEvents.EventProc = { ob: CD.Object _ NARROW [x]; [] _ EnforceInvariants[design, NARROW[ob.specificRef, CD.CellPtr].contents]; }; SelectGroupCommand: 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 group.\n"]; RETURN }; IF CDTexts.IsText[selected.ob] THEN numSats _ SelectGroup[comm.design, world, FindMaster[world, selected]] ELSE numSats _ SelectGroup[comm.design, world, selected]; TerminalIO.WriteF["Group selected: %g satellites(s) in group.\n", IO.int[numSats]]; }; AddSatelliteCommand: 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 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]; AddSatellite[comm.design, selected, text]; TerminalIO.WriteRope["Satellite added\n"]; }; SetSatellitesCommand: PROC [comm: CDSequencer.Command] = { world: InstanceList _ CDOps.InstList[comm.design]; satellites: InstanceList _ NIL; master: CD.Instance _ NIL; [] _ EnforceInvariants[comm.design, world]; 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}; IF CDProperties.GetProp[master, satellitesProp]#NIL THEN FOR sl: InstanceList _ NARROW [CDProperties.GetProp[master, satellitesProp]], sl.rest WHILE sl#NIL DO RemoveSatellite[world, sl.first]; ENDLOOP; FOR sl: InstanceList _ satellites, sl.rest WHILE sl#NIL DO RemoveSatellite[world, sl.first]; AddSatellite[comm.design, master, sl.first]; ENDLOOP; TerminalIO.WriteRope["Satellites set\n"]; }; 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.GetPropFromInstance[master, satellitesProp]=NIL THEN sats _ LIST[satellite] ELSE { sats _ NARROW[CDProperties.GetPropFromInstance[master, satellitesProp], InstanceList]; IF ~Member[sats, satellite] THEN sats _ CONS[satellite, sats]; }; CDProperties.PutPropOnInstance[master, satellitesProp, sats]; }; Member: PROC [insances: InstanceList, inst: CD.Instance] RETURNS [BOOL _ FALSE] = { WHILE insances#NIL DO IF insances.first=inst THEN RETURN [TRUE]; insances _ insances.rest; ENDLOOP; }; GroupMember: PROC [instances: InstanceList, inst: CD.Instance] RETURNS [BOOL _ FALSE] = { 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]; }; SelectGroup: 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]; master.selected _ TRUE; CDCommandOps.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; CDCommandOps.RedrawInstance[design, w.first]; ENDLOOP; }; IntervalMinDist: PROC [i1min, i1max, i2min, i2max: INT] RETURNS [INT] = INLINE { IF i1maxi2max 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] }; 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]; CDEvents.RegisterEventProc[$BeforeOutput, BeforeOutput]; CDEvents.RegisterEventProc[$AfterInput, AfterInput]; CDEvents.RegisterEventProc[$AfterCellReplacement, AfterCellReplacement]; END. ςCDSatellitesImpl.mesa Copyright c 1985 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 works and is comfortable to use both from programs and interactively. This last version is a complete rewrite, one hopes for the last time! Pradeep Sindhu December 23, 1985 2:51:44 am PST This property hangs on a master. Its value is the list of the master's satellites. This property hangs on a design. Its value is the highest numbered satellite group in the design. This property hangs on each instance that is in a satellite group. Its value is the group's groupId. Public functions Check that maxGroupIdProp exists; if not, put it Make masterTable and satelliteList; at the same time strip masters of their satellite lists For each satellite find its closest master and put it on that master's satellite list Now go through the masterTable and renumber groups with identical group Id's EnforceInvariants: PUBLIC PROC [design: CD.Design, world: InstanceList] RETURNS [ok: BOOL _ TRUE] = { masterList, satelliteList: InstanceList; maxGroupId: INT; Check that maxGroupIdProp exists; if not, put it IF CDProperties.GetProp[design, maxGroupIdProp]=NIL THEN CDProperties.PutProp[design, maxGroupIdProp, NEW [INT _ 0]]; Make masterList and satelliteList, at the same time stripping 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 satelliteList _ CONS [w.first, satelliteList]} ELSE {IF CDProperties.GetProp[w.first, groupIdProp]#NIL THEN {masterList _ CONS [w.first, masterList]; CDProperties.PutProp[w.first, satellitesProp, NIL]}} ENDLOOP; For each satellite find its master and put it on that master's satellite list FOR sl: InstanceList _ satelliteList, 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 _ masterList, 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 and renumber groups with identical group Id's maxGroupId _ NARROW [CDProperties.GetProp[design, maxGroupIdProp], REF INT]^; FOR ml: InstanceList _ masterList, ml.rest WHILE ml#NIL DO IF ~GroupMember[ml.rest, ml.first] THEN LOOP; 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; CDProperties.PutProp[design, maxGroupIdProp, NEW [INT _ maxGroupId]] }; Event Procs for reading and writing satellites from files Event Proc for processing a cell change Command Procs Given a satellite or a master select the whole group. Adds a single satellite to those already existing for a master. Given zero or more text instances and exactly one non-text instance (the master) remove any previous satellites and add the ones currently selected. Find the master and put selected text onto the list of satellites Remove master's old satellites, if any Remove each satellite from its previous master, if any, and put it on to the new one Internal Utilities Returns true if inst is in the list insances Returns true if some instance in intances has the same group Id as inst. Select the satellite group for master Initialization Κ―– "cedar" style˜codešœ™Kšœ Οmœ1™<™:J™\J™ZJ™]J™.—K™/—K™šΟk œ˜ Kšžœžœžœ ˜’—K˜•StartOfExpansion[]šΠblœžœž˜Kšžœhžœžœ ˜Kšžœ˜Kšžœžœ˜K˜šœž œžœžœ˜CJšœR™R—šœž œžœ'žœ˜MJšœa™a—šœ ž œžœ$žœ˜GJšœd™d—K˜Kšœžœ˜Ihead™š Οn œžœžœ žœžœžœ˜^Kšœ)˜)šžœ+žœžœ˜7Kšœžœžœžœ/˜DK˜Kšœ*žœžœ ˜Kšœ"˜"šžœ0žœžœž˜BK˜Kšœ,žœžœ˜Dš žœžœ:žœžœž˜fKšœ,žœžœ˜DKšžœ˜—Kšžœ˜—Kšžœ˜—Kšžœ˜—Kšœ-žœžœ˜DKšœ˜—K˜š œžœžœ žœžœžœžœ™eK™(Kšœ žœ™K™K™0Kšžœ.ž œ-žœžœ™uK™K™^šžœ!žœžœž™2šžœ™Kš žœžœ,žœžœžœ™kKš žœžœ,žœžœžœFžœ™›—Kšžœ™—K™K™Mšžœ+žœžœž™=š  œžœ žœ žœžœžœ™?Kšœžœ™ Kš žœžœžœžœžœ™!Kšœ&™&Kšœ,™,Kšœ(™(Kšžœ;™AK™—Kšœžœ žœ™Kšœžœ™$šžœ(žœžœž™:Kšžœžœžœ™*Kšžœžœ™+Kšžœ™—šžœž™ Kšžœ-žœ™5Kšžœ)™-—Kšžœ™—K™Kšœ8žœ™Kšžœžœ*žœžœ˜AK˜—K˜š œžœ žœžœ˜MKšœ˜šžœ:ž˜?Kšžœžœ ˜šžœ˜KšœžœI˜VKšžœžœžœ˜>K˜——Kšœ=˜=K˜—K˜Kšœ,™,š  œžœ žœ žœžœžœ˜Sšžœ žœžœ˜Kšžœžœžœžœ˜DKšžœ˜—Kšœ˜K˜—K™Hš   œžœ!žœ žœžœžœ˜YKšœ žœ˜šžœ žœžœ˜Kšžœ"žœžœžœ˜SKšžœ˜—Kšœ˜K˜—K˜š   œžœ"žœ žœ žœ žœ˜fKšœžœ˜!šžœ!žœžœž˜2Kšžœžœžœžœ ˜QKšžœ˜—Kšœ˜—K˜š  œžœ žœžœžœžœ˜Ošžœ žœžœ˜KšœB˜BKšžœžœ˜K˜—šžœ žœ˜KšœI˜IKšžœžœ˜K˜—Kšžœžœ˜ Kšœ˜—K™K™%š  œžœ žœžœžœžœžœ žœ žœ˜qKšœ˜Kšœ ˜ K˜Kš žœžœžœžœžœ$˜BKšœžœ˜Kšœ-˜-šžœ!žœžœž˜2Kšžœžœžœ˜*Kšœžœ˜/Kšœ-˜-Kšžœ˜—Kšœ˜—K˜š  œžœžœžœžœžœ˜PKšžœ žœžœ˜)Kšžœ žœžœ˜)Kšžœ˜ K˜—K˜š   œžœžœžœžœžœ˜BKšœ žœ;˜HKšœ žœ;˜HK˜šžœ ˜ Kš žœžœ žœžœžœžœ ˜9Kš žœžœ žœžœ žœžœ˜I—K˜—L™Lšœ™Kšœ˜Kšœ˜KšœŸ˜ŸK˜Kšœ8˜8Kšœ4˜4KšœH˜HK˜Kšžœ˜K˜——…—.rP