<> <> <> <> <<>> DIRECTORY Buttons, CD, CDCells, CDCommandOps, CDEvents, CDLayers, CDPanel, CDPrivate, CDSequencer, CDTipEtc, CDValue, Containers, Labels, Menus, Rope USING [Cat, Concat, ROPE], TerminalIO, UserProfile, ViewerEvents, ViewerClasses, ViewerOps; CDPanelImpl: CEDAR MONITOR IMPORTS Buttons, CD, CDCells, CDCommandOps, CDEvents, CDLayers, CDPrivate, CDSequencer, CDTipEtc, CDValue, Containers, Labels, Rope, TerminalIO, UserProfile, ViewerEvents, ViewerOps EXPORTS CDPanel SHARES CDLayers, CDPrivate = BEGIN Error: ERROR = CODE; panelKey: REF INT = NEW[INT]; -- used instead of atoms, to be really unique panelClassKey: REF INT = NEW[INT]; globalClass: PanelClass = NEW[PanelEntryList_NIL]; Panel: TYPE = REF PanelRecord; -- one per design PanelRecord: TYPE = RECORD [ container: ViewerClasses.Viewer_NIL, design: CD.Design, class: PanelClass_NIL, nextLayerX: CARDINAL, nextLayerY: CARDINAL, nextX: CARDINAL, nextY: CARDINAL, nextSpecialX: CARDINAL, layerLabel: Labels.Label, layerWidths: LIST OF WidthValueRef _ NIL, firstLayer: REF LayerDefine _ NIL, labels: LIST OF REF Tripple _ NIL ]; panelList: LIST OF Panel _ NIL; GetPanel: PROC [design: CD.Design] RETURNS [Panel] = BEGIN x: REF _ CDValue.Fetch[boundTo: design, key: panelKey, propagation: global]; IF x=NIL THEN { panel: Panel = NEW[PanelRecord]; panel.class _ GetPanelClass[design.technology]; panel.design _ design; [] _ CDValue.StoreConditional[design, panelKey, panel]; RETURN [GetPanel[design]] }; RETURN [NARROW[x, Panel]]; END; GetPanelClass: PROC [tech: CD.Technology] RETURNS [PanelClass] = BEGIN IF tech=NIL THEN RETURN [globalClass] ELSE { x: REF _ CDValue.Fetch[boundTo: tech, key: panelClassKey, propagation: technology]; IF x=NIL THEN { panelClass: PanelClass = NEW[PanelEntryList_NIL]; [] _ CDValue.StoreConditional[boundTo: tech, key: panelClassKey, value: panelClass]; RETURN [GetPanelClass[tech]]; }; RETURN [NARROW[x, PanelClass]]; } END; CreatePanel: PUBLIC ENTRY PROC [design: CD.Design] RETURNS [ViewerClasses.Viewer] = <<--only one panel-viewer per design is created>> <<--panel may or may not be updated if definitions occur after first creation>> BEGIN ENABLE UNWIND => NULL; panel: Panel _ GetPanel[design]; panelClass: PanelClass _ GetPanelClass[design.technology]; IF panel.container#NIL AND NOT panel.container.destroyed THEN RETURN [panel.container]; panel.container _ NIL; panel.labels _ NIL; <<--supress creation of viewer if empty>> IF panelClass#NIL AND panelClass^#NIL THEN CreatePanelViewer[panel]; CheckPanelList[panel]; RETURN [panel.container] END; AppendEntryInClass: INTERNAL PROC [class: PanelClass, entry: REF ANY] = BEGIN IF class^=NIL THEN class^ _ LIST[entry] ELSE FOR list: PanelEntryList _ class^, list.rest DO IF list.rest = NIL THEN {list.rest _ LIST[entry]; EXIT} ENDLOOP; END; AppendEntryInViewers: INTERNAL PROC [class: PanelClass, entry: REF ANY] = BEGIN FOR list: LIST OF Panel _ panelList, list.rest WHILE list#NIL DO IF list.first.container#NIL AND ~list.first.container.destroyed THEN { IF list.first.class=class OR class=globalClass THEN NewEntry[list.first, entry]; } ENDLOOP END; CheckPanelList: INTERNAL PROC [p: Panel] = BEGIN copiedList: LIST OF Panel _ NIL; FOR list: LIST OF Panel _ panelList, list.rest WHILE list#NIL DO IF list.first.container#NIL AND ~list.first.container.destroyed AND list.first#p THEN copiedList _ CONS[list.first, copiedList] ENDLOOP; IF p#NIL THEN copiedList _ CONS[p, copiedList]; panelList _ copiedList END; AppendEntry: INTERNAL PROC [class: PanelClass, entry: REF ANY] = BEGIN AppendEntryInClass[class, entry]; AppendEntryInViewers[class, entry]; END; DefineNewLine: PUBLIC ENTRY PROC [tech: CD.Technology] = BEGIN ENABLE UNWIND => NULL; class: PanelClass = GetPanelClass[tech]; AppendEntry[class, $NewLine]; END; DefineButton: PUBLIC ENTRY PROC [tech: CD.Technology_NIL, name: Rope.ROPE, proc: CDSequencer.CommandProc _ NIL, command: ATOM _ NIL, queue: CDSequencer.QueueMethod _ useDefault, topLine: BOOL _ FALSE, border: BOOL _ FALSE,data: REF _ NIL, cdValueKey: REF _ NIL] = BEGIN ENABLE UNWIND => NULL; class: PanelClass = GetPanelClass[tech]; bd: REF ButtonDefine; IF command=NIL AND queue=useDefault THEN queue _ doQueue; bd _ NEW[ButtonDefine _ [ name: name, proc: proc, command: command, queue: queue, data: data, border: border, topLine: topLine, cdValueKey: cdValueKey ]]; AppendEntry[class, bd]; END; DefineLabel: PUBLIC ENTRY PROC [tech: CD.Technology_NIL, name: Rope.ROPE, border: BOOL _ FALSE, cdValueKey: REF _ NIL] = BEGIN ENABLE UNWIND => NULL; class: PanelClass = GetPanelClass[tech]; lab: REF LabelDefine; lab _ NEW[LabelDefine _ [name: name, border: border, cdValueKey: cdValueKey]]; AppendEntry[class, lab]; END; layerDefinitions: REF ARRAY [0..CD.layerNum) OF REF LayerDefine = NEW[ARRAY [0..CD.layerNum) OF REF LayerDefine _ ALL[NIL]]; LayerText: PROC [lev: CD.Layer] RETURNS [Rope.ROPE] = BEGIN RETURN [IF layerDefinitions[lev]#NIL THEN layerDefinitions[lev].text ELSE "not defined" ] END; LayerDefine: TYPE = RECORD [ layer: CD.Layer _ CD.undefLayer, text: Rope.ROPE _ NIL, min, default: CD.Number _ 0 ]; IntDefine: TYPE = RECORD [ cdValueKey: REF, text: Rope.ROPE, min, max, default: INT, lambda: INT _ 1 ]; ButtonDefine: TYPE = RECORD [ name: Rope.ROPE, proc: CDSequencer.CommandProc _ NIL, command: ATOM _ NIL, queue: CDSequencer.QueueMethod, topLine: BOOL _ FALSE, border: BOOL _ FALSE, data: REF _ NIL, cdValueKey: REF _ NIL ]; LabelDefine: TYPE = RECORD [ name: Rope.ROPE, cdValueKey: REF, border: BOOL _ FALSE ]; PanelEntryList: TYPE = LIST OF REF ANY; -- one per CD.Technology PanelClass: TYPE = REF PanelEntryList; DefineLayerEntry: PUBLIC ENTRY PROC [tech: CD.Technology, lev: CD.Layer, text: Rope.ROPE, min, default: CD.Number_1] = BEGIN ENABLE UNWIND => NULL; panelClass: PanelClass = GetPanelClass[tech]; IF lev=CD.undefLayer THEN ERROR; IF layerDefinitions[lev]#NIL THEN RETURN WITH ERROR CD.Error[doubleRegistration, "re-registers layer"]; layerDefinitions[lev] _ NEW[LayerDefine _ [ layer: lev, text: text, min: min, default: default ]]; AppendEntry[panelClass, layerDefinitions[lev]]; CDLayers.SetLayerWidth[design: NIL --!!! it sets default--, layer: lev, width: default]; END; NotifyCurrentLayer: CDLayers.DesignNotifyProc = BEGIN panel: Panel = GetPanel[design]; IF panel.layerLabel#NIL THEN { l: CD.Layer _ CDLayers.CurrentLayer[design]; Labels.Set[panel.layerLabel, LayerText[l]]; } END; NotifyLayerWidth: CDLayers.LayerNotifyProc = BEGIN panel: Panel = GetPanel[design]; IF panel.layerLabel#NIL THEN { FOR list: LIST OF WidthValueRef _ panel.layerWidths, list.rest WHILE list#NIL DO IF list.first.layerDefine.layer=layer THEN { Labels.Set[list.first.numbLab, CDCommandOps.LambdaRope[ n: CDLayers.LayerWidth[design, layer], lambda: design.technology.lambda ] ]; EXIT; }; ENDLOOP; } END; <<>> <<-- Viewer Stuff -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->> topY: CARDINAL = 1; entryHSpace: CARDINAL = 3; entryHeight: CARDINAL = 12; shorterEntryVSpace: CARDINAL = 1; Caption: PROC [panel: Panel] RETURNS [Rope.ROPE] = BEGIN RETURN [Rope.Cat[ IF panel.design.name#NIL THEN panel.design.name ELSE "no name", " ", panel.design.technology.name, " ", CDCells.PushedCellName[panel.design] ]]; END; CreatePanelViewer: INTERNAL PROC [panel: Panel] = BEGIN MakeClassEntries: INTERNAL PROC [class: PanelClass] = <<--uses intermediate layer variables!>> BEGIN FOR l: PanelEntryList _ class^, l.rest WHILE l#NIL DO NewEntry[panel, l.first]; ENDLOOP; END; TerminalIO.WriteRope["Create a control panel\n"]; IF panel=NIL OR panel.design=NIL OR panel.design.technology=NIL THEN { TerminalIO.WriteRope["not registered properly\n"]; ERROR }; panel.labels _ NIL; panel.layerWidths _ NIL; panel.nextLayerX _ panel.nextX _ 0; panel.nextY _ topY; panel.nextLayerY _ topY+entryHeight+entryHSpace; panel.container _ Containers.Create[[ name: Caption[panel], openHeight: CDValue.FetchInt[ boundTo: panel.design, key: $PanelHeight, propagation: global, ifNotFound: 120], scrollable: TRUE, iconic: UserProfile.Boolean["ChipNDale.ControlViewerOpenIconic", FALSE], icon: CDTipEtc.GetPanelIcon[panel.design], column: right ]]; ViewerOps.AddProp[panel.container, $CDPanelPrivate, panel]; [] _ NextLabel[panel, "current layer:"]; panel.layerLabel _ NextLabel[panel: panel, extraSpaces: 2, name: LayerText[CDLayers.CurrentLayer[panel.design]], extraWidth: 2 ]; [] _ NextLabel[panel, "| "]; panel.nextSpecialX _ panel.nextX; panel.nextY _ 3*(entryHeight+entryHSpace); panel.nextX _ 0; MakeClassEntries[panel.class]; MakeClassEntries[globalClass]; IF panel.firstLayer#NIL THEN CDLayers.SetCurrentLayer[panel.design, panel.firstLayer.layer]; END; NewEntry: INTERNAL PROC[panel: Panel, entry: REF] = BEGIN IF panel.container.destroyed THEN RETURN; WITH entry SELECT FROM layerDefine: REF LayerDefine => { IF panel.firstLayer=NIL THEN panel.firstLayer _ layerDefine; CreateLayerEntry[panel, layerDefine]; }; intDef: REF IntDefine => CreateIntEntry[panel, intDef]; butDef: REF ButtonDefine => CreateButtEntry[panel, butDef]; labDef: REF LabelDefine => [] _ CreateLabEntry[panel, labDef]; a: ATOM => { SELECT a FROM $NewLine => CreateLnEntry[panel]; ENDCASE => ERROR; }; ENDCASE => ERROR; END; RepaintCaptions: ENTRY CDEvents.EventProc = BEGIN panel: Panel _ GetPanel[design]; IF panel.container#NIL THEN { panel.container.name _ Caption[panel]; ViewerOps.PaintViewer[panel.container, caption]; }; END; WidthValueRec: TYPE = RECORD [ layerDefine: REF LayerDefine, panel: Panel, numbLab: Labels.Label _ NIL ]; WidthValueRef: TYPE = REF WidthValueRec; NextIntButton: INTERNAL PROC [panel: Panel, label: Rope.ROPE, proc: Buttons.ButtonProc, border: BOOL _ FALSE, clientData: REF ANY _ NIL] = BEGIN yoff: INT = Containers.ScrollOffset[panel.container]; button: Buttons.Button = Buttons.Create[ info: [ name: label, wx: panel.nextX, wy: panel.nextY+yoff, wh: entryHeight, parent: panel.container, border: border ], clientData: clientData, proc: proc ]; panel.nextX _ button.wx+button.ww+entryHSpace; END; NextButton: INTERNAL PROC [panel: Panel, label: Rope.ROPE, proc: Buttons.ButtonProc, clientData: REF ANY _ NIL, topLine: BOOL _ FALSE, border: BOOL _ FALSE] RETURNS [button: Buttons.Button] = BEGIN yoff: INT = Containers.ScrollOffset[panel.container]; x: INT _ panel.nextX; y: INT _ panel.nextY; IF topLine THEN {x _ panel.nextSpecialX; y _ 0}; button _ Buttons.Create[ info: [ name: label, wx: x, wy: y+yoff, wh: entryHeight, parent: panel.container, border: border ], clientData: clientData, proc: proc ]; IF topLine THEN panel.nextSpecialX _ button.wx+button.ww+entryHSpace ELSE panel.nextX _ button.wx+button.ww+entryHSpace END; NextLabel: INTERNAL PROC [panel: Panel, name: Rope.ROPE, border: BOOL _ FALSE, extraSpaces: CARDINAL _ 0, extraWidth: CARDINAL _ 0] RETURNS [Labels.Label] = BEGIN yoff: INT = Containers.ScrollOffset[panel.container]; label: Labels.Label; IF name=NIL THEN name _ " "; WHILE extraWidth>0 DO name _ Rope.Concat[base: name, rest: " "]; extraWidth _ extraWidth-1; ENDLOOP; label _ Labels.Create[ info: [ name: name, wx: panel.nextX+extraSpaces*entryHSpace, wy: panel.nextY+yoff, wh: entryHeight, parent: panel.container, border: border ] ]; panel.nextX _ label.wx+label.ww+entryHSpace; RETURN [label] END; CreateIntEntry: INTERNAL PROC [panel: Panel, intDef: REF IntDefine] = BEGIN initialValue: Rope.ROPE; intRef: REF Tripple _ NEW[Tripple _ [panel: panel, define: intDef]]; lambda: INT _ intDef.lambda; IF lambda<=0 THEN lambda _ panel.design.technology.lambda; [] _ NextIntButton[panel: panel, label: intDef.text, proc: IntCalled, clientData: intRef]; initialValue _ CDCommandOps.LambdaRope[ n: CDValue.FetchInt[ boundTo: panel.design, key: intDef.cdValueKey, propagation: technology, ifNotFound: intDef.default ], lambda: lambda ]; intRef.feedback _ NextLabel[panel: panel, name: initialValue, extraWidth: 9]; END; Tripple: TYPE = RECORD [panel: Panel, define: REF _ NIL, feedback: REF _ NIL]; CreateLabEntry: INTERNAL PROC [panel: Panel, labDef: REF LabelDefine] RETURNS [tripple: REF Tripple_NIL] = BEGIN lab: Labels.Label _ NextLabel[ panel: panel, name: labDef.name, border: labDef.border ]; IF labDef.cdValueKey#NIL THEN { tripple _ NEW[Tripple _ [panel: panel, define: labDef, feedback: lab]]; panel.labels _ CONS[tripple, panel.labels]; RedisplayLabel[tripple]; } END; RedisplayLabel: PROC [tripple: REF Tripple] = BEGIN IF tripple=NIL THEN RETURN; WITH tripple.define SELECT FROM labDef: REF LabelDefine => { x: REF = CDValue.Fetch[ boundTo: tripple.panel.design, key: labDef.cdValueKey, propagation: global ]; Labels.Set[NARROW[tripple.feedback], CDCommandOps.ToRope[x, "-?-"]]; }; butDef: REF ButtonDefine => { x: REF = CDValue.Fetch[ boundTo: tripple.panel.design, key: butDef.cdValueKey, propagation: global ]; Buttons.ReLabel[NARROW[tripple.feedback], CDCommandOps.ToRope[x, "-?-"]]; }; ENDCASE => WITH tripple.feedback SELECT FROM t: REF Tripple => RedisplayLabel[t] ENDCASE => NULL; END; RedisplayLabels: PUBLIC PROC [design: CD.Design] = BEGIN panel: Panel = GetPanel[design]; IF panel.container=NIL OR panel.container.destroyed THEN RETURN; FOR list: LIST OF REF Tripple _ panel.labels, list.rest WHILE list#NIL DO RedisplayLabel[list.first]; ENDLOOP; END; CreateButtEntry: INTERNAL PROC [panel: Panel, butDef: REF ButtonDefine] = BEGIN but: Buttons.Button = NextButton[ panel: panel, label: butDef.name, proc: ButtonCalled, border: butDef.border, topLine: butDef.topLine, clientData: NEW[Tripple _ [panel: panel, define: butDef]] ]; IF butDef.cdValueKey#NIL THEN { tripple: REF Tripple _ NEW[Tripple _ [panel: panel, define: butDef, feedback: but]]; panel.labels _ CONS[tripple, panel.labels]; RedisplayLabel[tripple]; } END; ButtonCalled: Buttons.ButtonProc = BEGIN tripple: REF Tripple = NARROW[clientData]; butDef: REF ButtonDefine = NARROW[tripple.define]; comm: CDSequencer.Command = NEW[CDSequencer.CommandRec_[ data: butDef.data, design: tripple.panel.design ]]; IF butDef.proc#NIL THEN CDSequencer.ExecuteProc[comm: comm, proc: butDef.proc, queue: butDef.queue] ELSE IF butDef.command#NIL THEN CDSequencer.ExecuteCommand[comm: comm, key: butDef.command, queue: butDef.queue]; END; Double: PROC[i: INT] RETURNS [INT] = INLINE { IF i(FIRST[INT]/2) THEN i _ i+i; RETURN [i] }; Inc: PROC[i: INT, x: INT_1] RETURNS [INT] = INLINE { IF i<=LAST[INT]-x THEN i _ i+x; RETURN [i] }; Dec: PROC[i: INT, x: INT_1] RETURNS [INT] = INLINE { IF i>=FIRST[INT]+x THEN i _ i-x; RETURN [i] }; GetAnInt: PROC [old, default: INT, label: Rope.ROPE, lambda: INT_1] RETURNS [new: INT] = BEGIN ENABLE TerminalIO.UserAbort => GOTO aborted; choice: LIST OF Rope.ROPE; new _ old; IF lambda=1 THEN choice _ LIST["+1", "-1", "*2", "/2", "default", "type"] ELSE choice _ LIST["+1", "-1", "*2", "/2", "default", "type", "type int", "trunc"]; SELECT TerminalIO.RequestSelection[label: label, choice: choice] FROM 1 => new _ Inc[old, lambda]; 2 => new _ Dec[old, lambda]; 3 => {new _ Double[old]}; 4 => {new _ old/2}; 5 => {new _ default}; 6 => {new _ TerminalIO.RequestInt["type value > "]*lambda}; 7 => {new _ TerminalIO.RequestInt["type value (int) > "]}; 8 => {new _ old/lambda*lambda}; ENDCASE => TerminalIO.WriteRope["Skipped\n"]; EXITS aborted => NULL; END; IntCalled: Buttons.ButtonProc = BEGIN tripple: REF Tripple = NARROW[clientData]; CDSequencer.ExecuteProc[ proc: SerializedIntCalled, design: tripple.panel.design, queue: doQueue, comm: NEW[CDSequencer.CommandRec _ [ data: clientData, key: SELECT mouseButton FROM red => $left, yellow => $middle, blue => $right, ENDCASE => NIL, n: IF control THEN 1 ELSE 0, b: shift ]] ]; END; SerializedIntCalled: PROC [comm: CDSequencer.Command] = BEGIN ENABLE UNWIND => NULL; i: INT; tripple: REF Tripple = NARROW[comm.data]; intDef: REF IntDefine = NARROW[tripple.define]; lambda: INT _ intDef.lambda; IF lambda<=0 THEN lambda _ tripple.panel.design.technology.lambda; i _ CDValue.FetchInt[ boundTo: tripple.panel.design, key: intDef.cdValueKey, propagation: global, ifNotFound: intDef.default ]; IF comm.b THEN { IF comm.n=1 THEN i _ i/lambda*lambda ELSE i _ GetAnInt[i, intDef.default, intDef.text, lambda] } ELSE IF comm.key=$right THEN { IF comm.n=1 THEN i_i/2 ELSE i _ Dec[i, lambda] } ELSE IF comm.key=$left THEN { IF comm.n=1 THEN i _ Double[i] ELSE i _ Inc[i, lambda] }; i _ MAX[MIN[i, intDef.max], intDef.min]; CDValue.StoreInt[ boundTo: tripple.panel.design, key: intDef.cdValueKey, value: i ]; Labels.Set[NARROW[tripple.feedback], CDCommandOps.LambdaRope[n: i, lambda: lambda]]; END; CreateLnEntry: INTERNAL PROC [panel: Panel] = BEGIN panel.nextY _ panel.nextY+entryHSpace+entryHeight; panel.nextX _ 0; END; CreateLayerEntry: INTERNAL PROC [panel: Panel, layerDefine: REF LayerDefine] = BEGIN yoff: INT = Containers.ScrollOffset[panel.container]; p: WidthValueRef _ NEW[WidthValueRec _ WidthValueRec[layerDefine: layerDefine, panel: panel] ]; button: Buttons.Button = Buttons.Create[ info: [ name: layerDefine.text, wx: panel.nextLayerX, wy: panel.nextLayerY+yoff, wh: entryHeight, parent: panel.container, border: FALSE ], clientData: p, proc: WireWidthCalled ]; p.numbLab _ Labels.Create[ info: [ wx: panel.nextLayerX, wy: panel.nextLayerY+shorterEntryVSpace+entryHeight+yoff, wh: entryHeight, ww: button.ww+4*entryHSpace, --2 spaces for real; 2 covering leading blank of next # parent: panel.container, border: FALSE, name: CDCommandOps.LambdaRope[ n: CDLayers.LayerWidth[panel.design, layerDefine.layer], lambda: panel.design.technology.lambda ] ] ]; panel.nextLayerX _ button.wx+button.ww+2*entryHSpace; panel.layerWidths _ CONS[p, panel.layerWidths]; END; WireWidthCalled: Buttons.ButtonProc = BEGIN wireRef: WidthValueRef = NARROW[clientData]; CDSequencer.ExecuteProc[ proc: SerializedWireWidthCalled, design: wireRef.panel.design, queue: doQueue, comm: NEW[CDSequencer.CommandRec _ [ data: clientData, key: SELECT mouseButton FROM red => $left, yellow => $middle, blue => $right, ENDCASE => NIL, n: IF control THEN 1 ELSE 0, b: shift ]] ]; END; SerializedWireWidthCalled: PROC[comm: CDSequencer.Command] = BEGIN wireRef: WidthValueRef = NARROW[comm.data]; lev: CD.Layer = wireRef.layerDefine.layer; design: CD.Design _ wireRef.panel.design; width: CD.Number _ CDLayers.LayerWidth[design, lev]; lambda: INT _ design.technology.lambda; IF comm.b THEN { IF comm.n=1 THEN width _ width/lambda*lambda ELSE width _ GetAnInt[width, wireRef.layerDefine.default, wireRef.layerDefine.text, lambda] } ELSE IF comm.key=$right THEN { IF comm.n=1 THEN width _ width/2 ELSE width _ Dec[width, lambda] } ELSE IF comm.key=$left THEN { IF comm.n=1 THEN width _ Double[width] ELSE width _ Inc[width, lambda] } ELSE IF comm.key=$middle THEN { CDLayers.SetCurrentLayer[design, lev]; RETURN }; width _ MAX[width, 0]; CDLayers.SetLayerWidth[design, lev, width]; END; <<>> DefineIntEntry: PUBLIC ENTRY PROC [tech: CD.Technology, cdValueKey: REF, text: Rope.ROPE, min: INT, max: INT, default: INT, lambda: INT] = BEGIN ENABLE UNWIND => NULL; class: PanelClass = GetPanelClass[tech]; def: REF IntDefine = NEW[IntDefine _ [cdValueKey: cdValueKey, text: text, min: min, max: max, default: default, lambda: lambda]]; AppendEntry[class, def]; CDValue.StoreInt[boundTo: tech, key: cdValueKey, value: default]; END; ContainerDeleted: ViewerEvents.EventProc = { <<--break circularities (for garbage collection)>> WITH ViewerOps.FetchProp[viewer, $CDPanelPrivate] SELECT FROM panel: Panel => { ViewerOps.AddProp[viewer, $CDPanelPrivate, NIL]; IF panel.container=viewer AND panel.design#NIL THEN { CDValue.Store[panel.design, panelKey, NIL]; panel.container _ NIL; panel.design _ NIL; panel.class _ NIL; panel.layerLabel _ NIL; panel.layerWidths _ NIL; panel.firstLayer _ NIL; panel.labels _ NIL; }; }; ENDCASE => NULL; }; <<>> EnumDesigns: PROC[p: CDPrivate.DesignEnumerator] RETURNS [quit: BOOL_FALSE] = BEGIN FOR list: LIST OF Panel _ panelList, list.rest WHILE list#NIL AND ~quit DO IF list.first.container#NIL AND ~list.first.container.destroyed THEN { d: CD.Design _ list.first.design; IF d#NIL THEN quit _ p[d]; } ENDLOOP; END; <<>> <<--module initialization>> [] _ CDValue.RegisterKey[panelKey, NIL]; [] _ CDValue.RegisterKey[panelClassKey, NIL]; CDValue.RegisterKey[key: $PanelHeight]; CDEvents.RegisterEventProc[$RenameDesign, RepaintCaptions]; CDEvents.RegisterEventProc[$AfterPush, RepaintCaptions]; CDEvents.RegisterEventProc[$AfterPop, RepaintCaptions]; CDLayers.RegisterNotifiers[layer: NotifyCurrentLayer, width: NotifyLayerWidth]; [] _ ViewerEvents.RegisterEventProc[ContainerDeleted, destroy, $Container]; CDPrivate.InstallDesignEnumerator[EnumDesigns]; END.