DIRECTORY Buttons, CD, CDCellsBackdoor, CDEnvironment, CDEvents, CDLayers, CDOps, CDPanel, CDPrivate, CDProperties, CDSequencer, CDValue, CDViewer, Containers, Labels, Menus, PopUpSelection, Rope, TerminalIO, UserProfile, ViewerEvents, ViewerClasses, ViewerOps, ViewerTools; CDPanelImpl: CEDAR MONITOR IMPORTS Buttons, CD, CDCellsBackdoor, CDEnvironment, CDEvents, CDLayers, CDOps, CDPrivate, CDProperties, CDSequencer, CDValue, CDViewer, Containers, Labels, PopUpSelection, Rope, TerminalIO, UserProfile, ViewerEvents, ViewerOps, ViewerTools EXPORTS CDPanel SHARES CDLayers, CDPrivate = BEGIN ROPE: TYPE = Rope.ROPE; Viewer: TYPE = ViewerClasses.Viewer; Info: TYPE = CDPanel.Info; LayerPanelRec: TYPE = RECORD [ container: Viewer _ NIL, nextX: CARDINAL _ 0 ]; layerOptionsCount: NAT = 3; Panel: TYPE = REF PanelRec; -- defines the control panel; one per design PanelRec: TYPE = RECORD [ container: Viewer _ NIL, layerOption: NAT _ 0, layers: ARRAY [0..layerOptionsCount) OF LayerPanelRec, design: CD.Design, class: Class _ NIL, nextX: CARDINAL _ 0, nextY: CARDINAL _ 0, nexTopX: CARDINAL _ 0, layerLabel: Labels.Label _ NIL, layerWidths: LIST OF Actual _ NIL, actuals: LIST OF Actual _ NIL ]; PanelList: TYPE = LIST OF Panel; EntryClass: TYPE = REF EntryClassRec; --class of entry in viewer EntryClassRec: TYPE = RECORD [ build: BuildProc, toDisplay: ToDisplayProc _ NIL, fromdisplay: FromDisplayProc _ NIL, --if defaulted uses cdvalue classData: REF _ NIL ]; BuildProc: TYPE = PROC [panel: Panel, entry: EntryDef]; --builds an entry into a viewer ToDisplayProc: TYPE = PROC [actual: Actual, value: REF]; FromDisplayProc: TYPE = PROC [actual: Actual] RETURNS [value: REF_NIL]; EntryDef: TYPE = REF EntryDefRec; --defines an entry EntryDefRec: TYPE = RECORD [ entryClass: EntryClass, button, val: Info, --only val's cdValueKey is looked at ever min, max, default, lambda: INT _ 1, layer: CD.Layer _ CD.undefLayer, option: NAT _ 0, topLine, editable: BOOL _ FALSE, proc: CDSequencer.CommandProc _ NIL, command: ATOM _ NIL, queue: CDSequencer.QueueMethod _ useDefault, data: REF _ NIL ]; EntryList: TYPE = LIST OF EntryDef; Class: TYPE = REF EntryList; -- one per Technology Actual: TYPE = REF ActualRec; --type used for clientData field of viewer stuff ActualRec: TYPE = RECORD [ panel: Panel, define: EntryDef, feedback: Viewer _ NIL --might be a different viewer doing feedback for the caller ]; panelKey: REF ATOM ~ NEW[ATOM_$Panel]; -- used instead of atoms, to be really unique classKey: REF ATOM ~ NEW[ATOM_$PanelClass]; globalClass: Class ~ NEW[EntryList_NIL]; panelList: PanelList _ NIL; newLineClass: EntryClass _ RegisterEntryClass[[ build: BuildNewLine ]]; buttonClass: EntryClass _ RegisterEntryClass[[ build: BuildButton, toDisplay: ToDisplayButton ]]; labelClass: EntryClass _ RegisterEntryClass[[ build: BuildLabel, toDisplay: ToDisplayLabel ]]; textClass: EntryClass _ RegisterEntryClass[[ build: BuildText, toDisplay: ToDisplayText, fromdisplay: FromDisplayText ]]; layerClass: EntryClass _ RegisterEntryClass[[ build: BuildLayer ]]; intClass: EntryClass _ RegisterEntryClass[[ build: BuildInt, toDisplay: ToDisplayNumber ]]; butYCompensation: INTEGER _ 0; labelYCompensation: INTEGER _ 1; textYCompensation: INTEGER _ -2; textHCompensation: INTEGER _ 2; topY: CARDINAL = 1; entryHSpace: CARDINAL = 3; entryHeight: CARDINAL = 12; shorterEntryVSpace: CARDINAL = 1; RegisterEntryClass: PROC [c: EntryClassRec] RETURNS [entryClass: EntryClass] = { entryClass _NEW[EntryClassRec_c] }; GetPanel: PROC [design: CD.Design] RETURNS [Panel] = { x: REF _ CDValue.Fetch[boundTo: design, key: panelKey, propagation: global]; IF x=NIL THEN { panel: Panel = NEW[PanelRec_[ design: design, class: GetClass[design.technology] ]]; [] _ CDValue.StoreConditional[design, panelKey, panel]; RETURN [GetPanel[design]] }; RETURN [NARROW[x, Panel]]; }; GetClass: PROC [tech: CD.Technology] RETURNS [Class] = { IF tech=NIL THEN RETURN [globalClass] ELSE { x: REF _ CDValue.Fetch[boundTo: tech, key: classKey, propagation: technology]; IF x=NIL THEN { class: Class = NEW[EntryList_NIL]; IF CDValue.StoreConditional[tech, classKey, class] THEN CDLayers.SetCurrentLayer[tech, CD.commentLayer]; RETURN [GetClass[tech]]; }; RETURN [NARROW[x, Class]]; } }; Create: PUBLIC ENTRY PROC [design: CD.Design] RETURNS [Viewer] = { ENABLE UNWIND => NULL; panel: Panel _ GetPanel[design]; class: Class _ GetClass[design.technology]; IF panel.container#NIL AND NOT panel.container.destroyed THEN RETURN [panel.container]; panel.container _ NIL; panel.actuals _ NIL; IF class#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] }; CheckPanelList: INTERNAL PROC [p: Panel] = { 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 }; BuildEntry: INTERNAL PROC[panel: Panel, entry: EntryDef] = { IF panel.container.destroyed THEN RETURN; entry.entryClass.build[panel, entry]; }; DefineEntry: INTERNAL PROC [tech: CD.Technology, entryRec: EntryDefRec] RETURNS [entry: EntryDef] = { IncludeEntryInClass: INTERNAL PROC [class: Class, entry: EntryDef] = { IF class^=NIL THEN class^ _ LIST[entry] ELSE FOR list: EntryList _ class^, list.rest DO IF list.rest = NIL THEN {list.rest _ LIST[entry]; EXIT} ENDLOOP; }; CreateEntryInViewers: INTERNAL PROC [class: Class, entry: EntryDef] = { 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 BuildEntry[list.first, entry]; } ENDLOOP }; class: Class = GetClass[tech]; entry _ NEW[EntryDefRec_entryRec]; entry.button.xpos _ MIN[2000, entry.button.xpos]; entry.button.width _ MIN[2000, entry.button.width]; entry.button.space _ MIN[2000, entry.button.space]; entry.val.xpos _ MIN[2000, entry.val.xpos]; entry.val.width _ MIN[2000, entry.val.width]; entry.val.space _ MIN[2000, entry.val.space]; IncludeEntryInClass[class, entry]; CreateEntryInViewers[class, entry]; }; Line: PUBLIC ENTRY PROC [tech: CD.Technology] = { ENABLE UNWIND => NULL; [] _ DefineEntry[tech, [entryClass: newLineClass]]; }; Button: PUBLIC ENTRY PROC [button: Info, proc: CDSequencer.CommandProc _ NIL, command: ATOM _ NIL, data: REF _ NIL, queue: CDSequencer.QueueMethod _ useDefault, topLine: BOOL _ FALSE, tech: CD.Technology _ NIL] = { ENABLE UNWIND => NULL; IF command=NIL AND queue=useDefault THEN queue _ doQueue; [] _ DefineEntry[tech, [ entryClass: buttonClass, button: button, val: button, proc: proc, command: command, queue: queue, data: data, topLine: topLine ]]; }; Label: PUBLIC ENTRY PROC [label: Info, tech: CD.Technology _ NIL] = { ENABLE UNWIND => NULL; [] _ DefineEntry[tech, [entryClass: labelClass, button: label, val: label]]; }; Layer: PUBLIC ENTRY PROC [layer: CD.Layer, text: Rope.ROPE, min, default: CD.Number _ 1, option: NAT _ 0, tech: CD.Technology _ NIL] = { ENABLE UNWIND => NULL; def: EntryDef _ DefineEntry[tech, [ entryClass: layerClass, layer: layer, button: [text: text], min: min, default: default, option: option MOD layerOptionsCount ]]; CDProperties.PutProp[CDPrivate.layers[layer].properties, GetClass[tech], def]; CDLayers.SetLayerWidth[design: NIL --!!! it sets default--, layer: layer, width: default]; }; Number: PUBLIC ENTRY PROC [button: Info, number: Info, min: INT _ FIRST[INT], max: INT _ LAST[INT], default: INT _ 0, lambda: INT _ 1, tech: CD.Technology _ NIL] = { ENABLE UNWIND => NULL; CDValue.StoreInt[boundTo: tech, key: number.cdValueKey, value: default]; button.cdValueKey _ NIL; number.text _ NIL; IF number.width<=0 THEN number.width _ 72; [] _ DefineEntry[tech, [ entryClass: intClass, button: button, val: number, min: min, max: max, default: default, lambda: lambda ]]; }; Text: PUBLIC ENTRY PROC [button: Info, text: Info, editable: BOOL _ TRUE, tech: CD.Technology _ NIL] = { ENABLE UNWIND => NULL; button.cdValueKey _ NIL; [] _ DefineEntry[tech, [entryClass: textClass, button: button, val: text, editable: editable]]; }; LayerName: PROC [panel: Panel, layer: CD.Layer] RETURNS [r: ROPE] = { WITH CDProperties.GetListProp[CDPrivate.layers[layer].properties^, panel.class] SELECT FROM entry: EntryDef => r _ entry.button.text; ENDCASE => NULL; IF Rope.IsEmpty[r] THEN { r _ CDOps.LayerRope[layer]; IF Rope.IsEmpty[r] THEN r _ "not defined" } }; NotifyCurrentLayer: CDLayers.DesignNotifyProc = { panel: Panel = GetPanel[design]; IF panel.layerLabel#NIL THEN { l: CD.Layer _ CDLayers.CurrentLayer[design]; Labels.Set[panel.layerLabel, LayerName[panel, l]]; } }; NotifyLayerWidth: CDLayers.LayerNotifyProc = { panel: Panel = GetPanel[design]; IF panel.layerLabel#NIL THEN { FOR list: LIST OF Actual _ panel.layerWidths, list.rest WHILE list#NIL DO IF list.first.define.layer=layer THEN { Labels.Set[list.first.feedback, CDOps.LambdaRope[ n: CDLayers.LayerWidth[design, layer], lambda: design.technology.lambda ] ]; }; ENDLOOP; } }; Caption: PROC [panel: Panel] RETURNS [ROPE] = { RETURN [Rope.Cat[ CD.DesignName[panel.design], " ", panel.design.technology.name, " ", CDCellsBackdoor.PushedCellName[panel.design] ]]; }; CreatePanelViewer: INTERNAL PROC [panel: Panel] = { MakeClassEntries: INTERNAL PROC [class: Class] = { FOR l: EntryList _ class^, l.rest WHILE l#NIL DO BuildEntry[panel, l.first]; ENDLOOP; }; IF panel=NIL OR panel.design=NIL OR panel.design.technology=NIL THEN ERROR; TerminalIO.PutRopes["create control panel for ", CD.DesignName[panel.design], "\n"]; panel.actuals _ NIL; panel.layerWidths _ NIL; panel.nextX _ 0; panel.nextY _ topY; panel.container _ Containers.Create[ info: [ name: Caption[panel], openHeight: CDValue.FetchInt[panel.design, $PanelHeight, global, 120], scrollable: TRUE, iconic: TRUE, column: right, icon: CDEnvironment.GetPanelIcon[panel.design] ], paint: FALSE ]; FOR i: NAT IN [0..layerOptionsCount) DO panel.layers[i].nextX _ 0; panel.layers[i].container _ Containers.Create[ info: [ wx: IF i=0 THEN 0 ELSE 1999, wy: topY+entryHeight+entryHSpace+Containers.ScrollOffset[panel.container], wh: entryHeight+shorterEntryVSpace+entryHeight+labelYCompensation, parent: panel.container, border: FALSE, scrollable: FALSE ], paint: FALSE ]; Containers.ChildXBound[container: panel.container, child: panel.layers[i].container]; ENDLOOP; ViewerOps.AddProp[panel.container, $ChipNDaleDesign, panel.design]; ViewerOps.AddProp[panel.container, $CDPanelsPrivate, panel]; [] _ NextButton[panel: panel, label: "layer: ", proc: NotifyChangeLayerOption, clientData: panel]; panel.layerLabel _ NextLabel[panel: panel, name: LayerName[panel, CDLayers.CurrentLayer[panel.design]], width: 120]; panel.nextX _ 180; [] _ NextLabel[panel, "| "]; panel.nexTopX _ panel.nextX; panel.nextY _ 3*(entryHeight+entryHSpace); panel.nextX _ 0; MakeClassEntries[panel.class]; MakeClassEntries[globalClass]; }; NotifyChangeLayerOption: ENTRY Buttons.ButtonProc = { ENABLE UNWIND => NULL; panel: Panel _ NARROW[clientData]; this: NAT _ panel.layerOption; next: NAT _ (this+1) MOD layerOptionsCount; IF control AND shift THEN next _ CDValue.FetchInt[panel.design, $PanelLayerMode, global] MOD layerOptionsCount; DO IF next=this THEN RETURN; IF panel.layers[next].nextX#0 THEN EXIT; next _ (next+1) MOD layerOptionsCount; ENDLOOP; ViewerOps.MoveViewer[ viewer: panel.layers[this].container, x: 1990, y: panel.layers[this].container.wy, w: 0, h: panel.layers[this].container.wh, paint: FALSE ]; panel.layerOption _ next; ViewerOps.MoveViewer[ viewer: panel.layers[next].container, x: 0, y: panel.layers[next].container.wy, w: panel.container.ww, h: panel.layers[next].container.wh, paint: TRUE ]; }; RepaintCaptions: CDEvents.EventProc = { panel: Panel _ GetPanel[design]; IF panel.container#NIL THEN { panel.container.name _ Caption[panel]; ViewerOps.PaintViewer[panel.container, caption]; }; }; NextIntButton: PROC [panel: Panel, label: ROPE, proc: Buttons.ButtonProc, border: BOOL _ FALSE, clientData: REF ANY _ NIL, width: INT _ -1, space: INT _ -1] = { button: Buttons.Button = Buttons.Create[ info: [ name: label, wx: panel.nextX, ww: SmallDefault[width], wy: panel.nextY+Containers.ScrollOffset[panel.container]+butYCompensation, wh: entryHeight, parent: panel.container, border: border ], clientData: clientData, proc: proc ]; panel.nextX _ button.wx+button.ww+SpaceDefault[space]; }; NextButton: INTERNAL PROC [panel: Panel, label: ROPE, proc: Buttons.ButtonProc, clientData: REF ANY _ NIL, topLine: BOOL _ FALSE, border: BOOL _ FALSE, width: INT _ 0, space: INT_0] RETURNS [button: Buttons.Button] = { yoff: INT = Containers.ScrollOffset[panel.container]; x: INT _ panel.nextX; y: INT _ panel.nextY; IF topLine THEN {x _ panel.nexTopX; y _ 0}; button _ Buttons.Create[ info: [ name: label, wx: x, ww: SmallDefault[width], wy: y+yoff+butYCompensation, wh: entryHeight, parent: panel.container, border: border ], clientData: clientData, proc: proc ]; IF topLine THEN panel.nexTopX _ button.wx+button.ww+SpaceDefault[space] ELSE panel.nextX _ button.wx+button.ww+SpaceDefault[space] }; NextLabel: PROC [panel: Panel, name: ROPE, border: BOOL _ FALSE, width: INT _ 0, space: INT _ 0] RETURNS [Labels.Label] = { yoff: INT = Containers.ScrollOffset[panel.container]; label: Labels.Label; IF name=NIL THEN name _ " "; label _ Labels.Create[ info: [ name: name, wx: panel.nextX, ww: SmallDefault[width], wy: panel.nextY+yoff+labelYCompensation, wh: entryHeight, parent: panel.container, border: border ] ]; panel.nextX _ label.wx+label.ww+SpaceDefault[space]; RETURN [label] }; BuildInt: BuildProc = { initialValue: ROPE; width: INT; actual: Actual _ NEW[ActualRec _ [panel: panel, define: entry]]; panel.nextX _ MAX[entry.button.xpos, panel.nextX]; width _ entry.button.width; IF entry.button.maxx>0 THEN width _ entry.button.maxx-panel.nextX; [] _ NextIntButton[panel: panel, label: entry.button.text, proc: IntCalled, clientData: actual, space: entry.button.space, width: width]; initialValue _ CDOps.LambdaRope[ n: CDValue.FetchInt[panel.design, entry.val.cdValueKey, technology, entry.default], lambda: ActualLambda[actual] ]; panel.nextX _ MAX[entry.val.xpos, panel.nextX+8]; width _ entry.val.width; IF entry.val.maxx>0 THEN width _ entry.val.maxx-panel.nextX; actual.feedback _ NextLabel[panel: panel, name: initialValue, width: entry.val.width]; panel.actuals _ CONS[actual, panel.actuals]; }; ActualLambda: PROC [actual: Actual] RETURNS [lambda: INT] = { lambda _ actual.define.lambda; IF lambda<=0 THEN lambda _ actual.panel.design.technology.lambda }; ToDisplayNumber: ToDisplayProc = { n: INT; lambda: INT _ ActualLambda[actual]; WITH value SELECT FROM ri: REF INT => n _ ri^ ENDCASE => n _ actual.define.default; Buttons.ReLabel[actual.feedback, CDOps.LambdaRope[n, lambda]]; }; BuildLabel: BuildProc = { actual: Actual_NIL; lab: Labels.Label; width: INT _ entry.button.width; panel.nextX _ MAX[entry.button.xpos, panel.nextX]; IF entry.button.maxx>0 THEN width _ entry.button.maxx-panel.nextX; lab _ NextLabel[ panel: panel, name: entry.button.text, width: width, space: entry.button.space, border: entry.button.border ]; IF entry.val.cdValueKey#NIL THEN { actual _ NEW[ActualRec _ [panel: panel, define: entry, feedback: lab ]]; panel.actuals _ CONS[actual, panel.actuals]; RedisplayVal[actual]; } }; ToDisplayLabel: ToDisplayProc = { Labels.Set[actual.feedback, CDOps.ToRope[value, "-?-"]]; }; FetchRopeWithList: PROC [from: REF, key: REF, start: ROPE_NIL] RETURNS [ROPE] = { NextName: PROC [list: LIST OF ROPE, last: ROPE_NIL] RETURNS [next: ROPE _ NIL] = { 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] }; 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 = { actual: Actual _ NARROW[clientData]; selection: REF ViewerTools.SelPosRec _ NIL; v: Viewer _ actual.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 _ FetchRopeWithList[actual.panel.design.technology, actual.define.val.cdValueKey, ViewerTools.GetContents[v]]; ViewerTools.SetContents[v, contents]; selection _ NEW[ViewerTools.SelPosRec _ [start: contents.Length[], length: 0]]; }; ENDCASE => NULL; ViewerTools.SetSelection[v, selection]; }; TextLabButProc: Menus.ClickProc = { FetchSimpleRope: PROC [from: REF, key: REF] RETURNS [ROPE] = INLINE { WITH CDValue.Fetch[from, key, global] SELECT FROM r: ROPE => RETURN [r]; rt: REF TEXT => RETURN [Rope.FromRefText[rt]]; ENDCASE => RETURN [NIL]; }; contents, start: ROPE; actual: Actual _ NARROW[clientData]; cdValueKey: REF _ actual.define.val.cdValueKey; start _ FetchSimpleRope[actual.panel.design, cdValueKey]; contents _ FetchRopeWithList[actual.panel.design.technology, cdValueKey, start]; CDValue.Store[actual.panel.design, cdValueKey, contents]; Labels.Set[actual.feedback, contents]; }; SmallDefault: PROC [w: INT] RETURNS [INT] = { RETURN [IF w<=0 THEN 0 ELSE MIN[2000, w]] }; SpaceDefault: PROC [s: INT] RETURNS [INT] = { RETURN [IF s<=0 THEN entryHSpace ELSE MIN[200, s]] }; BuildText: BuildProc = { v: Viewer; x, y: INT; actual: Actual _ NEW[ActualRec _ [panel: panel, define: entry, feedback: NIL]]; width: INT; height: INT _ entryHeight; panel.nextX _ MAX[entry.button.xpos, panel.nextX]; y _ panel.nextY+Containers.ScrollOffset[panel.container]; x _ panel.nextX; width _ SmallDefault[entry.button.width]; IF entry.button.maxx>0 THEN width _ entry.button.maxx-panel.nextX; IF ~Rope.IsEmpty[entry.button.text] THEN { button: Buttons.Button _ Buttons.Create[ info: [ name: entry.button.text, wx: x, ww: width, wy: y+butYCompensation, wh: height, border: FALSE, parent: panel.container ], clientData: actual, proc: IF entry.editable THEN TextButProc ELSE TextLabButProc ]; x _ button.wx+button.ww+SpaceDefault[entry.button.space]; }; x _ MAX[x, entry.val.xpos]; width _ IF entry.val.width<=0 THEN 2000 ELSE MIN[entry.val.width, 2000]; IF entry.val.maxx>0 THEN width _ entry.val.maxx-panel.nextX; IF entry.editable THEN v _ actual.feedback _ ViewerTools.MakeNewTextViewer[info: [ wx: x, wy: y+textYCompensation, ww: width, wh: height+textHCompensation, parent: panel.container, border: FALSE, data: FetchRopeWithList[panel.design, entry.val.cdValueKey] ]] ELSE v _ actual.feedback _ Labels.Create[info: [ name: FetchRopeWithList[panel.design, entry.val.cdValueKey], wx: x, ww: width, wy: y+labelYCompensation, wh: height, parent: panel.container, border: FALSE ]]; panel.nextX _ v.wx+v.ww+SpaceDefault[entry.val.space]; IF entry.val.width<=0 AND entry.val.maxx<=0 THEN Containers.ChildXBound[panel.container, v]; panel.actuals _ CONS[actual, panel.actuals]; }; ToDisplayText: ToDisplayProc = { entry: EntryDef _ actual.define; design: CD.Design _ actual.panel.design; rope: ROPE _ CDOps.ToRope[value, " "]; v: Viewer _ actual.feedback; IF v.class.flavor=$Label THEN Labels.Set[v, rope] ELSE ViewerTools.SetContents[v, rope]; }; FromDisplayText: FromDisplayProc = { RETURN [ViewerTools.GetContents[NARROW[actual.feedback]]]; }; RedisplayVal: PROC [actual: Actual] = { IF actual.define.val.cdValueKey#NIL THEN ToDisplay[actual, CDValue.Fetch[actual.panel.design, actual.define.val.cdValueKey, global]] }; ToDisplay: PROC [actual: Actual, x: REF] = { IF actual.define.entryClass.toDisplay#NIL THEN actual.define.entryClass.toDisplay[actual, x]; }; PutUpAll: PUBLIC PROC [design: CD.Design] = { panel: Panel = GetPanel[design]; IF panel.container=NIL OR panel.container.destroyed THEN RETURN; FOR list: LIST OF Actual _ panel.actuals, list.rest WHILE list#NIL DO IF list.first.define.val.redisplay THEN RedisplayVal[list.first]; ENDLOOP; }; BuildButton: INTERNAL PROC [panel: Panel, entry: EntryDef] = { but: Buttons.Button; width: INT _ SmallDefault[entry.button.width]; panel.nextX _ MAX[entry.button.xpos, panel.nextX]; IF entry.button.maxx>0 THEN width _ entry.button.maxx-panel.nextX; but _ NextButton[ panel: panel, label: entry.button.text, proc: ButtonCalled, border: entry.button.border, topLine: entry.topLine, width: width, clientData: entry ]; IF entry.val.cdValueKey#NIL THEN { actual: Actual _ NEW[ActualRec _ [panel: panel, define: entry, feedback: but]]; panel.actuals _ CONS[actual, panel.actuals]; RedisplayVal[actual]; } }; ToDisplayButton: ToDisplayProc = { Buttons.ReLabel[actual.feedback, CDOps.ToRope[value, "-?-"]]; }; ButtonCalled: Buttons.ButtonProc = { butDef: EntryDef = NARROW[clientData]; comm: CDSequencer.Command = NEW[CDSequencer.CommandRec_[ data: butDef.data, design: CDViewer.DesignOf[NARROW[parent]], n: ORD[mouseButton], b: shift OR control, ref: $Panel ]]; 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]; }; GetAnInt: PROC [old, default: INT, label: ROPE, lambda: INT_1] RETURNS [new: INT] = { 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", Rope.Concat["type * ", CDOps.LambdaRope[1, lambda]], "trunc"]; SELECT PopUpSelection.Request[header: 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[Rope.Cat["type value ", CDOps.LambdaRope[1, lambda], " > "]]; 8 => new _ old/lambda*lambda; ENDCASE => TerminalIO.PutRope["Skipped\n"]; EXITS aborted => NULL; }; IntCalled: Buttons.ButtonProc = { 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 ]] ]; }; SerializedIntCalled: PROC [comm: CDSequencer.Command] = { ENABLE UNWIND => NULL; design: CD.Design ~ comm.design; actual: Actual ~ NARROW[comm.data]; intDef: EntryDef ~ actual.define; lambda: INT _ ActualLambda[actual]; i: INT _ CDValue.FetchInt[design, intDef.val.cdValueKey, global, intDef.default]; SELECT TRUE FROM comm.b => { IF comm.n=1 THEN i _ i/lambda*lambda ELSE i _ GetAnInt[i, intDef.default, intDef.button.text, lambda] }; comm.key=$right => { IF comm.n=1 THEN i_i/2 ELSE i _ Dec[i, lambda] }; comm.key=$left => { IF comm.n=1 THEN i _ Double[i] ELSE i _ Inc[i, lambda] }; ENDCASE => NULL; i _ MAX[MIN[i, intDef.max], intDef.min]; CDValue.StoreInt[design, intDef.val.cdValueKey, i]; Labels.Set[actual.feedback, CDOps.LambdaRope[n: i, lambda: lambda]]; }; BuildNewLine: BuildProc = { panel.nextY _ panel.nextY+entryHSpace+entryHeight; panel.nextX _ 0; }; BuildLayer: BuildProc = { p: Actual _ NEW[ActualRec _ [define: entry, panel: panel]]; option: NAT _ entry.option; button: Buttons.Button = Buttons.Create[ info: [ name: entry.button.text, wx: panel.layers[option].nextX, wy: butYCompensation, wh: entryHeight, parent: panel.layers[option].container, border: FALSE ], clientData: p, proc: WireWidthCalled ]; p.feedback _ Labels.Create[ info: [ wx: panel.layers[option].nextX, wy: shorterEntryVSpace+entryHeight+labelYCompensation, wh: entryHeight, ww: button.ww+4*entryHSpace, --2 spaces for real; 2 covering leading blank of next # parent: panel.layers[option].container, border: FALSE, name: CDOps.LambdaRope[ n: CDLayers.LayerWidth[panel.design, entry.layer], lambda: panel.design.technology.lambda ] ] ]; panel.layers[option].nextX _ button.wx+button.ww+2*entryHSpace; panel.layerWidths _ CONS[p, panel.layerWidths]; }; WireWidthCalled: Buttons.ButtonProc = { wireRef: Actual = 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 ]] ]; }; SerializedWireWidthCalled: PROC[comm: CDSequencer.Command] = { wireRef: Actual = NARROW[comm.data]; layer: CD.Layer = wireRef.define.layer; design: CD.Design _ wireRef.panel.design; width: CD.Number _ CDLayers.LayerWidth[design, layer]; lambda: INT _ design.technology.lambda; IF comm.b THEN { IF comm.n=1 THEN width _ width/lambda*lambda ELSE width _ GetAnInt[width, wireRef.define.default, wireRef.define.button.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, layer]; RETURN }; CDLayers.SetLayerWidth[design, layer, MAX[width, 0]]; }; DoFromDisplayVal: PROC [actual: Actual] RETURNS [x: REF] = { RETURN [ IF actual.define.entryClass.fromdisplay=NIL THEN CDValue.Fetch[actual.panel.design, actual.define.val.cdValueKey, global] ELSE actual.define.entryClass.fromdisplay[actual] ] }; FromDisplayNoStoreVal: PROC [design: CD.Design, cdValueKey: REF] RETURNS [REF_NIL] = { panel: Panel = GetPanel[design]; FOR list: LIST OF Actual _ panel.actuals, list.rest WHILE list#NIL DO IF list.first.define.val.cdValueKey=cdValueKey THEN RETURN [DoFromDisplayVal[list.first]] ENDLOOP; }; TakeDown: PUBLIC PROC [design: CD.Design, cdValueKey: REF] RETURNS [x: REF] = { x _ FromDisplayNoStoreVal[design, cdValueKey]; WITH x SELECT FROM rt: REF TEXT => x _ Rope.FromRefText[rt]; ENDCASE => NULL; CDValue.Store[design, cdValueKey, x]; }; TakeDownText: PUBLIC PROC [design: CD.Design, cdValueKey: REF] RETURNS [Rope.ROPE] = { WITH TakeDown[design, cdValueKey] SELECT FROM r: Rope.ROPE => RETURN [r]; ENDCASE => RETURN [NIL]; }; ToDisplayNoFetchVal: PROC [design: CD.Design, cdValueKey: REF, value: REF] = { panel: Panel = GetPanel[design]; IF panel.container=NIL OR panel.container.destroyed THEN RETURN; FOR list: LIST OF Actual _ panel.actuals, list.rest WHILE list#NIL DO IF list.first.define.val.cdValueKey=cdValueKey THEN { ToDisplay[list.first, value]; } ENDLOOP; }; PutUpText: PUBLIC PROC [design: CD.Design, cdValueKey: REF, rope: Rope.ROPE] = { CDValue.Store[design, cdValueKey, rope]; ToDisplayNoFetchVal[design, cdValueKey, rope]; }; PutUp: PUBLIC PROC [design: CD.Design, cdValueKey: REF] RETURNS [REF_NIL] = { value: REF; IF cdValueKey=$PanelLayerMode THEN { panel: Panel _ GetPanel[design]; NotifyChangeLayerOption[panel.container, panel, red, TRUE, TRUE]; RETURN }; value _ CDValue.Fetch[design, cdValueKey, global]; ToDisplayNoFetchVal[design, cdValueKey, value]; RETURN [value] }; ContainerDeleted: ViewerEvents.EventProc = { WITH ViewerOps.FetchProp[viewer, $CDPanelsPrivate] SELECT FROM panel: Panel => { ViewerOps.AddProp[viewer, $ChipNDaleDesign, NIL]; ViewerOps.AddProp[viewer, $CDPanelsPrivate, 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.actuals _ NIL; }; }; ENDCASE => NULL; }; EnumDesigns: PROC[p: CDPrivate.DesignEnumerator] RETURNS [quit: BOOL_FALSE] = { 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; }; 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] }; [] _ CDValue.RegisterKey[panelKey, NIL]; [] _ CDValue.RegisterKey[classKey, 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. 0CDPanelImpl.mesa (part of ChipNDale) Copyright c 1983, 1987 by Xerox Corporation. All rights reserved. Created by Christian Jacobi, August 8, 1983 5:20 pm Last Edited by: Christian Jacobi, March 18, 1987 12:37:04 pm PST Terminology in procedure names Build: build entry [instance] into a viewer Define: define [register] behaviour for class PutUp: reset picture on screen from CDValue Called: proc is called by interactive action --Compensations --the viewer package uses funny y positions; use this to compensate --the height sometimes are not enogh to reasonably display contents... -- Viewer --supress creation of viewer if empty --Get rid of panels which are not used anymore --No user visible action -- Define's =========================================================== --we need to find names -- special treatment for layers =========================================================== EXIT; don't exit in case of multiple buttons... -- Viewer Stuff -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --uses intermediate layer variables! --special hack to make this outside accessible --called from special buttons which have a Text as actual.feedback --new contents is not stored to CDValue yet!! (It wont if edited either) --called from special buttons which have a Label as actual.feedback --default width for buttons --default spacing -- ================================================ --special hack to change layer mode --break circularities --vanilla utilities ================================ --module initialization ================================ Ê&˜codešœ'™'Kšœ Ïmœ7™BKšœ4™4K™@K™—šÏk ˜ K˜ Kšžœ˜K˜K˜K˜ Kšœ ˜ Kšœ˜Kšœ˜K˜ Kšœ ˜ K˜ K˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜K˜ K˜ Kšœ ˜ Kšœ˜K˜ K˜ —K˜šÏn œžœžœ˜Kšžœ žœÝ˜ðKšžœ˜Kšžœ˜—Kšž˜K˜šÏb™Kš œ&™+Kš œ'™-Kš œ&™+Kš œ&™,—K˜Kšžœžœžœ˜Kšœžœ˜$Kšœžœ˜K˜šœžœžœ˜Kšœžœ˜Kšœžœ˜K˜—Kšœžœ˜K˜Kšœžœžœ Ïc,˜Hšœ žœžœ˜Kšœžœ˜Kšœ žœ˜Kšœžœžœ˜6Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœ žœ˜Kšœžœ˜Kšœ žœžœ žœ˜"Kšœ žœžœ ž˜Kšœ˜—Kšœ žœžœžœ˜!K˜Kšœ žœžœ¡˜@šœžœžœ˜Kšœ˜Kšœžœ˜Kšœžœ¡˜?Kšœ žœž˜Kšœ˜—K˜Kšœ žœžœ"¡˜WKšœžœžœžœ˜9Kš œžœžœžœ žœžœ˜HK˜Kšœ žœžœ¡˜5šœ žœžœ˜Kšœ˜Kšœ¡)˜>Kšœžœ˜#Kšœžœ žœžœ˜1Kšœžœžœ˜ Kšœ žœ˜%Kšœ žœžœ˜Kšœ,˜,Kšœžœž˜K˜—K˜Kšœ žœžœžœ ˜%Kšœžœžœ ¡˜2K˜Kšœžœžœ ¡0˜Ošœ žœžœ˜Kšœ˜Kšœ˜Kšœžœ¡;˜RKšœ˜—K˜Kš œ žœžœžœžœ ¡-˜UKš œ žœžœžœžœ˜+Kšœžœ žœ˜(Kšœžœ˜K˜šœ/˜/Kšœ˜Kšœ˜—šœ.˜.Kšœ˜Kšœ˜Kšœ˜—šœ-˜-Kšœ˜Kšœ˜Kšœ˜—šœ,˜,Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœ-˜-Kšœ˜Kšœ˜—šœ+˜+Kšœ˜Kšœ˜Kšœ˜—K˜šœ™K™CKšœžœ˜Kšœžœ˜ Kšœžœ˜ K™FKšœžœ˜—K˜šœ ™ Kšœžœ˜Kšœ žœ˜Kšœ žœ˜Kšœžœ˜!K˜—K˜šŸœžœžœ˜PKšœ žœ˜ K˜—K˜šŸœžœ žœ žœ ˜6KšœžœF˜Lšžœžœžœ˜šœžœ ˜Kšœ˜Kšœ"˜"Kšœ˜—Kšœ7˜7Kšžœ˜Kšœ˜—Kšžœžœ ˜Kšœ˜K˜—šŸœžœžœ žœ ˜8Kšžœžœžœžœ˜&šžœ˜KšœžœH˜Nšžœžœžœ˜Kšœžœ žœ˜"šžœ1ž˜7Kšœžœ˜0—Kšžœ˜Kšœ˜—Kšžœžœ ˜K˜—Kšœ˜K˜—š Ÿœžœžœžœ žœ žœ ˜BKšžœžœžœ˜K˜ Kšœ+˜+Kš žœžœžœžœžœžœ˜WKšœžœžœ˜+Kšœ%™%Kšžœžœžœ˜+Kšœ˜šžœžœžœ˜Kšœ-˜-šžœ;žœž˜HKšœ$˜$—K˜—Kšžœ˜Kšœ˜K˜—šŸœžœžœ˜,K™/K™Kšœžœ˜šžœ(žœžœž˜<š žœžœžœ!žœžœ˜VKšœ žœ˜)—Kšžœ˜—Kšžœžœžœžœ˜/Kšœ˜Kšœ˜—K˜šŸ œžœžœ#˜˜>K˜—K˜šŸ œ˜Kšœžœ˜Kšœ˜Kšœžœ˜ Kšœžœ!˜2Kšžœžœ'˜Bšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜—šžœžœžœ˜"šœ žœ˜(Kšœ˜Kšœ˜Kšœ˜—Kšœžœ˜,Kšœ˜K˜—Kšœ˜—K˜šŸœ˜!Kšœ8˜8K˜—K˜K˜šŸœžœžœžœ žœžœžœžœ˜QšŸœžœžœžœžœžœžœžœžœžœ˜RKšžœžœžœžœ˜š žœžœžœžœžœžœž˜1šžœžœžœ˜)Kš žœžœžœžœ žœ˜8—Kšžœ˜—Kšžœ ˜K˜—šžœ"žœž˜1Kšœžœžœ˜Kšœžœžœžœ˜.Kš œžœžœžœžœ˜1Kšžœžœžœ˜—K˜—K˜šŸ œ˜ KšœB™BKšœžœ ˜$Kšœ žœžœ˜+Kšœ¡˜7šžœžœž˜Kšœ/žœ˜4šœ&¡.˜TKšœ žœo˜}Kšœ%˜%Kšœ žœ@˜OK˜—Kšžœžœ˜—Kšœ'˜'KšœH™HK˜—K˜šŸœ˜#KšœC™CšŸœžœžœžœžœžœžœ˜Ešžœ"žœž˜1Kšœžœžœ˜Kšœžœžœžœ˜.Kšžœžœžœ˜—Kšœ˜—Kšœžœ˜Kšœžœ ˜$Kšœ žœ ˜/Kšœ9˜9KšœP˜PKšœ9˜9Kšœ&˜&K˜—K˜š Ÿ œžœžœžœžœ˜-Kš¡™Kš žœžœžœžœžœ ˜)K˜—K˜š Ÿ œžœžœžœžœ˜-Kš¡™Kš žœžœžœ žœžœ ˜2K˜—K˜šŸ œ˜Kšœžœ˜Kšœžœ5žœ˜OKšœžœ˜ Kšœžœ˜Kšœžœ!˜2Kšœ9˜9Kšœ˜Kšœ)˜)Kšžœžœ'˜Bšžœ"žœ˜*šœ(˜(šœ ˜ Kšœ˜Kšœ#˜#Kšœžœ˜K˜K˜—Kšœ˜Kšœžœžœ žœ˜Kšœ˜Kšœžœ$˜.Kšœžœ!˜2Kšžœžœ'˜Bšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜—šžœžœžœ˜"Kšœžœ;˜OKšœžœ˜,Kšœ˜K˜—Kšœ˜—K˜šŸœ˜"Kšœ=˜=K˜—K˜š¢ œ˜$Kšœžœ ˜&šœžœ˜8Kšœ˜Kšœžœ ˜*Kšœžœ˜Kšœ žœ ˜Kšœ ˜ Kšœ˜—šžœ žœž˜KšœK˜K—šžœžœžœž˜KšœQ˜Q—Kšœ˜K˜—K˜šŸœžœžœ žœ žœžœžœ˜UKšžœžœ ˜,Kšœžœžœžœ˜Kšœ ˜ šžœ žœ˜Kšœ žœ,˜9—šžœ˜Kšœ žœj˜w—šžœ7ž˜AKšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ9˜9Kšœ^˜^Kšœ˜Kšžœ$˜+—Kšžœ žœ˜Kšœ˜—š¢ œ˜!Kšœžœžœ ˜6Kšžœžœžœžœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜šœžœ˜$Kšœ˜šœžœ žœ˜Kšœ1˜1Kšžœžœ˜—Kšœžœ žœžœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—K˜š¢œžœ ˜9Kšžœžœžœ˜Kšœžœ˜!Kšœžœ ˜#Kšœ!˜!Kšœžœ˜#KšœžœK˜Qšžœžœž˜šœ ˜ Kšžœ žœ˜%Kšžœ<˜@K˜—šœ˜Kšžœ žœžœ˜.K˜—šœ˜Kšžœ žœžœ˜6Kšœ˜—Kšžœžœ˜—Kšœžœžœ˜(Kšœ3˜3KšœD˜DKšœ˜K˜—šŸ œ˜K˜2K˜Kšœ˜K˜—šŸ œ˜Kšœ žœ,˜;Kšœžœ˜šœ(˜(˜ Kšœ˜Kšœ˜K˜Kšœ'˜'Kšœž˜ K˜—K˜Kšœ˜K˜—šœ˜˜Kšœ˜Kšœ6˜6K˜Kšœ¡7˜TKšœ'˜'Kšœžœ˜šœ˜Kšœ2˜2Kšœ&˜&Kšœ˜—Kšœ˜—K˜—Kšœ@˜@Kšœžœ˜/Kšœ˜K˜—š¢œ˜'Kšœžœ ˜%šœ˜Kšœ!˜!Kšœ˜Kšœ˜šœžœ˜$Kšœ˜šœžœ žœ˜Kšœ˜Kšœ˜Kšœ˜Kšžœžœ˜—Kšœžœ žœžœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—K˜šŸ¢œžœ˜>Kšœžœ ˜$Kšœžœ˜'Kšœžœ˜)Kšœžœ-˜6Kšœžœ˜'šžœžœ˜Kšžœ žœ˜-KšžœT˜XK˜—šžœžœžœ˜Kšžœ žœžœ˜@Kšœ˜—šžœžœžœ˜Kšžœ žœžœ˜FKšœ˜—šžœžœžœ˜Kšœ(˜(Kšž˜Kšœ˜—Kšœ&žœ ˜5Kšœ˜—K˜šŸœžœžœžœ˜<šžœ˜šžœ&žœ˜,KšžœI˜MKšžœ-˜1—Kšœ˜—K˜—K˜šŸœžœ žœžœžœžœžœ˜VK˜ š žœžœžœ#žœžœž˜Ešžœ-ž˜3Kšžœ˜%—Kšžœ˜—Kšœ˜—K˜š Ÿœž œ žœžœžœžœ˜OKšœ.˜.šžœžœž˜Kšœžœžœ˜)Kšžœžœ˜—Kšœ%˜%K˜—K˜šŸ œžœžœ žœžœžœžœ˜Všžœžœž˜-Kšœžœžœ˜Kšžœžœžœ˜—Kšœ˜—K˜š Ÿœžœ žœžœ žœ˜NK˜ Kš žœžœžœžœžœ˜@š žœžœžœ#žœžœž˜Ešžœ-žœ˜5Kšœ˜K˜—Kšžœ˜—K˜—K˜š Ÿ œžœžœ žœžœ žœ˜PKšœ(˜(Kšœ.˜.Kšœ˜—K˜šŸœžœžœ žœžœžœžœžœ˜MKšœž˜ Kšœ#™#šžœžœ˜$Kšœ ˜ Kšœ5žœžœ˜AKšž˜K˜—Kšœ2˜2Kšœ/˜/Kšžœ˜Kšœ˜K˜—š¢œ˜,Kšœ™šžœ/žœž˜>šœ˜Kšœ,žœ˜1Kšœ,žœ˜1šžœžœžœžœ˜5Kšœ&žœ˜+Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœ˜K˜—K˜—Kšžœžœ˜—K˜—K™š Ÿ œžœ žœžœžœ˜Oš žœ(žœžœžœž˜Fšžœžœžœ!žœ˜FKšœžœ˜!Kšžœžœžœ ˜Kšœ˜—Kšžœ˜—Kšœ˜—K™Kš¡4™4K˜š Ÿœžœžœžœžœžœ˜-Kšžœžœžœžœžœžœžœ ˜3Kšžœ˜ K˜K˜—šŸœžœžœžœžœžœžœ˜4Kšžœžœžœžœ ˜Kšžœ˜ K˜K˜—šŸœžœžœžœžœžœžœ˜4Kšžœžœžœžœ ˜ Kšžœ˜ K˜—K™Kšœ¡!™8Kšœ#žœ˜(Kšœ#žœ˜(Kšœ'˜'K˜;K˜8K˜7KšœO˜OKšœK˜KKšœ/˜/Kšžœ˜K˜K˜—…—q Ò