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, May 2, 1985 12:08:27 pm PDT
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: 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.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: 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.DesignNumber𡤁] =
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: 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;
NextNumberLabel: INTERNAL PROC [panel: Panel, value: INT, extraSpaces: CARDINAL𡤀] 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: 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
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: REFNIL];
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<LAST[INT]/2 AND i>(FIRST[INT]/2) THEN i ← i+i;
RETURN [i]
};
Inc: PROC[i: INT] RETURNS [INT] = INLINE {
IF i<LAST[INT] THEN i ← i+1;
RETURN [i]
};
Dec: PROC[i: INT] RETURNS [INT] = INLINE {
IF i>FIRST[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.ROPENIL,
min: INTFIRST[INT],
max: INTLAST[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.