CDPanelImpl.mesa (part of ChipNDale)
Copyright © 1983, 1985, 1986 by Xerox Corporation. All rights reserved.
Created by Christian Jacobi, August 8, 1983 5:20 pm
Last Edited by: Christian Jacobi, August 25, 1986 6:29:43 pm PDT
DIRECTORY
Buttons,
CD,
CDCells,
CDCommandOps,
CDEnvironment,
CDEvents,
CDLayers,
CDPanel,
CDPanelExtras,
CDPrivate,
CDSequencer,
CDValue,
CDViewer,
Containers,
Labels,
Menus,
Rope,
TerminalIO,
UserProfile,
ViewerEvents,
ViewerClasses,
ViewerOps,
ViewerTools;
CDPanelImpl: CEDAR MONITOR
IMPORTS Buttons, CD, CDCells, CDCommandOps, CDEnvironment, CDEvents, CDLayers, CDPrivate, CDSequencer, CDValue, CDViewer, Containers, Labels, Rope, TerminalIO, UserProfile, ViewerEvents, ViewerOps, ViewerTools
EXPORTS CDPanel, CDPanelExtras
SHARES CDLayers, CDPrivate =
BEGIN
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
panelKey: REF INT ~ NEW[INT]; -- used instead of atoms, to be really unique
panelClassKey: REF INT ~ NEW[INT];
Panel: TYPE = REF PanelRecord; -- one per design
PanelRecord: TYPE = RECORD [
container: 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,
tripples: LIST OF REF Tripple ← NIL
];
PanelList: TYPE = LIST OF Panel;
globalClass: PanelClass ~ NEW[PanelEntryList←NIL];
panelList: PanelList ← 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 [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.tripples ← NIL;
--supress creation of viewer if empty
IF panelClass#NIL AND panelClass^#NIL THEN CreatePanelViewer[panel];
CheckPanelList[panel];
IF panel.container#NIL THEN {
ViewerOps.PaintViewer[panel.container, all];
IF ~UserProfile.Boolean["ChipNDale.ControlViewerOpenIconic", FALSE] THEN
ViewerOps.OpenIcon[panel.container];
};
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: PanelList ← 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: PanelList ← NIL;
FOR list: PanelList ← 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, 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, 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] =
BEGIN
RETURN [IF layerDefinitions[lev]#NIL
THEN layerDefinitions[lev].text
ELSE "not defined"
]
END;
LayerDefine: TYPE = RECORD [
layer: CD.Layer ← CD.undefLayer,
text: ROPENIL,
min, default: CD.Number ← 0
];
IntDefine: TYPE = RECORD [
cdValueKey: REF,
text: ROPE,
min, max, default: INT,
lambda: INT ← 1
];
ButtonDefine: TYPE = RECORD [
name: ROPE,
proc: CDSequencer.CommandProc ← NIL,
command: ATOMNIL,
queue: CDSequencer.QueueMethod,
topLine: BOOLFALSE,
border: BOOLFALSE,
data: REFNIL,
cdValueKey: REFNIL
];
LabelDefine: TYPE = RECORD [
name: ROPE,
cdValueKey: REF,
border: BOOLFALSE
];
TextDefine: TYPE = RECORD [
cdValueKey: REF,
width: INT ← -1, --viewer units
buttonText: Rope.ROPE,
editable: BOOLTRUE,
border: BOOLFALSE
];
PanelEntryList: TYPE = LIST OF REF ANY; -- one per CD.Technology
PanelClass: TYPE = REF PanelEntryList;
--Compensations
--the viewer package uses funny y positions; use this to compensate
butYCompensation: INTEGER ← 0;
labelYCompensation: INTEGER ← 1;
textYCompensation: INTEGER ← -2;
--the height sometimes are not enogh to reasonably display contents...
textHCompensation: INTEGER ← 2;
DefineLayerEntry: PUBLIC ENTRY PROC [tech: CD.Technology, lev: CD.Layer, text: ROPE, min, default: CD.Number𡤁] =
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;
DesignName: PROC [d: CD.Design] RETURNS [Rope.ROPE] = {
RETURN [IF d.name#NIL THEN d.name ELSE "no name" ];
};
Caption: PROC [panel: Panel] RETURNS [ROPE] = {
RETURN [Rope.Cat[
DesignName[panel.design],
" ",
panel.design.technology.name,
" ",
CDCells.PushedCellName[panel.design]
]];
};
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;
IF panel=NIL OR panel.design=NIL OR panel.design.technology=NIL THEN ERROR;
TerminalIO.WriteRopes["create control panel for ", DesignName[panel.design], "\n"];
panel.tripples ← 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[panel.design, $PanelHeight, global, 120],
scrollable: TRUE,
iconic: TRUE, column: right, icon: CDEnvironment.GetPanelIcon[panel.design]
]];
ViewerOps.AddProp[panel.container, $ChipNDaleDesign, panel.design];
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 => [] ← CreateLabelEntry[panel, labDef];
tDef: REF TextDefine => [] ← CreateTextEntry[panel, tDef];
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, proc: Buttons.ButtonProc, border: BOOLFALSE, clientData: REF ANYNIL] =
BEGIN
yoff: INT = Containers.ScrollOffset[panel.container];
button: Buttons.Button = Buttons.Create[
info: [ name: label,
wx: panel.nextX,
wy: panel.nextY+yoff+butYCompensation,
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, 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+butYCompensation,
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, 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+labelYCompensation,
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;
intRef: REF Tripple ← NEW[Tripple ← [panel: panel, define: intDef]];
lambda: INTIF intDef.lambda<=0 THEN panel.design.technology.lambda ELSE intDef.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: 8];
panel.tripples ← CONS[intRef, panel.tripples];
END;
Tripple: TYPE = RECORD [panel: Panel, define: REFNIL, feedback: REFNIL];
CreateLabelEntry: 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.tripples ← CONS[tripple, panel.tripples];
RedisplayLabel[tripple];
}
END;
FetchRope: PROC [from: REF, key: REF] RETURNS [ROPE] = {
WITH CDValue.Fetch[from, key, global] SELECT FROM
r: ROPE => RETURN [r];
rt: REF TEXT => RETURN [Rope.FromRefText[rt]];
ENDCASE => RETURN [NIL];
};
NextName: PROC [list: LIST OF ROPE, last: ROPENIL] RETURNS [next: ROPENIL] = {
IF list=NIL THEN RETURN;
FOR l: LIST OF ROPE ← list, l.rest WHILE l#NIL DO
IF Rope.Equal[last, l.first, FALSE] THEN RETURN [IF l.rest=NIL THEN list.first ELSE l.rest.first]
ENDLOOP;
RETURN [list.first]
};
FetchOneRope: PROC [from: REF, key: REF, start: ROPENIL] RETURNS [ROPE] = {
WITH CDValue.Fetch[from, key, global] SELECT FROM
r: ROPE => RETURN [r];
rt: REF TEXT => RETURN [Rope.FromRefText[rt]];
lr: LIST OF ROPE => RETURN [NextName[lr, start]];
ENDCASE => RETURN [NIL];
};
TextButProc: Menus.ClickProc = {
--called from special buttons which have a Text as tripple.feedback
tripple: REF Tripple ← NARROW[clientData];
tsDef: REF TextDefine ← NARROW[tripple.define];
selection: REF ViewerTools.SelPosRec ← NIL;
v: Viewer ← NARROW[tripple.feedback]; --not the button; the text
SELECT TRUE FROM
mouseButton=blue => ViewerTools.SetContents[v, NIL];
ViewerTools.GetSelectedViewer[]=v => {--viewer previously selected; iterate through.
contents: ROPE ← FetchOneRope[tripple.panel.design.technology, tsDef.cdValueKey, ViewerTools.GetContents[v]];
ViewerTools.SetContents[v, contents];
selection ← NEW[ViewerTools.SelPosRec ← [start: contents.Length[], length: 0]];
};
ENDCASE => NULL;
ViewerTools.SetSelection[v, selection];
--new contents is not stored to CDValue yet!! (It wont if edited either)
};
TextLabButProc: Menus.ClickProc = {
--called from special buttons which have a Label as tripple.feedback
tripple: REF Tripple ← NARROW[clientData];
tsDef: REF TextDefine ← NARROW[tripple.define];
v: Viewer ← NARROW[tripple.feedback]; --not the button; the label
contents: ROPE ← FetchRope[tripple.panel.design, tsDef.cdValueKey];
contents ← FetchOneRope[tripple.panel.design.technology, tsDef.cdValueKey, contents];
CDValue.Store[tripple.panel.design, tsDef.cdValueKey, contents];
Labels.Set[v, contents];
};
CreateTextEntry: INTERNAL PROC [panel: Panel, tsDef: REF TextDefine] RETURNS [tripple: REF Tripple←NIL] =
BEGIN
yoff: INT = Containers.ScrollOffset[panel.container];
w: INTIF tsDef.width<=0 THEN 2000 ELSE MIN[tsDef.width, 2000];
h: INT ← entryHeight;
y: INT ← panel.nextY+yoff;
x: INT ← panel.nextX;
v: Viewer;
tripple ← NEW[Tripple ← [panel: panel, define: tsDef, feedback: NIL]];
IF ~Rope.IsEmpty[tsDef.buttonText] THEN {
button: Buttons.Button ← Buttons.Create[
info: [ name: tsDef.buttonText,
wx: x, wy: y+butYCompensation, wh: h,
border: FALSE,
parent: panel.container
],
clientData: tripple,
proc: IF tsDef.editable THEN TextButProc ELSE TextLabButProc
];
x ← button.wx+button.ww+entryHSpace;
};
IF tsDef.editable THEN
v ← ViewerTools.MakeNewTextViewer[info: [
wx: x, wy: y+textYCompensation, ww: w, wh: h+textHCompensation,
parent: panel.container,
border: FALSE,
data: FetchOneRope[panel.design, tsDef.cdValueKey]
]]
ELSE
v ← Labels.Create[info: [
name: FetchOneRope[panel.design, tsDef.cdValueKey],
wx: x, wy: y+labelYCompensation, ww: w, wh: h,
parent: panel.container,
border: FALSE
]];
tripple.feedback ← v;
panel.nextX ← v.wx+w+entryHSpace;
IF tsDef.width<=0 THEN Containers.ChildXBound[panel.container, v];
panel.tripples ← CONS[tripple, panel.tripples];
END;
RedisplayLabel: PROC [tripple: REF Tripple] =
BEGIN
IF tripple=NIL THEN RETURN;
WITH tripple.define SELECT FROM
labDef: REF LabelDefine => {
x: REF = CDValue.Fetch[tripple.panel.design, labDef.cdValueKey, global];
Labels.Set[NARROW[tripple.feedback], CDCommandOps.ToRope[x, "-?-"]];
};
butDef: REF ButtonDefine => {
x: REF = CDValue.Fetch[tripple.panel.design, butDef.cdValueKey, 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.tripples, 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: butDef
];
IF butDef.cdValueKey#NIL THEN {
tripple: REF Tripple ← NEW[Tripple ← [panel: panel, define: butDef, feedback: but]];
panel.tripples ← CONS[tripple, panel.tripples];
RedisplayLabel[tripple];
}
END;
ButtonCalled: Buttons.ButtonProc =
BEGIN
butDef: REF ButtonDefine = NARROW[clientData];
comm: CDSequencer.Command = NEW[CDSequencer.CommandRec←[
data: butDef.data,
design: CDViewer.DesignOf[NARROW[parent]]
]];
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<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, lambda: INT𡤁] RETURNS [new: INT] =
BEGIN
ENABLE TerminalIO.UserAbort => GOTO aborted;
choice: LIST OF 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
design: CD.Design ← CDViewer.DesignOf[NARROW[parent]];
IF design=NIL THEN RETURN;
CDSequencer.ExecuteProc[
proc: SerializedIntCalled,
design: 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;
design: CD.Design ~ comm.design;
tripple: REF Tripple = NARROW[comm.data];
intDef: REF IntDefine = NARROW[tripple.define];
lambda: INT ← intDef.lambda;
IF lambda<=0 THEN lambda ← design.technology.lambda;
i ← CDValue.FetchInt[
boundTo: 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[design, intDef.cdValueKey, 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+butYCompensation,
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+labelYCompensation,
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, min: INT, max: INT, default: INT, lambda: INT] = {
ENABLE UNWIND => NULL;
class: PanelClass = GetPanelClass[tech];
def: REF IntDefine ← NEW[IntDefine ← [cdValueKey: cdValueKey, text: text, min: min, max: max, default: default, lambda: lambda]];
CDValue.StoreInt[boundTo: tech, key: cdValueKey, value: default];
AppendEntry[class, def];
};
DefineTextEntry: PUBLIC ENTRY PROC [tech: CD.Technology ← NIL, cdValueKey: REF, button: Rope.ROPENIL, width: INT ← -1, editable: BOOLTRUE] =
BEGIN ENABLE UNWIND => NULL;
class: PanelClass = GetPanelClass[tech];
def: REF TextDefine = NEW[TextDefine ← [cdValueKey: cdValueKey, width: width, border: FALSE, buttonText: button, editable: editable]];
AppendEntry[class, def];
END;
FromDisplay: PROC [design: CD.Design, cdValueKey: REF] RETURNS [Rope.ROPENIL] =
BEGIN
panel: Panel = GetPanel[design];
FOR list: LIST OF REF Tripple ← panel.tripples, list.rest WHILE list#NIL DO
WITH list.first.define SELECT FROM
tsDef: REF TextDefine =>
IF tsDef.cdValueKey=cdValueKey THEN {
RETURN [ViewerTools.GetContents[NARROW[list.first.feedback]]];
};
ENDCASE => NULL;
ENDLOOP;
END;
FromDisplayRope: PUBLIC PROC [design: CD.Design, cdValueKey: REF] RETURNS [Rope.ROPE] =
BEGIN
x: REF ← FromDisplay[design, cdValueKey];
CDValue.Store[design, cdValueKey, x];
WITH x SELECT FROM
r: Rope.ROPE => RETURN [r];
ENDCASE => RETURN [NIL];
END;
ReDisplayNoStore: PROC [design: CD.Design, cdValueKey: REF, rope: Rope.ROPE] = {
panel: Panel = GetPanel[design];
IF panel.container=NIL OR panel.container.destroyed THEN RETURN;
FOR list: LIST OF REF Tripple ← panel.tripples, list.rest WHILE list#NIL DO
WITH list.first.define SELECT FROM
tDef: REF TextDefine =>
IF tDef.cdValueKey=cdValueKey THEN {
v: Viewer ← NARROW[list.first.feedback];
IF v#NIL THEN {
IF v.class.flavor=$Label THEN Labels.Set[v, rope]
ELSE ViewerTools.SetContents[v, rope];
};
};
labDef: REF LabelDefine =>
IF labDef.cdValueKey=cdValueKey THEN {
v: Viewer ← NARROW[list.first.feedback];
IF v#NIL THEN Labels.Set[v, rope];
};
butDef: REF ButtonDefine =>
IF butDef.cdValueKey=cdValueKey THEN {
v: Viewer ← NARROW[list.first.feedback];
IF v#NIL THEN Buttons.ReLabel[v, rope];
};
intDef: REF IntDefine =>
IF intDef.cdValueKey=cdValueKey THEN {
v: Viewer ← NARROW[list.first.feedback];
IF v#NIL THEN {
n: INT ← CDValue.FetchInt[design, intDef.cdValueKey, technology, intDef.default];
lambda: INTIF intDef.lambda<=0 THEN design.technology.lambda ELSE intDef.lambda;
Buttons.ReLabel[v, CDCommandOps.LambdaRope[n, lambda]];
};
};
ENDCASE => NULL;
ENDLOOP;
};
ReDisplayRope: PUBLIC PROC [design: CD.Design, cdValueKey: REF, rope: Rope.ROPE] =
BEGIN
CDValue.Store[design, cdValueKey, rope];
ReDisplayNoStore[design, cdValueKey, rope];
END;
ReDisplay: PUBLIC PROC [design: CD.Design, cdValueKey: REF] =
BEGIN
rope: ROPE ← FetchRope[design, cdValueKey];
ReDisplayNoStore[design, cdValueKey, rope];
END;
ContainerDeleted: ViewerEvents.EventProc = {
--break circularities (for garbage collection)
WITH ViewerOps.FetchProp[viewer, $CDPanelPrivate] SELECT FROM
panel: Panel => {
ViewerOps.AddProp[viewer, $ChipNDaleDesign, NIL];
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.tripples ← NIL;
};
};
ENDCASE => NULL;
};
EnumDesigns: PROC[p: CDPrivate.DesignEnumerator] RETURNS [quit: BOOLFALSE] =
BEGIN
FOR list: PanelList ← 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.