<> <> <> <> <<>> DIRECTORY Atom, Buttons, CD, CDEvents, CDExtras, CDOps, CDPanel, CDPrivate, CDDefaults, CDSequencer, CDTipEtc, CDValue, Containers, IO, Labels, Menus, MBQueue, NumberLabels, Rope USING [Cat, Concat, ROPE], TerminalIO, UserProfile, ViewerClasses, ViewerOps; CDPanelImpl: CEDAR MONITOR IMPORTS Atom, Buttons, CD, CDEvents, CDExtras, CDDefaults, CDOps, CDSequencer, CDTipEtc, CDValue, Containers, IO, Labels, MBQueue, NumberLabels, Rope, TerminalIO, UserProfile, ViewerOps EXPORTS CDPanel SHARES CDDefaults = 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, firstLayer: REF LayerDefine _ NIL, labels: LIST OF REF Tripple _ NIL ]; panelList: LIST OF Panel _ NIL; EntryGetPanel: ENTRY PROC [design: CD.Design] RETURNS [Panel] = BEGIN RETURN [GetPanel[design]] END; GetPanel: INTERNAL PROC [design: CD.Design] RETURNS [Panel] = BEGIN x: REF ANY _ 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.Store[design, panelKey, panel]; RETURN [panel] }; RETURN [NARROW[x, Panel]]; END; GetPanelClass: INTERNAL PROC [tech: CD.Technology] RETURNS [PanelClass] = BEGIN IF tech=NIL THEN RETURN [globalClass] ELSE { x: REF ANY _ CDValue.Fetch[boundTo: tech, key: panelClassKey, propagation: technology]; IF x=NIL THEN { panelClass: PanelClass = NEW[PanelEntryList_NIL]; CDValue.Store[boundTo: tech, key: panelClassKey, value: panelClass]; RETURN [panelClass] }; 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.combined, text: Rope.ROPE _ NIL, min, default: CD.DesignNumber _ 0 ]; IntDefine: TYPE = RECORD [ cdValueKey: REF, text: Rope.ROPE, min, max, default: INT ]; 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.DesignNumber_1] = BEGIN ENABLE UNWIND => NULL; panelClass: PanelClass = GetPanelClass[tech]; IF lev=CD.combined 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]]; CDDefaults.SetLayerWidth[design: tech --!!! it sets default--, layer: lev, width: default]; END; StoreDefaultLayer: PUBLIC ENTRY PROC [design: CD.Design, l: CD.Layer] = BEGIN ENABLE UNWIND => NULL; p: Panel _ GetPanel[design]; CDDefaults.SetCurrentLayer[design, l]; IF p.layerLabel#NIL THEN Labels.Set[p.layerLabel, LayerText[l]]; END; StoreWidth: PUBLIC PROC [design: CD.Design, l: CD.Layer, width: CD.DesignNumber] = BEGIN CDDefaults.SetLayerWidth[design: design, layer: l, width: width] END; FetchWidth: PUBLIC PROC [design: CD.Design, layer: CD.Layer] RETURNS [CD.DesignNumber] = BEGIN RETURN [CDDefaults.LayerWidth[design, layer]] END; FetchDefaultLayer: PUBLIC PROC [design: CD.Design] RETURNS [CD.Layer] = BEGIN RETURN [CDDefaults.CurrentLayer[design]] END; <<>> <<>> <<-- Viewer Stuff -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -->> entryHSpace: CARDINAL = 3; entryHeight: CARDINAL = 12; shorterEntryVSpace: CARDINAL = 1; Caption: PROC [panel: Panel] RETURNS [Rope.ROPE] = BEGIN TechnologyName: PROC [t: CD.Technology] RETURNS [Rope.ROPE] = INLINE { RETURN [IF t.name#NIL THEN t.name ELSE Atom.GetPName[t.key]] }; RETURN [Rope.Cat[ IF panel.design.name#NIL THEN panel.design.name ELSE "no name", " ", TechnologyName[panel.design.technology], " ", CDExtras.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.nextLayerX _ panel.nextX _ panel.nextY _ 0; panel.nextLayerY _ 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 ]]; [] _ NextLabel[panel, "current layer:"]; panel.layerLabel _ NextLabel[panel: panel, extraSpaces: 2, name: LayerText[FetchDefaultLayer[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 { <<--does not work: deadlock: StoreDefaultLayer[panel.design, firstLayer.layer];>> <<--so do the work inline>> CDDefaults.SetCurrentLayer[panel.design, panel.firstLayer.layer]; Labels.Set[panel.layerLabel, LayerText[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: NumberLabels.NumberLabel_NIL ]; WidthValueRef: TYPE = REF WidthValueRec; NextMBQButton: 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 = MBQueue.CreateButton[q: panel.design.queue, 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; NextNumberLabel: INTERNAL PROC [panel: Panel, value: INT, extraSpaces: CARDINAL_0] RETURNS [NumberLabels.NumberLabel] = BEGIN yoff: INT = Containers.ScrollOffset[panel.container]; nl: NumberLabels.NumberLabel _ NumberLabels.CreateNumber[ info: [ wx: panel.nextX+extraSpaces*entryHSpace, wy: panel.nextY+yoff, wh: entryHeight, parent: panel.container, border: FALSE], chars: 4, initialValue: value ]; panel.nextX _ nl.wx+nl.ww+entryHSpace; RETURN [nl] 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 intRef: REF Tripple _ NEW[Tripple _ [panel: panel, define: intDef]]; [] _ NextMBQButton[panel: panel, label: intDef.text, proc: IntCalled, clientData: intRef]; [intRef.feedback] _ NextNumberLabel[panel: panel, value: CDValue.FetchInt[ boundTo: panel.design, key: intDef.cdValueKey, propagation: technology, ifNotFound: intDef.default ] ]; END; Tripple: TYPE = RECORD [panel: Panel, define: REF _ NIL, feedback: REF _ NIL]; ToRope: PROC [x: REF] RETURNS [Rope.ROPE] = BEGIN RETURN [ WITH x SELECT FROM r: Rope.ROPE => r, i: REF INT => IO.PutFR["%0g", IO.int[i^]], a: ATOM => Atom.GetPName[a], l: CDPrivate.LayerRef => IF layerDefinitions[l.number]#NIL THEN layerDefinitions[l.number].text ELSE CDOps.LayerName[l.number], ENDCASE => "-?-" ] END; 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], ToRope[x]]; }; butDef: REF ButtonDefine => { x: REF = CDValue.Fetch[ boundTo: tripple.panel.design, key: butDef.cdValueKey, propagation: global ]; Buttons.ReLabel[NARROW[tripple.feedback], 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 = EntryGetPanel[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, command: 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] RETURNS [INT] = INLINE { IF iFIRST[INT] THEN i _ i-1; RETURN [i] }; GetAnInt: PROC [old, default: INT, label: Rope.ROPE] RETURNS [new: INT] = BEGIN ENABLE TerminalIO.UserAbort => GOTO aborted; new _ old; SELECT TerminalIO.RequestSelection[label: label, choice: LIST["+1", "-1", "*2", "/2", IO.PutFR["_%0g", IO.int[default]], "type"] ] FROM 1 => new _ Inc[old]; 2 => new _ Dec[old]; 3 => {new _ Double[old]}; 4 => {new _ old/2}; 5 => {new _ default}; 6 => {new _ TerminalIO.RequestInt["type value > "]}; ENDCASE => TerminalIO.WriteRope["Skipped\n"]; EXITS aborted => NULL; END; IntCalled: ENTRY Buttons.ButtonProc = <<--clientData: REF ANY, mouseButton: { red, yellow, blue }>> BEGIN ENABLE UNWIND => NULL; i: INT; tripple: REF Tripple = NARROW[clientData]; intDef: REF IntDefine = NARROW[tripple.define]; i _ CDValue.FetchInt[ boundTo: tripple.panel.design, key: intDef.cdValueKey, propagation: global, ifNotFound: intDef.default ]; IF shift THEN i _ GetAnInt[i, intDef.default, intDef.text] ELSE IF mouseButton=Menus.MouseButton[blue] THEN {IF control THEN i_i/2 ELSE i _ Dec[i]} ELSE IF mouseButton=Menus.MouseButton[red] THEN {IF control THEN i _ Double[i] ELSE i _ Inc[i]}; i _ MIN[i, intDef.max]; i _ MAX[i, intDef.min]; CDValue.StoreInt[ boundTo: tripple.panel.design, key: intDef.cdValueKey, value: i ]; NumberLabels.NumberLabelUpdate[NARROW[tripple.feedback], i] 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 = MBQueue.CreateButton[q: panel.design.queue, info: [ name: layerDefine.text, wx: panel.nextLayerX, wy: panel.nextLayerY+yoff, wh: entryHeight, parent: panel.container, border: FALSE ], clientData: p, proc: WireWidthCalled ]; p.numbLab _ NumberLabels.CreateNumber[ info: [ wx: panel.nextLayerX, wy: panel.nextLayerY+shorterEntryVSpace+entryHeight+yoff, wh: entryHeight, parent: panel.container, border: FALSE], chars: 3, initialValue: FetchWidth[panel.design, layerDefine.layer]/CD.lambda ]; panel.nextLayerX _ button.wx+button.ww+2*entryHSpace; END; WireWidthCalled: Buttons.ButtonProc = <<--clientData: REF ANY, mouseButton: { red, yellow, blue }>> BEGIN ENABLE UNWIND => NULL; wireRef: WidthValueRef = NARROW[clientData]; lev: CD.Layer = wireRef.layerDefine.layer; design: CD.Design _ wireRef.panel.design; width: CD.DesignNumber _ FetchWidth[design, lev]/CD.lambda; IF shift THEN width _ GetAnInt[width, wireRef.layerDefine.default/CD.lambda, wireRef.layerDefine.text] ELSE IF mouseButton=Menus.MouseButton[blue] THEN {IF control THEN width _ width/2 ELSE width _ Dec[width]} ELSE IF mouseButton=Menus.MouseButton[red] THEN {IF control THEN width _ Double[width] ELSE width _ Inc[width]} ELSE IF mouseButton=Menus.MouseButton[yellow] THEN { StoreDefaultLayer[design, lev]; RETURN }; width _ MAX[width, 0]; StoreWidth[design, lev, width*CD.lambda]; NumberLabels.NumberLabelUpdate[wireRef.numbLab, width] END; DefineIntEntry: PUBLIC ENTRY PROC [tech: CD.Technology, cdValueKey: REF, text: Rope.ROPE_NIL, min: INT _ FIRST[INT], max: INT _ LAST[INT], default: INT _ 1] = <<--cdValueKey must have been correctly registered with CDValue! CDPanel does NOT itself. >> <<--(needed to allow hooking entries onto already used keys)>> <<--Restriction: displayed value does not follow changes of CDValue.StoreInt>> BEGIN ENABLE UNWIND => NULL; class: PanelClass = GetPanelClass[tech]; def: REF IntDefine = NEW[IntDefine _ [ cdValueKey: cdValueKey, text: text, min: min, max: max, default: default ]]; AppendEntry[class, def]; CDValue.StoreInt[boundTo: tech, key: cdValueKey, value: default]; END; <<--module initialization>> [] _ CDValue.EnregisterKey[panelKey, NIL]; [] _ CDValue.EnregisterKey[panelClassKey, NIL]; CDValue.EnregisterKey[key: $PanelHeight]; CDEvents.RegisterEventProc[$RenameDesign, RepaintCaptions]; CDEvents.RegisterEventProc[$AfterPush, RepaintCaptions]; CDEvents.RegisterEventProc[$AfterPop, RepaintCaptions]; END.