CDPanelImpl.mesa (part of ChipNDale)
Copyright © 1983, 1985 by Xerox Corporation. All rights reserved.
by Christian Jacobi, August 8, 1983 5:20 pm
last edited by Christian Jacobi, January 16, 1986 12:19:22 pm PST
DIRECTORY
Buttons,
CD,
CDEvents,
CDExtras,
CDLayers,
CDPanel,
CDPanelExtras,
CDPrivate,
CDSequencer,
CDTipEtc,
CDValue,
Containers,
Labels,
Menus,
MBQueue,
Rope USING [Cat, Concat, ROPE],
TerminalIO,
UserProfile,
ViewerEvents,
ViewerClasses,
ViewerOps;
CDPanelImpl: CEDAR MONITOR
IMPORTS Buttons, CD, CDEvents, CDExtras, CDLayers, CDSequencer, CDTipEtc, CDValue, Containers, Labels, MBQueue, Rope, TerminalIO, UserProfile, ViewerEvents, ViewerOps
EXPORTS CDPanel, CDPanelExtras
SHARES CDLayers =
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: ATOMNIL, queue: CDSequencer.QueueMethod ← useDefault, topLine: BOOLFALSE, border: BOOLFALSE,data: REFNIL, cdValueKey: REFNIL] =
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: BOOLFALSE, cdValueKey: REFNIL] =
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.ROPENIL,
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: ATOMNIL,
queue: CDSequencer.QueueMethod,
topLine: BOOLFALSE,
border: BOOLFALSE,
data: REFNIL,
cdValueKey: REFNIL
];
LabelDefine: TYPE = RECORD [
name: Rope.ROPE,
cdValueKey: REF,
border: BOOLFALSE
];
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𡤁] =
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]];
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,
CDExtras.ToLambda[
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,
" ",
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.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;
NextMBQButton: INTERNAL PROC [panel: Panel,
label: Rope.ROPE, proc: Buttons.ButtonProc, border: BOOLFALSE, clientData: REF ANYNIL] =
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 ANYNIL, topLine: BOOLFALSE, border: BOOLFALSE] 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: BOOLFALSE, 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;
[] ← NextMBQButton[panel: panel, label: intDef.text, proc: IntCalled, clientData: intRef];
initialValue ← CDExtras.ToLambda[
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: REFNIL];
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], CDExtras.ToRope[x, "-?-"]];
};
butDef: REF ButtonDefine => {
x: REF = CDValue.Fetch[
boundTo: tripple.panel.design,
key: butDef.cdValueKey,
propagation: global
];
Buttons.ReLabel[NARROW[tripple.feedback], CDExtras.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, command: butDef.command, queue: butDef.queue];
END;
Double: PROC[i: INT] RETURNS [INT] = INLINE {
IF i<LAST[INT]/2 AND i>(FIRST[INT]/2) THEN i ← i+i;
RETURN [i]
};
Inc: PROC[i: INT, x: INT𡤁] RETURNS [INT] = INLINE {
IF i<=LAST[INT]-x THEN i ← i+x;
RETURN [i]
};
Dec: PROC[i: INT, x: INT𡤁] RETURNS [INT] = INLINE {
IF i>=FIRST[INT]+x THEN i ← i-x;
RETURN [i]
};
GetAnInt: PROC [old, default: INT, label: Rope.ROPE, lambda: INT𡤁] 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: 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];
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 shift THEN {
IF control THEN i ← i/lambda*lambda
ELSE i ← GetAnInt[i, intDef.default, intDef.text, lambda]
}
ELSE IF mouseButton=Menus.MouseButton[blue] THEN {
IF control THEN i←i/2 ELSE i ← Dec[i, lambda]
}
ELSE IF mouseButton=Menus.MouseButton[red] THEN {
IF control 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], CDExtras.ToLambda[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 = 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 ← 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: CDExtras.ToLambda[
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 =
--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.Number ← CDLayers.LayerWidth[design, lev];
lambda: INT ← design.technology.lambda;
IF shift THEN {
IF control THEN width ← width/lambda*lambda
ELSE width ← GetAnInt[width, wireRef.layerDefine.default, wireRef.layerDefine.text, lambda]
}
ELSE IF mouseButton=Menus.MouseButton[blue] THEN {
IF control THEN width ← width/2 ELSE width ← Dec[width, lambda]
}
ELSE IF mouseButton=Menus.MouseButton[red] THEN {
IF control THEN width ← Double[width] ELSE width ← Inc[width, lambda]
}
ELSE IF mouseButton=Menus.MouseButton[yellow] THEN {
CDLayers.SetCurrentLayer[design, lev];
RETURN
};
width ← MAX[width, 0];
CDLayers.SetLayerWidth[design, lev, width];
END;
DefineIntEntry: PUBLIC PROC [tech: CD.Technology, cdValueKey: REF, text: Rope.ROPE, min: INT, max: INT, default: INT] =
BEGIN
DefineIntEntryX[tech, cdValueKey, text, min, max, default, 1];
END;
DefineIntEntryX: 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;
};
--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];
CDLayers.RegisterNotifiers[layer: NotifyCurrentLayer, width: NotifyLayerWidth];
[] ← ViewerEvents.RegisterEventProc[ContainerDeleted, destroy, $Container];
END.