DIRECTORY Atom, AtomButtons, AtomButtonsTypes, BasicTime, ButtonClassesCommon, CedarProcess, CodeTimer, Convert, EBButtonClasses, EBEvent, EBLanguage, EBMesaLisp, EBTypes, EmbeddedButtons, Imager, InputFocus, IO, List, PopUpButtons, Process, RefTab, Rope, RuntimeError, ScreenCoordsTypes, SimpleFeedback, TIPPrivate, TIPTypes, TIPUser, UserInput, UserInputLookahead, UserInputOps, ViewerClasses; ButtonClassesImpl: CEDAR MONITOR IMPORTS Atom, AtomButtons, BasicTime, ButtonClassesCommon, CedarProcess, CodeTimer, Convert, EBButtonClasses, EBLanguage, EBMesaLisp, EmbeddedButtons, Imager, InputFocus, IO, PopUpButtons, Process, Rope, RuntimeError, SimpleFeedback, UserInputLookahead, UserInputOps, TIPPrivate, TIPUser EXPORTS ButtonClassesCommon, EBTypes = BEGIN ButtonInfo: TYPE = EBTypes.ButtonInfo; Context: TYPE = EBTypes.Context; EventRep: PUBLIC TYPE = EBEvent.EventRep; -- for EBTypes ROPE: TYPE = Rope.ROPE; Variable: TYPE = EBLanguage.Variable; VariableObj: TYPE = EBLanguage.VariableObj; VariableType: TYPE = EBLanguage.VariableType; VariableTypeObj: TYPE = EBLanguage.VariableTypeObj; VariableTable: TYPE = EBLanguage.VariableTable; TIPScreenCoords: TYPE = ScreenCoordsTypes.TIPScreenCoords; ActiveClientData: TYPE = REF ActiveClientDataRec; ActiveClientDataRec: TYPE = RECORD[ buttonInfo: ButtonInfo, instanceData: REF]; popUpStateClass: EBButtonClasses.ButtonClass ¬ NEW[EBButtonClasses.ButtonClassObj ¬ [ name: $PopUpStateButton, instantiate: PopUpStateInstantiate, unparseInstanceData: PopUpStateUnparse, handleEvent: PopUpStateHandle, getValue: PopUpStateGet, setValue: PopUpStateSet, defaultBehavior: PopUpStateDefaultBehavior]]; PopUpStateData: TYPE = REF PopUpStateDataObj; PopUpStateDataObj: TYPE = RECORD[ popUp: REF, multi: Multi, newValue: REF ¬ NIL ]; bigFont: Imager.Font ¬ Imager.FindFontScaled["xerox/tiogafonts/helvetica10", 1.0]; PopUpStateInstantiate: EBButtonClasses.InstantiateProc = { menuName: ROPE; choices: AtomButtons.PopUpChoices; self: PopUpStateData; quickClickEnabled: BOOL ¬ TRUE; IF language # $Poppy THEN RETURN[NIL]; self ¬ NEW[PopUpStateDataObj]; self.multi ¬ NEW[MultiRec ¬ [ name: NIL, nameExists: FALSE, value: NEW[VariableObj ¬ [ type: NEW[VariableTypeObj[3]], value: NEW[INT ¬ 1]]], message: NIL, mouseInside: FALSE, defaultDirection: FALSE -- down ]]; self.multi.value.type.class ¬ enumerated; FOR i: INT IN [1..3] DO self.multi.value.type.enumeration[i-1] ¬ NEW[INT ¬ i]; ENDLOOP; MultiInstantiateAux[buttonInfo, language, languageVersion, self.multi, initFeedback]; BEGIN menuRope: ROPE ¬ EmbeddedButtons.GetFieldRope[$Menu, buttonInfo]; IF menuRope = NIL THEN { choices ¬ PopUpStateMakeMenu[self.multi]; menuName ¬ "Values"; } ELSE { ropeStream: IO.STREAM ¬ IO.RIS[menuRope]; object: REF; quickClickRope: ROPE ¬ EmbeddedButtons.GetFieldRope[$QuickClickEnabled, buttonInfo]; quickClickEnabled ¬ IF quickClickRope = NIL THEN TRUE ELSE Convert.BoolFromRope[quickClickRope ! Convert.Error => {quickClickEnabled ¬ TRUE; CONTINUE}]; object ¬ EBMesaLisp.Parse[ropeStream].val; IF object = NIL THEN { menuName ¬ "DummyName"; choices ¬ LIST[[action: LIST[$Dummy], actionImage: "No Action", doc: "Embedded Buttons couldn't parse the Menu entry for this button"]]; } ELSE [menuName, choices] ¬ PopUpStateParseMenu[object, menuRope]; }; END; BEGIN entry: AtomButtons.ButtonLineEntry ¬ [popUpButton[ name: menuName, choices: choices, help: PopUpButtons.noHelp, -- providing this field saves lots of compute time in PCedar font: bigFont, disableDecoding: NOT quickClickEnabled ]]; self.popUp ¬ AtomButtons.BuildPopUp[ clientData: self, handleProc: PopUpStatePassEventToApplication, paint: PopUpPaint, inButton: PopUpInButton, entry: entry, clientPackageName: "ButtonClasses"]; END; RETURN[self]; }; PopUpStateMenuAction: TYPE = REF PopUpStateMenuActionObj; PopUpStateMenuActionObj: TYPE = RECORD [ count: NAT, message: LIST OF REF, value: REF ]; GetMenuListAndName: PROC [obj: REF, instanceData: ROPE] RETURNS [o: LIST OF REF, menuName: ROPE ¬ NIL, success: BOOL ¬ TRUE] = { IF NOT ISTYPE[obj, LIST OF REF] THEN { EmbeddedButtons.Error["Pop-up button: Invalid ButtonData!", Rope.Concat["The ButtonData for this pop-up button is invalid:\n", instanceData]]; RETURN[NIL, NIL, FALSE]; }; o ¬ NARROW[obj]; IF o.first # NIL AND ISTYPE[o.first, ROPE] THEN { menuName ¬ NARROW[o.first]; o ¬ o.rest; }; }; GetMessageAsList: PROC [entry: LIST OF REF] RETURNS [event: LIST OF REF] = { IF entry.first = NIL THEN event ¬ NIL ELSE { WITH entry.first SELECT FROM lor: LIST OF REF => event ¬ lor; ENDCASE => event ¬ LIST[entry.first]; }; }; GetMenuEntry: PROC [entry: LIST OF REF] RETURNS [menuEntry: ROPE, success: BOOL ¬ TRUE] = { IF entry.rest = NIL THEN RETURN[NIL, FALSE]; IF NOT ISTYPE[entry.rest.first, ROPE] THEN RETURN[NIL, FALSE]; menuEntry ¬ NARROW[entry.rest.first]; }; GetDocString: PROC [entry: LIST OF REF] RETURNS [docString: ROPE ¬ NIL, success: BOOL ¬ TRUE] = { IF entry.rest.rest = NIL THEN docString ¬ "" ELSE { IF NOT ISTYPE[entry.rest.rest.first, ROPE] THEN RETURN[NIL, FALSE]; docString ¬ NARROW[entry.rest.rest.first]; }; }; PopUpStateParseMenu: PROC [obj: REF, instanceData: ROPE] RETURNS [menuName: ROPE ¬ NIL, choices: AtomButtons.PopUpChoices ¬ NIL] = { o: LIST OF REF; success: BOOL ¬ TRUE; choice: AtomButtons.PopUpChoice; i: NAT ¬ 0; [o, menuName, success] ¬ GetMenuListAndName[obj, instanceData]; IF NOT success THEN RETURN; FOR list: LIST OF REF ¬ o, list.rest UNTIL list = NIL DO -- for each entry... entry: LIST OF REF; event: LIST OF REF; menuEntry: ROPE; docString: ROPE; BEGIN action: PopUpStateMenuAction; IF NOT ISTYPE[list.first, LIST OF REF] THEN GOTO FieldTypeError; entry ¬ NARROW[list.first]; IF entry # NIL THEN { -- get the application message, entry name, and doc string event ¬ GetMessageAsList[entry]; [menuEntry, success] ¬ GetMenuEntry[entry]; IF NOT success THEN GOTO FieldTypeError; [docString, success] ¬ GetDocString[entry]; IF NOT success THEN GOTO FieldTypeError; } ELSE {event ¬ NIL; menuEntry ¬ NIL; docString ¬ NIL}; action ¬ NEW[PopUpStateMenuActionObj ¬ [ count: i, message: event, value: NIL ]]; choice ¬ [action: LIST[action], actionImage: menuEntry, doc: docString, font: bigFont]; choices ¬ PopUpAppend[choices, LIST[choice]]; EXITS FieldTypeError => { EmbeddedButtons.Error["Pop-up buttons: Invalid field in menu!", Rope.Concat["The menu description for this pop-up button contains an invalid field:\n", instanceData]]; }; END; i ¬ i + 1; ENDLOOP; }; PopUpStateMakeMenu: PROC [multi: Multi] RETURNS [choices: AtomButtons.PopUpChoices ¬ NIL] = { AddChoice: PROC [action: ATOM, entry: ROPE, doc: ROPE, count: NAT] = { choice: AtomButtons.PopUpChoice; pusmAction: PopUpStateMenuAction; pusmAction ¬ NEW[PopUpStateMenuActionObj ¬ [ count: count, message: LIST[action], value: NIL ]]; choice ¬ [action: LIST[pusmAction], actionImage: entry, doc: doc, font: bigFont]; choices ¬ PopUpAppend[choices, LIST[choice]]; }; SELECT multi.value.type.class FROM enumerated => { FOR i: NAT IN [0..multi.value.type.count) DO docString, menuEntry: ROPE; choice: AtomButtons.PopUpChoice; action: PopUpStateMenuAction; WITH multi.value.type[i] SELECT FROM rope: Rope.ROPE => menuEntry ¬ rope; atom: ATOM => menuEntry ¬ Atom.GetPName[atom]; ENDCASE => menuEntry ¬ "no value"; docString ¬ IO.PutFR1["Set to value %g", [rope[menuEntry]] ]; action ¬ NEW[PopUpStateMenuActionObj ¬ [ count: i, message: LIST[$ButtonSetValue, menuEntry], value: multi.value.type[i] ]]; choice ¬ [action: LIST[action], actionImage: menuEntry, doc: docString, font: bigFont]; choices ¬ PopUpAppend[choices, LIST[choice]]; ENDLOOP; }; integer => { AddChoice[$ButtonValueUp, "Up", "increase value by 1", 0]; AddChoice[$ButtonValueUp, "Up", "increase value by 1", 1]; AddChoice[$ButtonValueDown, "Down", "decrease value by 1", 2]; }; real => { AddChoice[$ButtonValueUp, "Up", "multiply value by 2", 0]; AddChoice[$ButtonValueUp, "Up", "multiply value by 2", 1]; AddChoice[$ButtonValueDown, "Down", "divide value by 2", 2]; }; ENDCASE => NULL; }; PassEvent: PROC [msg: REF, buttonInfo: ButtonInfo] = { msgAsList: LIST OF REF; WITH msg SELECT FROM list: LIST OF REF => msgAsList ¬ list; ENDCASE => msgAsList ¬ LIST[msg]; EmbeddedButtons.PassEventToApplication[msgAsList, buttonInfo]; }; PopUpStatePassEventToApplication: AtomButtons.HandlePopUpProc = { PreprocessEvent: PROC [event: LIST OF REF] RETURNS [newEvent: LIST OF REF] = { IF event = NIL THEN RETURN[event]; IF event.first = NIL THEN RETURN[event]; WITH event.first SELECT FROM ours: PopUpStateMenuAction => { self: PopUpStateData ¬ NARROW[clientData]; newEvent ¬ ours.message; self.newValue ¬ ours.value; IF self.newValue = NIL THEN { IF (ours.count MOD 3 = 2) THEN self.multi.defaultDirection ¬ TRUE ELSE self.multi.defaultDirection ¬ FALSE; }; }; ENDCASE => newEvent ¬ event; }; WITH button SELECT FROM buttonInfo: ButtonInfo => { message: REF; event ¬ PreprocessEvent[event]; EBLanguage.SetSystemValue[clickContext, $MessageReceiver, EmbeddedButtons.GetApplications[buttonInfo]]; message ¬ EBLanguage.Evaluate[event, buttonInfo, clientData, clickContext]; IF message # NIL THEN PassEvent[message, buttonInfo]; }; ENDCASE => { EmbeddedButtons.Error["Pop-up buttons: button is wrong type (PopUpStatePassEventToApplication)"]; }; }; PopUpStateUnparse: EBButtonClasses.UnparseInstanceDataProc = { self: PopUpStateData; rope: ROPE; ppListPoppy: LIST OF REF; multi: Multi; IF instanceData = NIL THEN RETURN[""]; IF NOT ISTYPE[instanceData, PopUpStateData] THEN { EmbeddedButtons.Error["PopUpState buttons: Internal Error, button is wrong type (PopUpStateUnparse)"]; RETURN[""]; }; self ¬ NARROW[instanceData]; multi ¬ self.multi; ppListPoppy ¬ LIST[ "", refFALSE, "\n ", refFALSE]; IF multi.name # NIL AND NOT ButtonClassesCommon.IsGeneratedRope[Atom.GetPName[multi.name]] THEN EmbeddedButtons.SetFieldRope[$Name, EBMesaLisp.Unparse[multi.name], buttonInfo]; SELECT language FROM $Poppy => { tableRef: REF; table: VariableTable; type: VariableType; tableRef ¬ EmbeddedButtons.GetFieldRef[$Variables, buttonInfo]; IF ISTYPE[tableRef, VariableTable] AND tableRef # NIL THEN table ¬ NARROW[tableRef] ELSE { table ¬ EBLanguage.CreateVariableTable[]; EmbeddedButtons.SetFieldRef[$Variables, table, buttonInfo]; }; IF multi.value.type = booleanEnumeratedType THEN { type ¬ NEW[VariableTypeObj[0]]; type.class ¬ boolean; EBLanguage.SetVariable[table, $Value, NEW[VariableObj ¬ [ value: multi.value.value, type: type]]]; } ELSE EBLanguage.SetVariable[table, $Value, multi.value]; }; ENDCASE; SELECT language FROM $Poppy => IF multi.message # NIL THEN EmbeddedButtons.SetFieldRope[$UpClickMessage, EBMesaLisp.Unparse[multi.message, ppListPoppy], buttonInfo]; ENDCASE; SELECT language FROM $Poppy => rope ¬ NIL; -- A button class doesn't know enough to unparse any further. Embedded Buttons will. ENDCASE; RETURN[rope]; }; PopUpStateHandle: EBButtonClasses.HandleEventProc ~ { ENABLE UNWIND => { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, "Pressing that PopUpButton caused an internal error."]; }; self: PopUpStateData ¬ NARROW[instanceData]; ProduceStandardFeedback[event, buttonInfo]; IF instanceData = NIL OR self.popUp = NIL THEN GOTO NoPop ELSE { maybeInstance: PopUpButtons.MaybeInstance ¬ PopUpButtons.QuaInstance[self.popUp]; IF NOT maybeInstance.is THEN GOTO NoPop; PopUpButtons.RawNotify[buttonInfo, maybeInstance.inst, event.handle, event.action]; }; EXITS NoPop => EmbeddedButtons.Error["This PopUpStateButton is broken (syntax problem?)"]; }; PopUpStateGet: EBButtonClasses.GetValueProc = { self: PopUpStateData; multi: Multi; IF instanceData = NIL THEN RETURN[NIL]; IF NOT ISTYPE[instanceData, PopUpStateData] THEN { EmbeddedButtons.Error["PopUpState buttons: Internal error. Button is wrong type (PopUpStateGet)."]; RETURN[NIL]; }; self ¬ NARROW[instanceData]; multi ¬ self.multi; value ¬ multi.value.value; EmbeddedButtons.RegisterNameValuePair[multi.name, value, buttonInfo]; }; PopUpStateSet: EBButtonClasses.SetValueProc = { self: PopUpStateData; multi: Multi; IF instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, PopUpStateData] THEN {EmbeddedButtons.Error["PopUpState buttons: Internal error. Button is wrong type (PopUpStateSet)."]; RETURN}; self ¬ NARROW[instanceData]; multi ¬ self.multi; IF multi.value.type.class = integer THEN { WITH value SELECT FROM i: REF INT => { multi.value.value ¬ value; changed ¬ TRUE; EmbeddedButtons.FeedbackNotify[LIST[$Value, value], buttonInfo]; }; ENDCASE => EmbeddedButtons.Error[IO.PutFR["Attempt to set button %g (type INT) to %g", [rope[Atom.GetPName[multi.name]]], [rope[EBMesaLisp.Unparse[value]]] ]]; } ELSE IF multi.value.type.class = real THEN { WITH value SELECT FROM i: REF REAL => { multi.value.value ¬ value; changed ¬ TRUE; EmbeddedButtons.FeedbackNotify[LIST[$Value, value], buttonInfo]; }; ENDCASE => EmbeddedButtons.Error[IO.PutFR["Attempt to set button %g (type REAL) to %g", [rope[Atom.GetPName[multi.name]]], [rope[EBMesaLisp.Unparse[value]]] ]]; } ELSE { FOR i: NAT IN [0..multi.value.type.count) DO IF EBLanguage.Equal[value, multi.value.type.enumeration[i]] THEN { multi.value.value ¬ value; changed ¬ TRUE; EmbeddedButtons.FeedbackNotify[LIST[$Value, value], buttonInfo]; EXIT; }; ENDLOOP; }; }; PopUpStateDefaultBehavior: EBButtonClasses.DefaultBehaviorProc = { self: PopUpStateData; multi: Multi; state: INT ¬ 0; newValue: REF; IF instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, PopUpStateData] THEN { EmbeddedButtons.Error["PopUpState: Internal error. Button is wrong type (PopUpStateDefaultBehavior)."]; RETURN; }; self ¬ NARROW[instanceData]; multi ¬ self.multi; IF self.newValue # NIL THEN { newValue ¬ self.newValue; } ELSE { IF multi.value.type.class = integer THEN { oldValue: REF INT ¬ NARROW[multi.value.value]; IF NOT multi.defaultDirection THEN newValue ¬ NEW[INT ¬ oldValue­ +1] ELSE newValue ¬ NEW[INT ¬ oldValue­ -1]; } ELSE IF multi.value.type.class = real THEN { oldValue: REF REAL ¬ NARROW[multi.value.value]; IF NOT multi.defaultDirection THEN newValue ¬ NEW[REAL ¬ oldValue­ *2.0] ELSE newValue ¬ NEW[REAL ¬ oldValue­ /2.0]; } ELSE { FOR i: NAT IN [0..multi.value.type.count) DO IF EBLanguage.Equal[multi.value.value, multi.value.type.enumeration[i]] THEN { state ¬ i; EXIT }; ENDLOOP; IF NOT multi.defaultDirection THEN { state ¬ state + 1; IF state >= multi.value.type.count THEN state ¬ 0; -- Greater than equal for safety? } ELSE { state ¬ state - 1; IF state < 0 THEN state ¬ multi.value.type.count-1; }; newValue ¬ multi.value.type.enumeration[state]; }; }; IF multi.nameExists THEN EmbeddedButtons.SetValue[multi.name, newValue, buttonInfo.doc] ELSE { changed: BOOL ¬ PopUpStateSet[instanceData, newValue, buttonInfo]; IF changed THEN EBButtonClasses.MarkButtonAsChanged[buttonInfo]; }; }; buttonClass: EBButtonClasses.ButtonClass ¬ NEW[EBButtonClasses.ButtonClassObj ¬ [ name: $GuardedButton, instantiate: ButtonInstantiate, unparseInstanceData: NIL, handleEvent: ButtonHandle, getValue: NIL, setValue: NIL, defaultBehavior: NIL]]; ObjectAsEvents: PROC [object: REF] RETURNS [events: LIST OF LIST OF REF] = { WITH object SELECT FROM es: LIST OF LIST OF REF => RETURN[es]; e: LIST OF REF => RETURN[LIST[e]]; ENDCASE => RETURN[LIST[LIST[object]]]; }; ButtonInstantiate: EBButtonClasses.InstantiateProc = { menuName: ROPE; events: LIST OF AtomButtons.Event; guarded: BOOL ¬ FALSE; CodeTimer.StartInt[$GuardedInstantiate, $EmbeddedButtons]; SELECT language FROM $Poppy => { object: REF; actionRope: ROPE ¬ EmbeddedButtons.GetFieldRope[$Message, buttonInfo]; guardedRope: ROPE ¬ EmbeddedButtons.GetFieldRope[$GuardMessage, buttonInfo]; ropeStream: IO.STREAM ¬ IO.RIS[actionRope]; object ¬ EBMesaLisp.Parse[ropeStream].val; events ¬ ObjectAsEvents[object]; guarded ¬ guardedRope # NIL; }; ENDCASE => { CodeTimer.StopInt[$GuardedInstantiate, $EmbeddedButtons]; RETURN[NIL]; }; BEGIN entry: AtomButtons.ButtonLineEntry ¬ [button[ name: "", events: events, font: bigFont, confirmProc: NIL -- overridden by the confirmProc argument below ]]; instance ¬ BuildButtonHandler[ clientData: NIL, handleProc: ButtonPassEventToApplication, confirmProc: IF guarded THEN ButtonConfirm ELSE NIL, entry: entry, clientPackageName: "ButtonClasses"]; END; CodeTimer.StopInt[$GuardedInstantiate, $EmbeddedButtons]; RETURN[instance]; }; ButtonConfirm: AtomButtons.HandlePopUpProc = { ENABLE RuntimeError.Uncaught => { GOTO Error; }; message: REF; buttonInfo: ButtonInfo; WITH button SELECT FROM buttonInfo: ButtonInfo => { confirmRope: ROPE ¬ EmbeddedButtons.GetFieldRope[$GuardMessage, buttonInfo]; ropeStream: IO.STREAM ¬ IO.RIS[confirmRope]; object: REF; event: LIST OF REF; EBLanguage.SetSystemValue[clickContext, $MessageReceiver, EmbeddedButtons.GetApplications[buttonInfo]]; object ¬ EBMesaLisp.Parse[ropeStream].val; message ¬ EBLanguage.Evaluate[object, buttonInfo, clientData, clickContext]; IF message # NIL THEN PassEvent[message, buttonInfo]; }; ENDCASE => SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, "Pressing that guarded button caused an internal error."]; EXITS Error => { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, "ButtonConfirm received an uncaught error."]; }; }; ButtonPassEventToApplication: AtomButtons.HandlePopUpProc = { message: REF; WITH button SELECT FROM buttonInfo: ButtonInfo => { EBLanguage.SetSystemValue[clickContext, $MessageReceiver, EmbeddedButtons.GetApplications[buttonInfo]]; message ¬ EBLanguage.Evaluate[event, buttonInfo, clientData, clickContext]; IF message # NIL THEN PassEvent[message, buttonInfo]; }; ENDCASE => { EmbeddedButtons.Error["Button is wrong type (ButtonPassEventToApplication)"]; }; }; ButtonHandle: EBButtonClasses.HandleEventProc = { errorMsg: ROPE; BEGIN ENABLE BEGIN RuntimeError.NarrowRefFault => {errorMsg ¬ "NarrowRefFault"; GOTO InternalError}; RuntimeError.NarrowFault => {errorMsg ¬ "NarrowFault"; GOTO InternalError}; RuntimeError.NilFault => {errorMsg ¬ "NilFault"; GOTO InternalError}; RuntimeError.BoundsFault => {errorMsg ¬ "BoundsFault"; GOTO InternalError}; RuntimeError.Uncaught => {errorMsg ¬ "Uncaught error"; GOTO InternalError}; RuntimeError.UnnamedError => {errorMsg ¬ "UnnamedError"; GOTO InternalError}; RuntimeError.UnnamedSignal => {errorMsg ¬ "UnnamedSignal"; GOTO InternalError}; UNWIND => {errorMsg ¬ "internal error"; GOTO InternalError}; END; buttonData: ButtonData ¬ NARROW[instanceData]; ProduceStandardFeedback[event, buttonInfo]; IF instanceData = NIL THEN GOTO NoButton ELSE { success: BOOL ¬ PushButtonHandler[buttonInfo, buttonData, event]; IF NOT success THEN GOTO NoButton; }; EXITS NoButton => { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, "A button was pressed that has no button handler"]; }; InternalError => { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, Rope.Concat["Pressing that GuardedButton caused ", errorMsg]]; }; END; }; BuildButtonHandler: PROC [clientData: REF, handleProc: HandleButtonProc, entry: ButtonLineEntry, confirmProc: HandleButtonProc, clientPackageName: Rope.ROPE] RETURNS [instance: REF] = { WITH entry SELECT FROM b: ButtonLineEntry.button => instance ¬ BuildButtonHandlerAux[clientData, handleProc, b.name, b.events, b.font, confirmProc, b.initProc, clientPackageName]; ENDCASE => ERROR; }; ButtonLineEntry: TYPE = AtomButtons.ButtonLineEntry; Event: TYPE = AtomButtons.Event; InitButtonProc: TYPE = AtomButtonsTypes.InitButtonProc; ButtonData: TYPE = REF ButtonDataObj; ButtonDataObj: TYPE = RECORD [ clientData: REF, events: LIST OF Event, handleProc: HandleButtonProc, name: Rope.ROPE, font: Imager.Font, confirmProc: HandleButtonProc, initProc: InitButtonProc, clientPackageName: Rope.ROPE, inverted: BOOL ¬ FALSE, state: GuardState, guarded: BOOL ¬ FALSE, greyCount: INTEGER ¬ 0, buttonInfo: ButtonInfo ¬ NIL, viewer: ViewerClasses.Viewer ¬ NIL, displayStyle: DisplayStyle ¬ blackOnWhite ]; DisplayStyle: TYPE = {blackOnWhite, whiteOnBlack, blackOnGrey}; GuardState: TYPE = { guarded, arming, armed }; BuildButtonHandlerAux: PROC [clientData: REF, handleProc: HandleButtonProc, name: Rope.ROPE, events: LIST OF Event, font: Imager.Font, confirmProc: HandleButtonProc, initProc: InitButtonProc, clientPackageName: Rope.ROPE] RETURNS [instanceData: ButtonData] = { choiceList, ptr: LIST OF PopUpButtons.Choice; image: PopUpButtons.Image; fakeViewer: ViewerClasses.Viewer; fakeViewer ¬ NEW[ViewerClasses.ViewerRec]; instanceData ¬ NEW[ButtonDataObj ¬ [ clientData: clientData, handleProc: handleProc, name: name, events: events, font: font, confirmProc: confirmProc, guarded: confirmProc # NIL, initProc: initProc, clientPackageName: clientPackageName, state: IF confirmProc = NIL THEN armed ELSE guarded, buttonInfo: NIL, -- for now viewer: fakeViewer ]]; fakeViewer.data ¬ instanceData; }; PushButtonHandler: PROC [buttonInfo: ButtonInfo, instanceData: ButtonData, event: EBEvent.Event] RETURNS [success: BOOL ¬ TRUE] = { userEvent, buttonEvent: LIST OF REF; tipParseInfo.inCreek ¬ event.handle; userEvent ¬ TIPPrivate.WideMatchEvent[tipParseInfo, event.action­]; IF instanceData.handleProc # NIL THEN { ButtonHandlerEvent[buttonInfo, instanceData, userEvent]; }; }; ButtonNotify: ViewerClasses.NotifyProc = { data: ButtonData ¬ NARROW[self.data]; ButtonHandlerEvent[data.buttonInfo, data, input]; }; HandleButtonProc: TYPE = PROC [button: ButtonInfo, clientData: REF, event: LIST OF REF]; ButtonHandlerEvent: ENTRY PROC [self: ButtonInfo, data: ButtonData, input: LIST OF REF] = { ENABLE BEGIN UNWIND => InputFocus.ReleaseButtons[]; RuntimeError.Uncaught => GOTO Uncaught; RuntimeError.NarrowRefFault => GOTO NarrowRefFault; END; button: ViewerClasses.MouseButton ¬ red; shift, control: BOOL ¬ FALSE; mouse: TIPScreenCoords; IF data = NIL THEN RETURN; data.buttonInfo ¬ self; FOR list: LIST OF REF ¬ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => SELECT x FROM $Blue => button ¬ blue; $Control => control ¬ TRUE; $Up => IF data.inverted THEN SELECT data.state FROM guarded => { data.state¬arming; EmbeddedButtons.FeedbackNotify[LIST[$Unhighlight], self]; data.inverted ¬ FALSE; InputFocus.ReleaseButtons[]; TRUSTED {Process.Detach[FORK ArmButtonProc[data, self]]}; IF data.confirmProc # NIL THEN data.confirmProc[self, data.clientData, input]; }; arming=> NULL; -- no action armed=> { InputFocus.ReleaseButtons[]; EmbeddedButtons.FeedbackNotify[LIST[$Unhighlight], self]; data.inverted ¬ FALSE; IF data.guarded THEN { data.state ¬ guarded; EmbeddedButtons.FeedbackNotify[LIST[$Guard, $On], self]; }; ButtonPusher[self, data, data.handleProc, data.clientData, input, FALSE]; }; ENDCASE; $Down => { IF ~data.inverted THEN { InputFocus.CaptureButtons[ButtonNotify, tipParseInfo.tableHead, data.viewer]; EmbeddedButtons.FeedbackNotify[LIST[$Highlight], self]; data.inverted ¬ TRUE; } ELSE { }; }; $Red => button ¬ red; $Shift => shift ¬ TRUE; $Yellow => button ¬ yellow; ENDCASE => NULL; z: TIPScreenCoords => mouse ¬ z; ENDCASE => ERROR; ENDLOOP; EXITS Uncaught => { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, "ButtonHandlerEvent received an uncaught error."]; }; NarrowRefFault => { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, "ButtonHandlerEvent received a NarrowRefFault."]; }; }; armingTime: Process.Milliseconds ¬ 100; -- cover removal time. armedTime: Process.Milliseconds ¬ 5000; -- unguarded interval. ArmButtonProc: ENTRY PROC [data: ButtonData, button: ButtonInfo] = { IF data = NIL THEN RETURN; ButtonWait[data, armingTime]; IF data.state = arming THEN { data.state ¬ armed; EmbeddedButtons.FeedbackNotify[LIST[$Guard, $Off], button]; ButtonWait[data, armedTime]; }; IF data.state#guarded THEN { data.state ¬ guarded; EmbeddedButtons.FeedbackNotify[LIST[$Guard, $On], button]; }; }; ButtonWait: INTERNAL PROC[data: ButtonData, msec: Process.Milliseconds] = TRUSTED { buttonWaitCondition: CONDITION; Process.SetTimeout[LONG[@buttonWaitCondition], Process.MsecToTicks[msec]]; WAIT buttonWaitCondition; }; DoPush: PROC [button: ButtonInfo, instanceData: ButtonData, userEvent: LIST OF REF] = { buttonEvent: LIST OF REF; buttonEvent ¬ IF ButtonClassesCommon.CountMatches[LIST[$Red, $Up], userEvent] = 2 THEN instanceData.events.first ELSE IF ButtonClassesCommon.CountMatches[LIST[$Yellow, $Up], userEvent] = 2 THEN IF instanceData.events.rest = NIL THEN instanceData.events.first ELSE instanceData.events.rest.first ELSE IF ButtonClassesCommon.CountMatches[LIST[$Blue, $Up], userEvent] = 2 THEN IF instanceData.events.rest = NIL OR instanceData.events.rest.rest = NIL THEN instanceData.events.first ELSE instanceData.events.rest.rest.first ELSE NIL; IF instanceData.handleProc # NIL AND buttonEvent # NIL THEN instanceData.handleProc[button, instanceData.clientData, buttonEvent]; }; ButtonPusher: PROC [button: ButtonInfo, myData: ButtonData, proc: HandleButtonProc, clientData: REF, userEvent: LIST OF REF, normalPriority: BOOL] = { IF myData = NIL THEN RETURN; myData.greyCount ¬ myData.greyCount + 1; IF myData.displayStyle#blackOnGrey THEN EmbeddedButtons.FeedbackNotify[LIST[$Pressed], button]; IF normalPriority THEN CedarProcess.SetPriority[normal]; DoPush[button, myData, userEvent ! ABORTED => CONTINUE]; myData.greyCount ¬ MAX[myData.greyCount - 1, 0]; IF myData.displayStyle#blackOnGrey THEN EmbeddedButtons.FeedbackNotify[LIST[$Done], button]; }; popUpClass: EBButtonClasses.ButtonClass ¬ NEW[EBButtonClasses.ButtonClassObj ¬ [ name: $PopUpButton, instantiate: PopUpInstantiate, unparseInstanceData: NIL, handleEvent: PopUpHandle, getValue: NIL, setValue: NIL, defaultBehavior: NIL]]; PopUpInstantiate: EBButtonClasses.InstantiateProc = { menuName: ROPE; choices: AtomButtons.PopUpChoices; quickClickEnabled: BOOL ¬ TRUE; CodeTimer.StartInt[$PopUpInstantiate, $EmbeddedButtons]; CodeTimer.StartInt[$PopUpInstantiateParse, $EmbeddedButtons]; SELECT language FROM $Poppy => { menuRope: ROPE ¬ EmbeddedButtons.GetFieldRope[$Menu, buttonInfo]; ropeStream: IO.STREAM ¬ IO.RIS[menuRope]; object: REF; quickClickRope: ROPE ¬ EmbeddedButtons.GetFieldRope[$QuickClickEnabled, buttonInfo]; quickClickEnabled ¬ IF quickClickRope = NIL THEN TRUE ELSE Convert.BoolFromRope[quickClickRope ! Convert.Error => {quickClickEnabled ¬ TRUE; CONTINUE}]; object ¬ EBMesaLisp.Parse[ropeStream].val; IF object = NIL THEN { menuName ¬ "DummyName"; choices ¬ LIST[[action: LIST[$Dummy], actionImage: "No Action", doc: "Embedded Buttons couldn't parse the Menu entry for this button"]]; } ELSE [menuName, choices] ¬ PopUpParseMenu[object, menuRope]; }; ENDCASE => { CodeTimer.StopInt[$PopUpInstantiateParse, $EmbeddedButtons]; CodeTimer.StopInt[$PopUpInstantiate, $EmbeddedButtons]; RETURN[NIL]; }; CodeTimer.StopInt[$PopUpInstantiateParse, $EmbeddedButtons]; BEGIN entry: AtomButtons.ButtonLineEntry ¬ [popUpButton[ name: menuName, choices: choices, help: PopUpButtons.noHelp, -- providing this field saves lots of compute time in PCedar font: bigFont, disableDecoding: NOT quickClickEnabled ]]; instance ¬ AtomButtons.BuildPopUp[ clientData: NIL, handleProc: PopUpPassEventToApplication, paint: PopUpPaint, inButton: PopUpInButton, entry: entry, clientPackageName: "ButtonClasses"]; END; CodeTimer.StopInt[$PopUpInstantiate, $EmbeddedButtons]; RETURN[instance]; }; PopUpParseMenu: PROC[obj: REF, instanceData: ROPE] RETURNS [menuName: ROPE ¬ "", choices: AtomButtons.PopUpChoices ¬ NIL] = { o: LIST OF REF; success: BOOL ¬ TRUE; CodeTimer.StartInt[$PopUpParseMenu, $EmbeddedButtons]; [o, menuName, success] ¬ GetMenuListAndName[obj, instanceData]; IF NOT success THEN RETURN; FOR list: LIST OF REF ¬ o, list.rest UNTIL list = NIL DO -- for each entry... entry: LIST OF REF; event: LIST OF REF; menuEntry: ROPE; docString: ROPE; choice: AtomButtons.PopUpChoice; BEGIN IF NOT ISTYPE[list.first, LIST OF REF] THEN GOTO FieldTypeError; entry ¬ NARROW[list.first]; IF entry # NIL THEN { -- get the application message, entry name, and doc string event ¬ GetMessageAsList[entry]; [menuEntry, success] ¬ GetMenuEntry[entry]; IF NOT success THEN GOTO FieldTypeError; [docString, success] ¬ GetDocString[entry]; IF NOT success THEN GOTO FieldTypeError; } ELSE {event ¬ NIL; menuEntry ¬ ""; docString ¬ ""}; choice ¬ [action: event, actionImage: menuEntry, doc: docString, font: bigFont]; choices ¬ PopUpAppend[choices, LIST[choice]]; EXITS FieldTypeError => { EmbeddedButtons.Error["Pop-up buttons: Invalid field in menu!", Rope.Concat["The menu description for this pop-up button contains an invalid field:\n", instanceData]]; }; END; ENDLOOP; CodeTimer.StopInt[$PopUpParseMenu, $EmbeddedButtons]; }; PopUpPassEventToApplication: AtomButtons.HandlePopUpProc = { message: REF; WITH button SELECT FROM buttonInfo: ButtonInfo => { EBLanguage.SetSystemValue[clickContext, $MessageReceiver, EmbeddedButtons.GetApplications[buttonInfo]]; message ¬ EBLanguage.Evaluate[event, buttonInfo, clientData, clickContext]; IF message # NIL THEN PassEvent[message, buttonInfo]; }; ENDCASE => { EmbeddedButtons.Error["Pop-up buttons: button is wrong type (PopUpPassEventToApplication)"]; }; }; PopUpHandle: EBButtonClasses.HandleEventProc = { ENABLE UNWIND => {GOTO InternalError}; CodeTimer.StartInt[$PopUpHandle, $EmbeddedButtons]; ProduceStandardFeedback[event, buttonInfo]; IF instanceData = NIL THEN GOTO NoPop ELSE { maybeInstance: PopUpButtons.MaybeInstance ¬ PopUpButtons.QuaInstance[instanceData]; IF NOT maybeInstance.is THEN GOTO NoPop ELSE PopUpButtons.RawNotify[buttonInfo, maybeInstance.inst, event.handle, event.action]; }; CodeTimer.StopInt[$PopUpHandle, $EmbeddedButtons]; EXITS NoPop => { EmbeddedButtons.Error["This PopUpButton is broken (syntax problem?)"]; CodeTimer.StopInt[$PopUpHandle, $EmbeddedButtons]; }; InternalError => { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $Error, "Pressing that PopUpButton caused an internal error."]; CodeTimer.StopInt[$PopUpHandle, $EmbeddedButtons]; }; }; clickContext: Context ¬ EBLanguage.CreateContext[]; PopUpPaint: PopUpButtons.PaintProc = { }; PopUpInButton: PopUpButtons.InTestProc = { buttonInfo: ButtonInfo; IF NOT ISTYPE[view, ButtonInfo] THEN { EmbeddedButtons.Error["Pop-up buttons: Internal Error, button is wrong type (PopUpInButton)"]; RETURN[TRUE]; }; buttonInfo ¬ NARROW[view]; RETURN[EmbeddedButtons.InButton[coords.mouseX, coords.mouseY, buttonInfo]]; }; PopUpAppend: PROC [l1: AtomButtons.PopUpChoices, l2: AtomButtons.PopUpChoices ¬ NIL] RETURNS[val: AtomButtons.PopUpChoices] = { z: AtomButtons.PopUpChoices ¬ NIL; val ¬ l2; IF l1 = NIL THEN RETURN[val]; val ¬ CONS[l1.first, val]; z ¬ val; UNTIL (l1 ¬ l1.rest) = NIL DO z.rest ¬ CONS[l1.first, z.rest]; z ¬ z.rest; ENDLOOP; RETURN[val]; }; multiClass: EBButtonClasses.ButtonClass ¬ NEW[EBButtonClasses.ButtonClassObj ¬ [ name: $MultiStateButton, instantiate: MultiInstantiate, unparseInstanceData: MultiUnparse, handleEvent: MultiHandle, getValue: MultiGet, setValue: MultiSet, defaultBehavior: MultiDefaultBehavior]]; Multi: TYPE = REF MultiRec; MultiRec: TYPE = RECORD[ nameExists: BOOL ¬ FALSE, name: ATOM, value: Variable, message: REF, mouseInside: BOOL, defaultDirection: BOOL -- direction to change state by default FALSE => up, TRUE => down ]; MultiInstantiate: EBButtonClasses.InstantiateProc = { self: Multi; CodeTimer.StartInt[$MultiInstantiate, $EmbeddedButtons]; self ¬ NEW[MultiRec ¬ [ name: NIL, nameExists: FALSE, value: NEW[VariableObj ¬ [ type: NEW[VariableTypeObj[3]], value: NEW[INT ¬ 1]]], message: NIL, mouseInside: FALSE, defaultDirection: FALSE -- down ]]; self.value.type.class ¬ enumerated; FOR i: INT IN [1..3] DO self.value.type.enumeration[i-1] ¬ NEW[INT ¬ i]; ENDLOOP; MultiInstantiateAux[buttonInfo, language, languageVersion, self, initFeedback]; CodeTimer.StopInt[$MultiInstantiate, $EmbeddedButtons]; RETURN[self]; }; MultiInstantiateAux: PROC [buttonInfo: ButtonInfo, language: ATOM, languageVersion: NAT, self: Multi, initFeedback: BOOL ¬ FALSE] = { fieldError, fieldValue: ROPE; val: REF; success: BOOL ¬ FALSE; varTable: REF; found: BOOL ¬ FALSE; self.name ¬ EmbeddedButtons.GetButtonName[buttonInfo]; self.nameExists ¬ self.name # NIL; varTable ¬ EmbeddedButtons.GetFieldRef[$Variables, buttonInfo]; IF varTable # NIL THEN { v: Variable ¬ EBLanguage.GetVariable[NARROW[varTable, VariableTable], $Value]; IF v # NIL THEN { SELECT v.type.class FROM boolean => { self.value.type ¬ booleanEnumeratedType; self.value.value ¬ v.value; }; integer => { self.value.type ¬ v.type; self.value.value ¬ v.value; }; real => { self.value.type ¬ v.type; self.value.value ¬ v.value; }; enumerated => self.value ¬ v; ENDCASE => EmbeddedButtons.Error["Multi-state buttons: Invalid Value variable. Must be a boolean, integer, real or enumerated type."]; }; }; IF self.value.value = NIL AND self.name # NIL THEN { -- get value from like-named button? val ¬ EmbeddedButtons.GetValue[self.name, buttonInfo.doc]; IF val # NIL THEN { fieldError ¬ EBMesaLisp.Unparse[val]; IF self.value.type.class = integer THEN found ¬ ISTYPE[val, REF INT] ELSE IF self.value.type.class = real THEN found ¬ ISTYPE[val, REF REAL] ELSE { -- Search through the values list. found ¬ FALSE; FOR i: NAT IN [1 .. self.value.type.count) DO IF EBLanguage.Equal[val, self.value.type.enumeration[i]] THEN {self.value.value ¬ val; found ¬ TRUE}; ENDLOOP; }; IF NOT found THEN EmbeddedButtons.Error[IO.PutFR["Two multi-state buttons of name %g have incompatible values, including %g", [rope[Atom.GetPName[self.name]]], [rope[fieldError]] ]]; }; }; IF self.value.value = NIL -- button still has no value. Give it a default THEN self.value.value ¬ self.value.type.enumeration[0]; fieldValue ¬ EmbeddedButtons.GetFieldRope[$UpClickMessage, buttonInfo]; self.message ¬ EBMesaLisp.Parse[IO.RIS[fieldValue]].val; IF initFeedback THEN { EmbeddedButtons.FeedbackNotify[ feedbackEvent: LIST[$Value, self.value.value], buttonInfo: buttonInfo]; }; }; MultiUnparse: EBButtonClasses.UnparseInstanceDataProc = { self: Multi; rope: ROPE; ppListPoppy: LIST OF REF; IF instanceData = NIL THEN RETURN[""]; IF NOT ISTYPE[instanceData, Multi] THEN { EmbeddedButtons.Error["Multiple-state buttons: Internal Error, button is wrong type (MultiUnparse)"]; RETURN[""]; }; self ¬ NARROW[instanceData]; ppListPoppy ¬ LIST[ "", refFALSE, "\n ", refFALSE]; IF self.name # NIL AND NOT ButtonClassesCommon.IsGeneratedRope[Atom.GetPName[self.name]] THEN EmbeddedButtons.SetFieldRope[$Name, EBMesaLisp.Unparse[self.name], buttonInfo]; SELECT language FROM $Poppy => { tableRef: REF; table: VariableTable; type: VariableType; tableRef ¬ EmbeddedButtons.GetFieldRef[$Variables, buttonInfo]; IF ISTYPE[tableRef, VariableTable] AND tableRef # NIL THEN table ¬ NARROW[tableRef] ELSE { table ¬ EBLanguage.CreateVariableTable[]; EmbeddedButtons.SetFieldRef[$Variables, table, buttonInfo]; }; IF self.value.type = booleanEnumeratedType THEN { type ¬ NEW[VariableTypeObj[0]]; type.class ¬ boolean; EBLanguage.SetVariable[table, $Value, NEW[VariableObj ¬ [ value: self.value.value, type: type]]]; } ELSE EBLanguage.SetVariable[table, $Value, self.value]; }; ENDCASE; SELECT language FROM $Poppy => IF self.message # NIL THEN EmbeddedButtons.SetFieldRope[$UpClickMessage, EBMesaLisp.Unparse[self.message, ppListPoppy], buttonInfo]; ENDCASE; SELECT language FROM $Poppy => rope ¬ NIL; -- A button class doesn't know enough to unparse any further. Embedded Buttons will. ENDCASE; RETURN[rope]; }; MultiHandle: EBButtonClasses.HandleEventProc = { userEvent: LIST OF REF; ProduceStandardFeedback[event, buttonInfo]; tipParseInfo.inCreek ¬ event.handle; userEvent ¬ TIPPrivate.WideMatchEvent[tipParseInfo, event.action­]; MultiEvent[instanceData, userEvent, buttonInfo]; }; gClientData: REF; MultiCaptureNotify: InputFocus.NotifyProc = { acd: ActiveClientData; IF NOT ISTYPE[gClientData, ActiveClientData] THEN { EmbeddedButtons.Error["Multiple-state buttons: Internal error. Client data is wrong type (MultiCaptureNotify)."]; RETURN; }; acd ¬ NARROW[gClientData]; MultiEvent[acd.instanceData, input, acd.buttonInfo]; }; MultiEvent: PROC [instanceData: REF, userEvent: LIST OF REF, buttonInfo: ButtonInfo] = { self: Multi; mousePos: TIPScreenCoords ¬ NIL; IF userEvent = NIL OR instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, Multi] THEN {EmbeddedButtons.Error["Multiple-state buttons: Internal error. Button is wrong type (MultiEvent)."]; RETURN}; self ¬ NARROW[instanceData]; IF ButtonClassesCommon.CountMatches[userEvent, LIST[$Up]] > 0 THEN { -- up event message: REF ¬ self.message; self.defaultDirection ¬ ButtonClassesCommon.CountMatches[userEvent, LIST[$Right]] > 0; IF self.mouseInside THEN {self.mouseInside ¬ FALSE; InputFocus.ReleaseButtons[]}; EBLanguage.SetSystemValue[clickContext, $MessageReceiver, EmbeddedButtons.GetApplications[buttonInfo]]; message ¬ EBLanguage.Evaluate[message, buttonInfo, NIL, clickContext]; IF ISTYPE[message, LIST OF REF] THEN EmbeddedButtons.PassEventToApplication[NARROW[message], buttonInfo] ELSE EmbeddedButtons.PassEventToApplication[LIST[message], buttonInfo]; } ELSE { -- not a button up event. Generate Enter or Exit actions IF NOT self.mouseInside THEN { -- Enter action self.mouseInside ¬ TRUE; EmbeddedButtons.FeedbackNotify[LIST[$Enter], buttonInfo]; RETURN; }; IF ButtonClassesCommon.CountMatches[userEvent, LIST[$MouseMoved]] = 0 THEN RETURN; -- can't deal with this event. Ignoring it. FOR l: LIST OF REF ¬ userEvent, l.rest UNTIL l = NIL DO IF ISTYPE[l.first, TIPScreenCoords] THEN mousePos ¬ NARROW[userEvent.rest.first]; ENDLOOP; IF mousePos = NIL THEN { EmbeddedButtons.Error["Multiple-state buttons: Internal error, NIL mouse position (MultiEvent)."]; RETURN; }; IF NOT EmbeddedButtons.InButton[mousePos.mouseX, mousePos.mouseY, buttonInfo] THEN { self.mouseInside ¬ FALSE; InputFocus.ReleaseButtons[]; EmbeddedButtons.FeedbackNotify[LIST[$Exit], buttonInfo]; }; }; }; MultiGet: EBButtonClasses.GetValueProc = { self: Multi; IF instanceData = NIL THEN RETURN[NIL]; IF NOT ISTYPE[instanceData, Multi] THEN { EmbeddedButtons.Error["Multiple-state buttons: Internal error. Button is wrong type (MultiGet)."]; RETURN[NIL]; }; self ¬ NARROW[instanceData]; value ¬ self.value.value; EmbeddedButtons.RegisterNameValuePair[self.name, value, buttonInfo]; }; MultiSet: EBButtonClasses.SetValueProc = { self: Multi; IF instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, Multi] THEN {EmbeddedButtons.Error["Multiple-state buttons: Internal error. Button is wrong type (MultiSet)."]; RETURN}; self ¬ NARROW[instanceData]; IF self.value.type.class = integer THEN { WITH value SELECT FROM i: REF INT => { self.value.value ¬ value; changed ¬ TRUE; EmbeddedButtons.RegisterNameValuePair[self.name, value, buttonInfo]; EmbeddedButtons.FeedbackNotify[LIST[$Value, value], buttonInfo]; }; ENDCASE => EmbeddedButtons.Error[IO.PutFR["Attempt to set button %g (type INT) to %g", [rope[Atom.GetPName[self.name]]], [rope[EBMesaLisp.Unparse[value]]] ]]; } ELSE IF self.value.type.class = real THEN { WITH value SELECT FROM r: REF REAL => { self.value.value ¬ value; changed ¬ TRUE; EmbeddedButtons.RegisterNameValuePair[self.name, value, buttonInfo]; EmbeddedButtons.FeedbackNotify[LIST[$Value, value], buttonInfo]; }; ENDCASE => EmbeddedButtons.Error[IO.PutFR["Attempt to set button %g (type REAL) to %g", [rope[Atom.GetPName[self.name]]], [rope[EBMesaLisp.Unparse[value]]] ]]; } ELSE { FOR i: NAT IN [0..self.value.type.count) DO IF EBLanguage.Equal[value, self.value.type.enumeration[i]] THEN { self.value.value ¬ value; changed ¬ TRUE; EmbeddedButtons.RegisterNameValuePair[self.name, value, buttonInfo]; EmbeddedButtons.FeedbackNotify[LIST[$Value, value], buttonInfo]; EXIT; }; ENDLOOP; }; }; MultiDefaultBehavior: EBButtonClasses.DefaultBehaviorProc = { self: Multi; state: INT ¬ 0; newValue: REF; IF instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, Multi] THEN { EmbeddedButtons.Error["Multiple-state buttons: Internal error. Button is wrong type (MultiDefaultNotify)."]; RETURN; }; self ¬ NARROW[instanceData]; IF self.value.type.class = integer THEN { oldValue: REF INT ¬ NARROW[self.value.value]; IF NOT self.defaultDirection THEN newValue ¬ NEW[INT ¬ oldValue­ +1] ELSE newValue ¬ NEW[INT ¬ oldValue­ -1]; } ELSE IF self.value.type.class = real THEN { oldValue: REF REAL ¬ NARROW[self.value.value]; IF NOT self.defaultDirection THEN newValue ¬ NEW[REAL ¬ oldValue­ *2.0] ELSE newValue ¬ NEW[REAL ¬ oldValue­ /2.0]; } ELSE { FOR i: NAT IN [0..self.value.type.count) DO IF EBLanguage.Equal[self.value.value, self.value.type.enumeration[i]] THEN { state ¬ i; EXIT }; ENDLOOP; IF NOT self.defaultDirection THEN { state ¬ state + 1; IF state >= self.value.type.count THEN state ¬ 0; -- Greater than equal for safety? } ELSE { state ¬ state - 1; IF state < 0 THEN state ¬ self.value.type.count-1; }; newValue ¬ self.value.type.enumeration[state]; }; IF self.nameExists THEN EmbeddedButtons.SetValue[self.name, newValue, buttonInfo.doc] ELSE { changed: BOOL ¬ MultiSet[instanceData, newValue, buttonInfo]; IF changed THEN EBButtonClasses.MarkButtonAsChanged[buttonInfo]; }; }; twoStateClass: EBButtonClasses.ButtonClass ¬ NEW[EBButtonClasses.ButtonClassObj ¬ [ name: $TwoStateButton, instantiate: TwoStateInstantiate, unparseInstanceData: MultiUnparse, handleEvent: MultiHandle, getValue: MultiGet, setValue: MultiSet, defaultBehavior: MultiDefaultBehavior]]; TwoStateInstantiate: EBButtonClasses.InstantiateProc = { self: Multi ¬ NEW[MultiRec ¬ [ name: NIL, value: NEW[VariableObj ¬ [ type: booleanEnumeratedType, value: refFALSE]], message: NIL, mouseInside: FALSE, defaultDirection: FALSE -- up ]]; MultiInstantiateAux[buttonInfo, language, languageVersion, self, initFeedback]; RETURN[self]; }; radioClass: EBButtonClasses.ButtonClass ¬ NEW[EBButtonClasses.ButtonClassObj ¬ [ name: $RadioButton, instantiate: RadioInstantiate, unparseInstanceData: RadioUnparse, handleEvent: RadioHandle, getValue: RadioGet, setValue: RadioSet, defaultBehavior: RadioDefaultBehavior]]; Radio: TYPE = REF RadioRec; RadioRec: TYPE = RECORD[ nameExists: BOOL ¬ FALSE, name: ATOM, value: REF, on: BOOL, mouseInside: BOOL, message: LIST OF REF]; RadioInstantiate: EBButtonClasses.InstantiateProc = { self: Radio; fieldError, fieldValue: ROPE; success: BOOL; val, varTable: REF; CodeTimer.StartInt[$RadioInstantiate, $EmbeddedButtons]; self ¬ NEW[RadioRec ¬ [nameExists: FALSE, name: NIL, value: NIL, on: FALSE, mouseInside: FALSE, message: NIL]]; self.name ¬ EmbeddedButtons.GetButtonName[buttonInfo]; self.nameExists ¬ self.name # NIL; IF self.name = NIL THEN { EmbeddedButtons.Error["Every radio button must have a Name: field."]; CodeTimer.StopInt[$PopUpInstantiate, $EmbeddedButtons]; RETURN[NIL]; }; fieldValue ¬ EmbeddedButtons.GetFieldRope[$RadioButtonValue, buttonInfo]; fieldError ¬ fieldValue; self.value ¬ EBMesaLisp.Parse[IO.RIS[fieldValue]].val; IF self.value = NIL THEN -- Create a value if none provided self.value ¬ ButtonClassesCommon.GenerateUniqueRope[]; varTable ¬ EmbeddedButtons.GetFieldRef[$Variables, buttonInfo]; success ¬ FALSE; IF varTable # NIL THEN { v: Variable ¬ EBLanguage.GetVariable[NARROW[varTable, VariableTable], $State]; IF v # NIL THEN { IF v.type.class = boolean THEN { success ¬ TRUE; self.on ¬ NARROW[v.value, REF BOOL]­; } ELSE EmbeddedButtons.Error["Radio buttons: Invalid State variable. Must be a boolean."]; }; }; IF NOT success AND self.name # NIL THEN { -- get State from another like-named button? val ¬ EmbeddedButtons.GetValue[self.name, buttonInfo.doc]; IF val # NIL AND EBLanguage.Equal[val, self.value] THEN self.on ¬ TRUE; }; fieldValue ¬ EmbeddedButtons.GetFieldRope[$UpClickMessage, buttonInfo]; fieldError ¬ fieldValue; val ¬ EBMesaLisp.Parse[IO.RIS[fieldValue]].val; IF val # NIL THEN { IF ISTYPE[val, LIST OF REF] THEN self.message ¬ NARROW[val] ELSE EmbeddedButtons.Error[IO.PutFR1["Radio button: invalid message field: %g", [rope[fieldError]] ]]; }; CodeTimer.StopInt[$PopUpInstantiate, $EmbeddedButtons]; RETURN[self]; }; RadioUnparse: EBButtonClasses.UnparseInstanceDataProc = { self: Radio; IF instanceData = NIL THEN RETURN[""]; IF NOT ISTYPE[instanceData, Radio] THEN { EmbeddedButtons.Error["Radio buttons: Internal Error, button is wrong type (RadioUnparse)"]; RETURN[""]; }; self ¬ NARROW[instanceData]; SELECT language FROM $Poppy => { IF NOT ISTYPE[self.value, ROPE] OR NOT ButtonClassesCommon.IsGeneratedRope[NARROW[self.value, ROPE]] THEN EmbeddedButtons.SetFieldRope[$RadioButtonValue, EBMesaLisp.Unparse[self.value], buttonInfo]; EmbeddedButtons.SetFieldRope[$Name, EBMesaLisp.Unparse[self.name], buttonInfo]; EmbeddedButtons.SetFieldRope[$UpClickMessage, EBMesaLisp.Unparse[self.message], buttonInfo]; BEGIN tableRef: REF; table: VariableTable; type: VariableType; tableRef ¬ EmbeddedButtons.GetFieldRef[$Variables, buttonInfo]; IF ISTYPE[tableRef, VariableTable] AND tableRef # NIL THEN table ¬ NARROW[tableRef] ELSE { table ¬ EBLanguage.CreateVariableTable[]; EmbeddedButtons.SetFieldRef[$Variables, table, buttonInfo]; }; type ¬ NEW[VariableTypeObj[0]]; type.class ¬ boolean; EBLanguage.SetVariable[table, $State, NEW[VariableObj ¬ [ type: type, value: IF self.on THEN refTRUE ELSE refFALSE ]]]; END; RETURN; }; ENDCASE; }; RadioHandle: EBButtonClasses.HandleEventProc = { userEvent: LIST OF REF; ProduceStandardFeedback[event, buttonInfo]; tipParseInfo.inCreek ¬ event.handle; userEvent ¬ TIPPrivate.WideMatchEvent[tipParseInfo, event.action­]; RadioEvent[instanceData, userEvent, buttonInfo]; }; RadioCaptureNotify: InputFocus.NotifyProc = { acd: ActiveClientData; IF NOT ISTYPE[gClientData, ActiveClientData] THEN { EmbeddedButtons.Error["Radio buttons: Internal error. Client data is wrong type (RadioCaptureNotify).", "Radio buttons: Internal error. Client data is wrong type (RadioCaptureNotify)."]; RETURN; }; acd ¬ NARROW[gClientData]; RadioEvent[acd.instanceData, input, acd.buttonInfo]; }; RadioEvent: PROC [instanceData: REF, userEvent: LIST OF REF, buttonInfo: ButtonInfo] = { self: Radio; mousePos: TIPScreenCoords ¬ NIL; message: REF; IF userEvent = NIL OR instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, Radio] THEN { EmbeddedButtons.Error["Radio buttons: Internal error. Button is incorrect type (RadioHandle)."]; RETURN; }; self ¬ NARROW[instanceData]; IF ButtonClassesCommon.CountMatches[userEvent, LIST[$Up]] > 0 THEN { IF self.mouseInside THEN { self.mouseInside ¬ FALSE; InputFocus.ReleaseButtons[]; }; EBLanguage.SetSystemValue[clickContext, $MessageReceiver, EmbeddedButtons.GetApplications[buttonInfo]]; message ¬ EBLanguage.Evaluate[self.message, buttonInfo, NIL, clickContext]; IF ISTYPE[message, LIST OF REF] THEN EmbeddedButtons.PassEventToApplication[NARROW[message], buttonInfo] ELSE EmbeddedButtons.PassEventToApplication[LIST[message], buttonInfo]; RETURN; }; IF NOT self.mouseInside THEN { self.mouseInside ¬ TRUE; EmbeddedButtons.FeedbackNotify[LIST[$Enter], buttonInfo]; RETURN; }; IF ButtonClassesCommon.CountMatches[userEvent, LIST[$MouseMoved]] = 0 THEN RETURN; -- can't deal with this event. Ignoring it. FOR l: LIST OF REF ¬ userEvent, l.rest UNTIL l = NIL DO IF ISTYPE[l.first, TIPScreenCoords] THEN mousePos ¬ NARROW[userEvent.rest.first]; ENDLOOP; IF mousePos = NIL THEN { EmbeddedButtons.Error["Radio buttons: Internal error, TIP table probably mashed (RadioEvent)."]; RETURN; }; IF NOT EmbeddedButtons.InButton[mousePos.mouseX, mousePos.mouseY, buttonInfo] THEN { self.mouseInside ¬ FALSE; InputFocus.ReleaseButtons[]; EmbeddedButtons.FeedbackNotify[LIST[$Exit], buttonInfo]; }; }; RadioGet: EBButtonClasses.GetValueProc = { self: Radio; IF NOT ISTYPE[instanceData, Radio] THEN { EmbeddedButtons.Error["Radio buttons: Internal error. Button is incorrect type (RadioHandle)."]; RETURN[NIL]; }; self ¬ NARROW[instanceData]; IF instanceData = NIL THEN RETURN[NIL]; value ¬ self.value; IF self.on THEN { EmbeddedButtons.RegisterNameValuePair[self.name, value, buttonInfo]; RETURN[value]; }; RETURN[NIL]; }; RadioSet: EBButtonClasses.SetValueProc = { self: Radio; IF instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, Radio] THEN {EmbeddedButtons.Error["RadioSet: This radio button is not a radio button."]; RETURN}; self ¬ NARROW[instanceData]; IF EBLanguage.Equal[value, self.value] THEN { IF NOT self.on THEN { self.on ¬ TRUE; changed ¬ TRUE; IF self.nameExists THEN EmbeddedButtons.RegisterNameValuePair[self.name, self.value, buttonInfo]; EmbeddedButtons.FeedbackNotify[LIST[$State, refTRUE], buttonInfo]; } ELSE EmbeddedButtons.FeedbackNotify[LIST[$StateStill, refTRUE], buttonInfo]; } ELSE { -- someone else has been pressed. Turn off, (if you're not already off) IF self.on THEN { self.on ¬ FALSE; changed ¬ TRUE; EmbeddedButtons.FeedbackNotify[LIST[$State, refFALSE], buttonInfo]; }; }; }; RadioDefaultBehavior: EBButtonClasses.DefaultBehaviorProc = { self: Radio; IF instanceData = NIL THEN RETURN; IF NOT ISTYPE[instanceData, Radio] THEN { EmbeddedButtons.Error["Radio buttons: Internal error. Button is wrong type (RadioDefaultBehavior)."]; RETURN; }; self ¬ NARROW[instanceData]; EmbeddedButtons.SetValue[self.name, self.value, buttonInfo.doc]; }; CountMatches: PUBLIC PROC [a, b: LIST OF REF] RETURNS [result: INT ¬ 0] = { FOR c: LIST OF REF ¬ a, c.rest UNTIL c = NIL DO FOR d: LIST OF REF ¬ b, d.rest UNTIL d = NIL DO IF Match[c.first, d.first] THEN result ¬ result + 1; ENDLOOP; ENDLOOP; }; Match: PROC [a, b: REF] RETURNS [BOOL] = { IF a = b THEN RETURN[TRUE] ELSE IF EBLanguage.Equal[a, b] THEN RETURN[TRUE] ELSE IF a = $Red AND b = $Left THEN RETURN[TRUE] ELSE IF a = $Yellow AND b = $Middle THEN RETURN[TRUE] ELSE IF a = $Blue AND b = $Right THEN RETURN[TRUE] ELSE IF a = $Left AND b = $Red THEN RETURN[TRUE] ELSE IF a = $Middle AND b = $Yellow THEN RETURN[TRUE] ELSE IF a = $Right AND b = $Blue THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; ExactMatch: PUBLIC PROC [a, b: LIST OF REF] RETURNS [BOOL] = { blist: LIST OF REF ¬ b; FOR alist: LIST OF REF ¬ a, alist.rest UNTIL alist = NIL DO IF NOT Match[alist.first, blist.first] THEN RETURN[FALSE]; blist ¬ blist.rest; ENDLOOP; RETURN[TRUE]; }; Subset: PUBLIC PROC [a, b: LIST OF REF] RETURNS [BOOL] = { Included: PROC [a: REF, b: LIST OF REF] RETURNS [BOOL] = { FOR list: LIST OF REF ¬ b, list.rest UNTIL list = NIL DO IF Match[a, list.first] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]; }; FOR alist: LIST OF REF ¬ a, alist.rest UNTIL alist = NIL DO IF NOT Included[alist.first, b] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; KeyValueExtract: PUBLIC PROC [object: LIST OF REF, key: ATOM, screenError, fileError: ROPE] RETURNS [val: REF ¬ NIL, success: BOOL ¬ FALSE] = { FOR list: LIST OF REF ¬ object, list.rest UNTIL list = NIL DO first: LIST OF REF; keyword: ATOM; IF ~ISTYPE[list.first, LIST OF REF] OR list.first = NIL THEN { EmbeddedButtons.Error[screenError, fileError]; LOOP; }; first ¬ NARROW[list.first]; IF ~ISTYPE[first.first, ATOM] OR first.first = NIL THEN { EmbeddedButtons.Error[screenError, fileError]; LOOP; }; keyword ¬ NARROW[first.first]; IF keyword = key THEN { val ¬ first.rest.first; success ¬ TRUE; RETURN; }; ENDLOOP; }; ProduceStandardFeedback: PROC [event: EBEvent.Event, buttonInfo: ButtonInfo] = { userEvents: LIST OF REF; CodeTimer.StartInt[$ProduceStandardFeedback, $EmbeddedButtons]; TRUSTED { UserInputLookahead.SaveState[saved: standardFeedbackTIPParseInfo.inCreek, handle: event.handle]; }; userEvents ¬ TIPPrivate.WideMatchEvent[standardFeedbackTIPParseInfo, event.action­]; IF userEvents # NIL THEN { EmbeddedButtons.FeedbackNotify[userEvents, buttonInfo]; -- do the feedback }; UserInputOps.Close[standardFeedbackTIPParseInfo.inCreek]; -- so garbage collection can proceed CodeTimer.StopInt[$ProduceStandardFeedback, $EmbeddedButtons]; }; uniqueHeader: ROPE ¬ "EBButtonString"; GenerateUniqueRope: PUBLIC PROC [] RETURNS [rope: ROPE] = { rope ¬ uniqueHeader; rope ¬ Rope.Concat[rope, Convert.RopeFromCard[BasicTime.ToNSTime[BasicTime.Now[]]]]; rope ¬ Rope.Concat[rope, Convert.RopeFromCard[BasicTime.PulsesToMicroseconds[BasicTime.GetClockPulses[]]]]; }; IsGeneratedRope: PUBLIC PROC[rope: ROPE] RETURNS [BOOL] = { RETURN[Rope.Equal[uniqueHeader, Rope.Substr[rope, 0, Rope.Length[uniqueHeader]]]]; }; tipParseInfo: TIPPrivate.TIPParseInfo; standardFeedbackTIPParseInfo: TIPPrivate.TIPParseInfo; booleanEnumeratedType: VariableType ¬ NEW[VariableTypeObj[2]]; refFALSE: REF BOOL ¬ NEW[BOOL ¬ FALSE]; refTRUE: REF BOOL ¬ NEW[BOOL ¬ TRUE]; tipParseInfo ¬ TIPPrivate.CreateParseInfo[name: "ButtonClasses"]; tipParseInfo.tableHead ¬ TIPUser.InstantiateNewTIPTable["ButtonClasses.tip"]; standardFeedbackTIPParseInfo ¬ TIPPrivate.CreateParseInfo[name: "ButtonClassesFeedback"]; standardFeedbackTIPParseInfo.tableHead ¬ TIPUser.InstantiateNewTIPTable["ButtonClassesCommon.tip"]; booleanEnumeratedType.class ¬ enumerated; booleanEnumeratedType.enumeration[0] ¬ refFALSE; booleanEnumeratedType.enumeration[1] ¬ refTRUE; EBButtonClasses.RegisterButtonClass[$TwoStateButton, twoStateClass]; -- for backwards compat. EBButtonClasses.RegisterButtonClass[$MultiStateButton, multiClass]; EBButtonClasses.RegisterButtonClass[$PopUpButton, popUpClass]; EBButtonClasses.RegisterButtonClass[$RadioButton, radioClass]; EBButtonClasses.RegisterButtonClass[$PopUpStateButton, popUpStateClass]; -- experimental EBButtonClasses.RegisterButtonClass[$GuardedButton, buttonClass]; END. 2Ύ ButtonClassesImpl.mesa Copyright Σ 1989, 1990, 1992 by Xerox Corporation. All rights reserved. Contents: EmbeddedButtons button classes Last edited by Goodisman, August 18, 1989 1:57:51 pm PDT Kenneth A. Pier, August 27, 1990 4:18 pm PDT Bier, July 24, 1992 6:26 pm PDT Doug Wyatt, April 16, 1992 11:12 am PDT HandleRep: PUBLIC TYPE = UserInputPrivate.Rep; -- for UserInput Used for packing information away to be passed through other package's clientData fields. Pop-up State Button Class Behavior: Pop-up state buttons have both a pop-up menu and a state. The menu lists the possible states of the button. By selecting a given menu item (either by slow-clicking or fast-clicking; see the PopUp class below) the user moves the button to the new state. Feedback: Pop-up state buttons always succeed. Either they change state if a selection was made or they don't. The Feedback section of these buttons usually describes how to display each possible state value. Example: PopUpStateButton Pop-up state button class record definition PROC [buttonInfo: ButtonInfo, language: ATOM, languageVersion: NAT, initFeedback: BOOL _ FALSE] RETURNS [instance: REF]; Make sure that the object is a list, since a menu must be. Get the menu name if there is one. If o.first is NIL, we assume that it is a blank first entry rather than a blank menu name. Pull out the menu fields from the list, building data structures [button: REF, clientData: REF, event: LIST OF REF] If the event is one of ours, then update the click direction and extract the real application message before passing on the event. clientData is a PopUpStateData Evaluate any angle bracket "<>" expressions in "event" PROC [instanceData: REF, buttonInfo: ButtonInfo _ NIL, language: ATOM _ NIL, languageVersion: NAT _ 0] RETURNS [ROPE _ NIL]; If language is Poppy, unparses fields into symbol table of buttonInfo. Tries very hard not to unparse something if it has a default value so that a button specified with Name: Gravity will get unparsed with only Name: Gravity unless it is necessary to give more information. Get self. Set pretty print lists Unparse name Unparse value Unparse transitions Generate rope Produce any feedback associated with this mouse or keyboard action Let pop-up buttons handle the rest Will call PopUpStatePassEventToApplication with the menu selection PROC[instanceData: REF, buttonInfo: ButtonInfo] RETURNS [value: REF]; Get self name _ multi.name; PROC[instanceData: REF, value: REF, buttonInfo: ButtonInfo] RETURNS[changed: BOOL _ FALSE]; PROC[instanceData: REF, buttonInfo: ButtonInfo]; Guarded Button Class Behavior: Guarded buttons act much like the buttons implemented in Buttons.mesa. They perform a single action when clicked. If they are guarded, they must be clicked twice to trigger the action. The first click removes a guard, often displayed as a strike-through line. The second click causes the action to be performed. Feedback: Multi-state buttons generate the following events for feedback: The standard feedback events (Down, Up, Enter, Exit) and $Highlight -- when a mouse button goes down over the button $UnHighlight -- when a mouse button goes up over the button $Guard $Off -- when a mouse button goes up over the button for the first time $Guard $On -- when the guard returns to the button after 5 seconds $Pressed -- when a mouse button goes up over the button and it is unguarded $Done -- when the button has finished performing its action PROC [buttonInfo: ButtonInfo, language: ATOM, languageVersion: NAT, initFeedback: BOOL _ FALSE] RETURNS [instance: REF]; Instantiate and return the button. The input rope is a EBLanguage expression describing the behavior of the button. See ButtonClasses.form for the form of this expression. Build the button [button: REF, clientData: REF, event: LIST OF REF] [button: REF, clientData: REF, event: LIST OF REF] Evaluate any angle bracket "<>" expressions in "event" Let AtomButtons handle the rest Run the input action through TIP PROC [self: Viewer, input: LIST OF REF]; ViewerOps.PaintViewer[self, client, FALSE, $Invert]; ViewerOps.PaintViewer[self, client, FALSE, $Invert]; ViewerOps.PaintViewer[self, client]; IF data.fork THEN TRUSTED {Process.Detach[FORK ButtonPusher[self, data, data.handleProc, data.clientData, button, shift, control, TRUE]]} ELSE ViewerOps.PaintViewer[self, client, FALSE, $Invert]; c: BOOL; [v, c] _ ViewerOps.MouseInViewer[mouse]; IF v=self AND c THEN RETURN; ViewerOps.PaintViewer[self, client, FALSE, $Invert]; InputFocus.ReleaseButtons[]; assert: state=arming ViewerOps.PaintViewer[button, client]; ViewerOps.PaintViewer[button, client]; THEN ViewerOps.PaintViewer[button, client]; THEN ViewerOps.PaintViewer[button, client]; Pop-up Button Class Behavior: Pop-up buttons have no state. When a pop-up button is quick-clicked, it sends an application message corresponding to the mouse button that was clicked and any modifier keys that were pressed (Shift, Control, etc.). If a mouse button is clicked and held on a pop-up button, the button "pops up" a two dimensional menu of the button's functions (from which the user can choose) and displays a documentation string for the choice at which the cursor is pointing. Feedback: Pop-up buttons generate no special feedback. Like all buttons, the generate $Done, $Warning, or $Error to indicate the success of the button action (see EBImpl). Pop-up button class record definition PROC [buttonInfo: ButtonInfo, language: ATOM, languageVersion: NAT, initFeedback: BOOL _ FALSE] RETURNS [instance: REF]; Instantiate and return the button. The input rope is a EBLanguage expression describing the behavior of the pop-up button. See ButtonClasses.form for the form of this expression. Get the menu name and the menu Build the button Now go through the list and pull out the menu fields, building the data structures that PopUpButtons needs. [button: REF, clientData: REF, event: LIST OF REF] Evaluate any angle bracket "<>" expressions in "event" Let pop-up buttons handle the rest Will call PopUpPassEventToApplication with the menu selection PROC [view: View, coords: TIPScreenCoords] RETURNS [in: BOOL]; Get the button info Ask EmbeddedButtons if this position is in this button. Multi-State Buttons Behavior: When a multi-state button is clicked, it does not automatically change state. Instead, it sends the message corresponding to its current state to the application to which it is linked. It is the responsibility of that application to call EBApplications.SetKeyValuePair to cause the actual change, unless the button's MessageHandler includes Default, in which case EmbeddedButtons will cause the default behavior. The default behavior of a multi-state button is to change to the next state on a mouse up, wrapping around to the first state when reaching the last state. A multi-state button has a value associated with each of its states. (If no values are specified when the button is created, they default to the integers 1 through n, where n is the number of states.) A button's state is set by calling EBApplications.SetKeyValuePair with the value of the state to which the button is to change (not the number of the state, unless the two are the same). Feedback: Multi-state buttons generate the following events for feedback: The standard feedback events (Down, Up, etc.) and If the mouse enters the button with a mouse button down, the button generates $Enter Then the button generates one of: $Exit the mouse left the button without an up-click $Value value the button's value has changed to value. The $Value value events are generated when the actual change of state is made, and not when the request for state change is sent to the application. Multi-state button class record definition The internal representation of a multi-state button PROC [buttonInfo: ButtonInfo, language: ATOM, languageVersion: NAT, initFeedback: BOOL _ FALSE] RETURNS [instance: REF]; Set default values Transition message: Cause the feedback events corresponding to this button changing to its current state. PROC [instanceData: REF, buttonInfo: ButtonInfo _ NIL, language: ATOM _ NIL, languageVersion: NAT _ 0] RETURNS [ROPE _ NIL]; If language is Poppy, unparses fields into symbol table of buttonInfo. Tries very hard not to unparse something if it has a default value so that a button specified with Name: Gravity will get unparsed with only Name: Gravity unless it is necessary to give more information. Get self. Set pretty print lists Unparse name Unparse value Unparse transitions Generate rope Produce standard feedback Run the input action through TIP PROC [self: Viewer, input: LIST OF REF] Unpack the client data Get mouse position PROC[instanceData: REF, buttonInfo: ButtonInfo] RETURNS [value: REF]; Get self name _ self.name; PROC[instanceData: REF, value: REF, buttonInfo: ButtonInfo] RETURNS[changed: BOOL _ FALSE]; PROC[instanceData: REF, buttonInfo: ButtonInfo]; Two-state button class Behavior: Two-stae buttons are exactly like multi-state buttons, except that they have only two states and default values of FALSE and TRUE. In fact, two-state buttons are implemented with mostly multi-state buttons code. Feedback: Two-state buttons generate feedback like any other multi-state buttons. Two-state button class record definition PROC [buttonInfo: ButtonInfo, language: ATOM, languageVersion: NAT, initFeedback: BOOL _ FALSE] RETURNS [instance: REF]; Set default values Radio Button Class Behavior: Radio buttons are sets of buttons. In each set, only one button may be "on" at any given time. Clicking on a radio button turns that button on and all the other buttons in the set off. A set of radio buttons is defined as all radio buttons in a document which have the same Name. Each radio button has a value and the value of the radio button which is currently on is associated with the Name of the set of buttons. When a radio button is clicked, it sends its request message to the application to which it is linked. It is the responsibility of the application to call EBApplications.SetNameValuePair with the new value for the radio button set, unless the radio buttons have their default behavior. Radio buttons look at a boolean variable (in the Poppy variables field) called "State." This variable specifies whether a particular Radio button is on or not. Feedback: Radio buttons generate the following events for feedback: If the mouse enters the button with a mouse button down, the button generates: $Enter Then the button generates one of: $Exit -- the mouse left the button without an up-click ($State TRUE) -- change to the "on" appearance ($State FALSE) -- change to the "off" appearance Radio button class record definition The internal representation of a radio button PROC [buttonInfo: ButtonInfo, language: ATOM, languageVersion: NAT, initFeedback: BOOL _ FALSE] RETURNS [instance: REF]; In Poppy, then the following fields are used: Name: name -- (an ATOM) RadioButtonValue: value Variables: State -- (FALSE, TRUE, Off, or On) Message: message PROC [instanceData: REF, buttonInfo: ButtonInfo _ NIL, language: ATOM _ NIL, languageVersion: NAT _ 0] RETURNS [ROPE _ NIL]; Get self. Unparse each field. Produce standard feedback Run the input action through TIP PROC [self: Viewer, clientData: REF, input: LIST OF REF] Extract client data. On an upclick, send an application message. Release mouse keys if necessary Evaluate message and pass it to application. If the mouse was not previously in the button, then generate feedback and capture buttons If the mouse was previously inside the button, check if it still is and generate feedback if not, releasing buttons. Get mouse position PROC[instanceData: REF, buttonInfo: ButtonInfo] RETURNS [value: REF]; Get self name _ self.name; PROC[instanceData: REF, value: REF, buttonInfo: ButtonInfo] RETURNS[changed: BOOL _ FALSE]; If this button has been pressed, turn it on (if not already on) PROC[instanceData: REF, buttonInfo: ButtonInfo]; Get self Routines Shared By Several Button Classes Returns the number of elements of the first list that also occur in the second list with the following provisos: $Red matches $Left, $Yellow matches $Middle, $Blue matches $Right. Returns TRUE iff a and b contain the same REFS in the same order. The atom $Mouse matches any of $Red, $Yellow, $Blue, $Left, $Middle, or $Right. The atom $Highlight matches ($Mouse $Down) or $Enter, where $Mouse matches all the patterns just mentioned. The atom $UnHighlight matches ($Mouse $Up) or $Exit. Returns TRUE iff all REFs of a (or their equivalents, see ExactMatch) are also in b. Runs a copy of the action and queue through a standard tip table and produces standard feedback events such as Down, Up, etc. Copy queue into tipParseInfo since TIPPrivate.WideMatchEvent may eat actions off the queue. Load-time Processing Create the TIPParseInfo for event decoding Register the button classes. ΚG―•NewlineDelimiter –(cedarcode) style™šœ™Jšœ Οeœ=™HJšΟnœ ™(Jšœ8™8J™,Icode™K™'—K˜šΟk ˜ KšœΗŸœΈ˜—K˜šžœŸœŸœ˜!KšŸœ€Ÿœr˜ŸKšŸœ ˜'—šŸ˜K˜Kšœ Ÿœ˜&Kšœ Ÿœ˜ Kšœ ŸœŸœΟc˜8Kšœ ŸœŸœ ™?KšŸœŸœŸœ˜Kšœ Ÿœ˜%Kšœ Ÿœ˜+KšœŸœ˜-KšœŸœ˜3KšœŸœ˜/KšœŸœ%˜:K˜—KšœŸœŸœ˜1šœŸœŸœ˜#K˜KšœŸœ˜K™Y—headšΟx™L™ Ibody™ώL™ M™ΘM• CharPropsκ Postfix-1.0 outlineBoxBearoff 1.0 outlineBoxThickness ButtonData#Poppy1 Class: PopUpStateButton Variables: (Value: {orange, yellow, pink} = pink) Feedback: ( (MouseMoved ) ) MessageHandler: MessageWindowšœ Οb™—J™J™+šœ/Ÿœ#˜UK˜Kšœ#˜#Kšœ'˜'K˜Kšœ˜Kšœ˜Kšœ-˜-—K˜KšœŸœŸœ˜-šœŸœŸœ˜!KšœŸœ˜ K˜ Kšœ ŸœŸ˜Kšœ˜K˜—K˜Ršžœ%˜:KšŸœ$ŸœŸœŸœŸœŸœ Ÿœ™xKšœ Ÿ˜Kšœ"˜"Kšœ˜KšœŸœŸœ˜K˜KšŸœŸœŸœŸœ˜&KšœŸœ˜šœ Ÿœ ˜KšœŸœ˜ Kšœ Ÿœ˜šœŸœ˜KšœŸœ˜KšœŸœŸœ˜—Kšœ Ÿœ˜ Kšœ Ÿœ˜KšœŸœ ˜ K˜K˜—K˜)šŸœŸœŸœŸ˜Kšœ)ŸœŸœ˜6KšŸœ˜—KšœU˜UK˜šŸ˜Kšœ Ÿœ3˜AšŸœ ŸœŸœ˜Kšœ ’œ ˜)K˜K˜—šŸœ˜Kš œ ŸœŸœŸœŸœ ˜)KšœŸœ˜ KšœŸœ@˜TšœŸœŸœŸœŸ˜5KšŸœMŸœŸœ˜b—K˜*šŸœ ŸœŸœ˜K˜Kšœ Ÿœ Ÿœl˜ˆK˜—KšŸœ’œ˜AK˜—KšŸœ˜—K˜šŸ˜˜2Kšœ˜Kšœ˜Kšœ <˜WKšœ˜KšœŸœ˜&Kšœ˜K˜—˜$Kšœ˜Kšœ ž œ˜-Kšœ˜Kšœ˜K˜ K˜$—KšŸœ˜—KšŸœ˜ K˜K˜—KšœŸœŸœ˜9šœŸœŸœ˜(KšœŸœ˜ Kšœ ŸœŸœŸœ˜KšœŸ˜ K˜K˜—šžœŸœŸœŸœŸœŸœŸœŸœ ŸœŸœ ŸœŸœ˜€J™:šŸœŸœŸœŸœŸœŸœŸœ˜&KšœŽ˜ŽKšŸœŸœŸœŸœ˜K˜—KšœŸœ˜K˜J™~š Ÿœ ŸœŸœŸœ ŸœŸœ˜1Kšœ Ÿœ ˜K˜ K˜—K˜K˜—šžœŸœ ŸœŸœŸœŸœ ŸœŸœŸœ˜LKšŸœŸœŸœ Ÿ˜%šŸœ˜šŸœ ŸœŸ˜KšœŸœŸœŸœ˜ KšŸœ Ÿœ˜%—K˜—K˜K˜—šž œŸœ ŸœŸœŸœŸœ Ÿœ ŸœŸœ˜[Kš ŸœŸœŸœŸœŸœŸœ˜,KšŸœŸœŸœŸœŸœŸœŸœŸœ˜>Kšœ Ÿœ˜%K˜K˜—šž œŸœ ŸœŸœŸœŸœ ŸœŸœ ŸœŸœ˜aKšŸœŸœŸœ˜,šŸœ˜KšŸœŸœŸœŸœŸœŸœŸœŸœ˜CKšœ Ÿœ˜*K˜—K˜K˜—šžœŸœŸœŸœŸœ ŸœŸœ&Ÿœ˜„KšœŸœŸœŸœ˜Kšœ ŸœŸœ˜K˜ KšœŸœ˜ K˜K˜?KšŸœŸœ ŸœŸœ˜K˜J™@šŸœŸœŸœŸœŸœŸœŸœ ˜MKšœŸœŸœŸœ˜KšœŸœŸœŸœ˜Kšœ Ÿœ˜Kšœ Ÿœ˜K˜šŸ˜Kšœ˜KšŸœŸœŸœ ŸœŸœŸœŸœŸœ˜@KšœŸœ ˜K˜šŸœ ŸœŸœ :˜PKš’œ˜ Kšœ’ œ!˜+KšŸœŸœ ŸœŸœ˜(Kšœ’ œ!˜+KšŸœŸœ ŸœŸœ˜(K˜—KšŸœ ŸœŸœŸœ˜6K˜šœ Ÿœ˜(K˜ Kšœ ’˜KšœŸ˜ K˜—KšœŸœA˜WKšœŸœ ˜-šŸ˜˜Kšœ§˜§K˜——KšŸœ˜K˜ —KšŸœ˜—K˜K™—šžœŸœŸœ&Ÿœ˜]š ž œŸœ Ÿœ ŸœŸœ Ÿœ˜FK˜ Kšœ!˜!šœ Ÿœ˜,Kšœ ˜ Kšœ Ÿœ ˜KšœŸ˜ K˜—KšœŸœ;˜QKšœŸœ ˜-K˜—šŸœ˜"šœ˜šŸœŸœŸœŸ˜,KšœŸ˜K˜ Kšœ˜K˜šŸœŸœŸ˜$Kšœ Ÿœ˜$KšœŸœ$˜.KšŸœ˜"K˜Kšœ Ÿœ/˜=šœ Ÿœ˜(K˜ Kšœ Ÿœ˜*Kšœ˜K˜—KšœŸœA˜WKšœŸœ ˜-—KšŸœ˜—K˜—˜ Kšœ:˜:Kšœ:˜:Kšœ>˜>K˜—˜ Kšœ:˜:Kšœ:˜:Kšœ<˜K˜K˜—šž œ!˜AJš œ ŸœŸœ ŸœŸœŸœ™2Jšœ’™’šžœŸœ ŸœŸœŸœŸœ ŸœŸœŸœ˜NKšŸœ ŸœŸœŸœ˜"KšŸœŸœŸœŸœ˜(šŸœ ŸœŸ˜šœ˜KšœŸœ ˜*K˜K˜šŸœŸœŸœ˜KšŸœ ŸœŸœŸ˜AKšŸœŸœ˜)K˜—K˜—KšŸœ˜—K˜—šŸœŸœŸ˜šœ˜Kšœ Ÿœ˜ K˜Kšœg˜gšœ’œ.˜KJ™6—KšŸœ ŸœŸœ’ œ˜5K˜—šŸœ˜ Kšœa˜aKšœ˜——K˜K˜—šžœ-˜>KšŸœŸœŸœ ŸœŸœŸœŸœŸœŸœ™|K™K™“K™Kšœ˜KšœŸœ˜ Kšœ ŸœŸœŸœ˜K˜ K˜K™ KšŸœŸœŸœŸœ˜&šŸœŸœŸœŸœ˜2Kšœf˜fKšŸœ˜ K˜—KšœŸœ˜K˜K˜K™šœŸœ˜Kšœ ˜ Kšœ˜—K˜K™ šŸœŸœŸœŸœ?˜ZKšŸœQ˜U—K˜K™ šŸœ Ÿ˜˜ Kšœ Ÿœ˜K˜K˜K˜?š ŸœŸœŸœ ŸœŸœ˜;KšœŸœ ˜—šŸœ˜K˜)Kšœ;˜;K˜—šŸœ*Ÿœ˜2KšœŸœ˜K˜šœ&Ÿœ˜9K˜K˜—K˜—šŸ˜K˜3—K˜—KšŸœ˜—K˜K™šŸœ Ÿ˜˜ šŸœŸœŸ˜Kšœj˜j——KšŸœ˜—K™K™ šŸœ Ÿ˜KšœŸœ U˜kKšŸœ˜—K˜KšŸœ˜ K˜K˜—šžœ%˜5šŸœŸœ˜K˜qK˜—KšœŸœ˜,K™BK˜+K˜K™"Kš ŸœŸœŸœŸœŸœŸœ˜9šŸœ˜K˜QKšŸœŸœŸœŸœ˜(šœ ’ œ=˜SKšœB™B—K˜—šŸ˜KšœT˜T—K˜K˜—šž œ"˜/JšŸœŸœŸœ Ÿœ™EKšœ˜K˜ K˜Kšœ™Kš ŸœŸœŸœŸœŸœ˜'K˜šŸœŸœŸœŸœ˜2Kšœd˜dKšŸœŸœ˜ K˜—KšœŸœ˜K˜J™Jšœ™K˜K˜KšœE˜EK˜K˜—šž œ"˜/Kš ŸœŸœ ŸœŸœ ŸœŸœ™[Kšœ˜K˜ K˜KšŸœŸœŸœŸœ˜"Kš ŸœŸœŸœŸœgŸœ˜ŸKšœŸœ˜K˜J™šŸœ"Ÿœ˜*šŸœŸœŸ˜šœŸœŸœ˜K˜Kšœ Ÿœ˜KšœŸœ˜@K˜—KšŸœŸœ|˜Ÿ—K˜—šŸœŸœ˜,šŸœŸœŸ˜šœŸœŸœ˜K˜Kšœ Ÿœ˜KšœŸœ˜@K˜—KšŸœŸœ}˜ —K˜—šŸœ˜šŸœŸœŸœŸ˜-šŸœ:Ÿœ˜BK˜Kšœ Ÿœ˜K˜KšœŸœ˜@KšŸœ˜K˜—KšŸœ˜—K˜—K˜J™—•StartOfExpansionl -- [button: EmbeddedButtons.ActiveButton, doc: EmbeddedButtons.ActiveDoc] RETURNS [key: ATOM, val: REF ANY]šžœ)˜BKšŸœŸœ™0Kšœ˜K˜ KšœŸœ˜Kšœ Ÿœ˜K˜KšŸœŸœŸœŸœ˜"šŸœŸœŸœŸœ˜2Kšœh˜hKšŸœ˜K˜—KšœŸœ˜K˜K˜šŸœŸœŸœ˜K˜K˜—šŸœ˜šŸœ"Ÿœ˜*Kšœ ŸœŸœŸœ˜.š ŸœŸœŸœ ŸœŸœ˜EKšŸœ ŸœŸœ˜(—K˜—šŸœŸœŸœ˜,Kšœ ŸœŸœŸœ˜/š ŸœŸœŸœ ŸœŸœ˜HKšŸœ ŸœŸœ˜+—K˜—šŸœ˜šŸœŸœŸœŸ˜,šŸœE˜GKšŸœŸœ˜—KšŸœ˜—šŸœŸœŸœ˜$K˜KšŸœ!Ÿœ  !˜TK˜—šŸœ˜K˜KšŸœ Ÿœ"˜3K˜—K˜/K˜—K˜—šŸœŸ˜Kšœ>˜>—šŸœ˜Kšœ Ÿœ5˜BKšŸœ Ÿœ1˜@K˜—K˜K˜—š‘™šœ ™ M™»—šœ ™ ™?Iitem™N™8N™N™;N™;N™MN™BN™KN™;N™———šœ+Ÿœ#˜QK˜Kšœ˜KšœŸœ˜K˜Kšœ Ÿœ˜Kšœ Ÿœ˜KšœŸœ˜K˜—šžœŸœ ŸœŸœ ŸœŸœŸœŸœŸœ˜LšŸœŸœŸ˜Kš œŸœŸœŸœŸœŸœŸœ˜&Kš œŸœŸœŸœŸœŸœ˜"KšŸœŸœŸœŸœ ˜&K˜—K˜—šžœ%˜6KšŸœ$ŸœŸœŸœŸœŸœ Ÿœ™xJšœ­™­Kšœ Ÿ˜KšœŸœŸœ˜"Kšœ ŸœŸœ˜K˜Kšœ:˜:šŸœ Ÿ˜˜ KšœŸ˜ Kšœ Ÿœ6˜FKšœ Ÿœ;˜LKš œ ŸœŸœŸœŸœ ˜+K˜*K˜ KšœŸœ˜K˜—šŸœŸ˜ Kšœ9˜9KšŸœŸœ˜ K˜——K˜J™šŸ˜˜-Kšœ ˜ Kšœ˜Kšœ˜Kšœ Ÿœ /˜@Kšœ˜K˜—˜Kšœ Ÿœ˜Kšœ ’œ˜)Kš œ Ÿœ ŸœŸœŸœ˜4K˜ K˜$—KšŸœ˜—Kšœ9˜9KšŸœ ˜K˜K˜—šž œ!˜.Jš œ ŸœŸœ ŸœŸœŸœ™2šŸœ˜!KšŸœ˜ K˜—Kšœ Ÿœ˜ Kšœ˜šŸœŸœŸ˜šœ˜Kšœ Ÿœ;˜LKš œ ŸœŸœŸœŸœ˜,KšœŸœ˜ KšœŸœŸœŸœ˜Kšœg˜gK˜*Kšœ’œ/˜LKšŸœ ŸœŸœ’ œ˜5—K˜—šŸœ˜ K˜t—šŸ˜šœ ˜ K˜gK˜——K˜K˜—šžœ!˜=Jš œ ŸœŸœ ŸœŸœŸœ™2Kšœ Ÿœ˜ J™šŸœŸœŸ˜šœ˜Kšœg˜gšœ’œ.˜KJ™6—KšŸœ ŸœŸœ’ œ˜5K˜—šŸœ˜ KšœM˜MKšœ˜——K˜K˜—šž œ%˜1Kšœ Ÿœ˜šŸ˜šŸœŸ˜ Kšœ=Ÿœ˜QKšœ7Ÿœ˜KKšœ1Ÿœ˜EKšœ7Ÿœ˜KKšœ7Ÿœ˜KKšœ9Ÿœ˜MKšœ;Ÿœ˜OKšŸœ"Ÿœ˜Kšœ( ˜>K˜šž œŸœŸœ+˜DKšœ™KšŸœŸœŸœŸœ˜K˜šŸœŸœ˜K˜K™&KšœŸœ˜;K˜K˜—šŸœŸœ˜K˜K™&KšœŸœ˜:Kšœ˜—šœ˜K˜——šž œŸœŸœ1Ÿœ˜SKšœŸ œ˜KšœŸœ3˜JKšŸœ˜Kšœ˜K˜—š žœŸœ;ŸœŸœŸœ˜WKšœ ŸœŸœŸœ˜Kš œ ŸœŸœ"ŸœŸœ˜pKšŸœŸœ"ŸœŸœŸœŸœŸœŸœ˜΅KšŸœŸœ"ŸœŸœŸœŸœŸœ!ŸœŸœŸœ$˜ίKšŸœŸœ˜ šŸœŸœŸœŸ˜6KšŸœG˜K—K˜K˜—šž œŸœNŸœ ŸœŸœŸœŸœ˜–KšŸœ ŸœŸœŸœ˜K˜(šŸœ ˜"KšŸœ'™+KšŸœ Ÿœ˜<—KšŸœŸœ"˜8Kšœ#ŸœŸœ˜8KšœŸœ˜0šŸœ ˜"KšŸœ'™+KšŸœ Ÿœ˜9—Kšœ˜—š‘™L™ M™ΞL™ M™’—J™J™%šœ*Ÿœ#˜PK˜Kšœ˜KšœŸœ˜K˜Kšœ Ÿœ˜Kšœ Ÿœ˜KšœŸœ˜K˜—K˜šžœ%˜5KšŸœ$ŸœŸœŸœŸœŸœ Ÿœ™xJšœ΄™΄Kšœ Ÿ˜Kšœ"˜"KšœŸœŸœ˜K˜Kšœ8˜8K˜Kšœ=˜=J™šŸœ Ÿ˜˜ Kšœ Ÿœ3˜AKš œ ŸœŸœŸœŸœ ˜)KšœŸ˜ KšœŸœ@˜TšœŸœŸœŸœŸ˜5KšŸœMŸœŸœ˜b—K˜*šŸœ ŸœŸœ˜K˜Kšœ Ÿœ Ÿœl˜ˆK˜—KšŸœ8˜Kšœ˜K˜K™šŸœŸœŸœŸœ˜&Kšœ^˜^KšŸœŸœ˜ K˜—Kšœ Ÿœ˜K˜K™7KšŸœE˜KKšœ˜K˜—šž œŸœ?ŸœŸœ#˜KšœŸœ˜"K˜ KšŸœŸœŸœŸœ˜KšœŸœ˜K˜šŸœŸœŸ˜Kšœ Ÿœ˜ K˜ KšŸœ˜—KšŸœ˜ Kšœ˜—š‘™šœ ™ MšœΌ™ΌMšœ–’œj™…—šœ ™ ™?N™N™1N™N™MN™N™N™!N™6NšœΠeiœ%£œ™7N™Mšœ £œ„™”———J™J™*šœ*Ÿœ#˜PK˜Kšœ˜Kšœ"˜"Kšœ˜Kšœ˜Kšœ˜Kšœ(˜(—K˜K™3KšœŸœŸœ ˜šœ ŸœŸœ˜Kšœ ŸœŸœ˜KšœŸœ˜ Kšœ˜Kšœ Ÿœ˜ Kšœ Ÿ˜KšœŸœ A˜XKšœ˜—K˜šžœ%˜5JšŸœ$ŸœŸœŸœŸœŸœ Ÿœ™xKšœ ˜ K˜Kšœ8˜8J™šœŸœ ˜KšœŸœ˜ Kšœ Ÿœ˜šœŸœ˜KšœŸœ˜KšœŸœŸœ˜—Kšœ Ÿœ˜ Kšœ Ÿœ˜KšœŸœ ˜ K˜—K˜K˜#šŸœŸœŸœŸ˜Kšœ#ŸœŸœ˜0KšŸœ˜—K˜KšœO˜OKšœ7˜7KšŸœ˜ K˜J™—š žœŸœ$ŸœŸœŸœŸœ˜…KšœŸœ˜KšœŸœ˜ Kšœ ŸœŸœ˜Kšœ Ÿœ˜KšœŸœŸœ˜K˜K˜6šœŸœ˜"K˜—Kšœ(’ œ˜?šŸœ ŸœŸœ˜Kšœ%Ÿœ#˜NšŸœŸœŸœ˜šŸœŸ˜šœ ˜ K˜(K˜K˜—šœ ˜ K˜K˜K˜—šœ ˜ K˜K˜K˜—K˜šŸœ˜ Kšœ|˜|——K˜—K˜—š ŸœŸœŸœ ŸœŸœ $˜YK˜:šŸœŸœŸœ˜K˜%Kš Ÿœ!Ÿœ ŸœŸœŸœ˜DKš ŸœŸœŸœ ŸœŸœŸœ˜GšŸœ "˜)KšœŸœ˜šŸœŸœŸœŸ˜-šŸœ7Ÿœ˜>Kšœ!Ÿœ˜'—KšŸœ˜—K˜—šŸœŸœŸœ˜Kšœ€˜€—K˜—K˜—šŸœŸœ 0˜JKšŸœ3˜7—K˜Jšœ™K˜GKšœ ŸœŸœ˜8K˜J™UšŸœŸœ˜šœ˜KšœŸœ˜/Kšœ˜—K˜—K˜K˜—šž œ-˜9KšŸœŸœŸœ ŸœŸœŸœŸœŸœŸœ™|K™K™“K™Kšœ ˜ KšœŸœ˜ Kšœ ŸœŸœŸœ˜K˜K™ KšŸœŸœŸœŸœ˜&šŸœŸœŸœŸœ˜)Kšœe˜eKšŸœ˜ K˜—KšœŸœ˜K˜K™šœŸœ˜Kšœ ˜ Kšœ˜—K˜K™ šŸœ ŸœŸœŸœ>˜XKšŸœP˜T—K˜K™ šŸœ Ÿ˜˜ Kšœ Ÿœ˜K˜K˜K˜?š ŸœŸœŸœ ŸœŸœ˜;KšœŸœ ˜—šŸœ˜K˜)Kšœ;˜;K˜—šŸœ)Ÿœ˜1KšœŸœ˜K˜šœ&Ÿœ˜9K˜K˜—K˜—šŸ˜K˜2—K˜—KšŸœ˜—K˜K™šŸœ Ÿ˜˜ šŸœŸœŸ˜Kšœi˜i——KšŸœ˜—K™K™ šŸœ Ÿ˜KšœŸœ U˜kKšŸœ˜—K˜KšŸœ˜ K˜K˜—šž œ%˜0Kšœ ŸœŸœŸœ˜K˜K™K˜+K˜KšœŸ™!K˜$K˜CK˜Kšœ0˜0K˜K˜—Kšœ Ÿœ˜šžœ˜-KšŸœŸœŸœŸœ™'Kšœ˜K˜K™šŸœŸœŸœ Ÿœ˜3Kšœr˜rKšŸœ˜K˜—KšœŸœ˜K˜Kšœ4˜4K˜K˜—š ž œŸœŸœ ŸœŸœŸœ˜XKšœ ˜ KšœŸœ˜ K˜Kš Ÿœ ŸœŸœŸœŸœŸœ˜5K˜Kš ŸœŸœŸœŸœgŸœ˜—KšœŸœ˜K˜šŸœ-Ÿœ Ÿœ  ˜PKšœ ŸœŸœ ˜KšœDŸœ˜VK˜šŸœŸœŸœ˜QK˜—Kšœg˜gKšœ’œŸœ˜Fš ŸœŸœ ŸœŸœŸœ˜KšŸœ’œŸœ˜HKšŸœ’œŸœ˜G—K˜—šŸœ 9˜@šŸœŸœŸœ ˜.KšœŸœ˜KšœŸœ˜9KšŸœ˜K˜—K˜Kš Ÿœ-ŸœŸœŸœ ,˜K˜K™š ŸœŸœŸœŸœŸœŸœŸ˜7KšŸœŸœŸœ Ÿœ˜QKšŸœ˜—šŸœ ŸœŸœ˜Kšœb˜bKšŸœ˜K˜—K˜šŸœŸœHŸœ˜TKšœŸœ˜K˜KšœŸœ˜8K˜—K˜—Kšœ˜K˜—šžœ"˜*JšŸœŸœŸœ Ÿœ™EKšœ ˜ K˜Kšœ™Kš ŸœŸœŸœŸœŸœ˜'K˜šŸœŸœŸœŸœ˜)Kšœc˜cKšŸœŸœ˜ K˜—KšœŸœ˜J™Jšœ™K˜K˜KšœD˜DK˜K˜—šžœ"˜*Kš ŸœŸœ ŸœŸœ ŸœŸœ™[Kšœ ˜ K˜KšŸœŸœŸœŸœ˜"Kš ŸœŸœŸœŸœeŸœ˜•KšœŸœ˜J™šŸœ!Ÿœ˜)šŸœŸœŸ˜šœŸœŸœ˜K˜Kšœ Ÿœ˜KšœD˜DKšœŸœ˜@K˜—KšŸœŸœ{˜ž—K˜—šŸœŸœŸœ˜+šŸœŸœŸ˜šœŸœŸœ˜K˜Kšœ Ÿœ˜KšœD˜DKšœŸœ˜@K˜—KšŸœŸœ|˜Ÿ—K˜—šŸœ˜šŸœŸœŸœŸ˜,šŸœ9Ÿœ˜AK˜Kšœ Ÿœ˜KšœD˜DKšœŸœ˜@KšŸœ˜K˜—KšŸœ˜—K˜—K˜J™—–l -- [button: EmbeddedButtons.ActiveButton, doc: EmbeddedButtons.ActiveDoc] RETURNS [key: ATOM, val: REF ANY]šžœ)˜=KšŸœŸœ™0Kšœ ˜ KšœŸœ˜Kšœ Ÿœ˜K˜KšŸœŸœŸœŸœ˜"šŸœŸœŸœŸœ˜)Kšœm˜mKšŸœ˜K˜—KšœŸœ˜K˜šŸœ!Ÿœ˜)Kšœ ŸœŸœŸœ˜-š ŸœŸœŸœ ŸœŸœ˜DKšŸœ ŸœŸœ˜(—K˜—šŸœŸœŸœ˜+Kšœ ŸœŸœŸœ˜.š ŸœŸœŸœ ŸœŸœ˜GKšŸœ ŸœŸœ˜+—K˜—šŸœ˜šŸœŸœŸœŸ˜+šŸœC˜EKšŸœŸœ˜—KšŸœ˜—šŸœŸœŸœ˜#K˜KšŸœ Ÿœ  !˜SK˜—šŸœ˜K˜KšŸœ Ÿœ!˜2K˜—K˜.K˜—šŸœŸ˜Kšœ=˜=—šŸœ˜Kšœ Ÿœ0˜=KšŸœ Ÿœ1˜@K˜—K˜—š‘™™ M™Τ—™ M™G——J™J™(šœ-Ÿœ#˜SK˜Kšœ!˜!Kšœ"˜"Kšœ˜K˜K˜K˜(K˜—šžœ%˜8JšŸœ$ŸœŸœŸœŸœŸœ Ÿœ™xJ™J™šœŸœ ˜KšœŸœ˜ šœŸœ˜Kšœ˜Kšœ˜—Kšœ Ÿœ˜ Kšœ Ÿœ˜KšœŸœ ˜K˜—K˜KšœO˜OKšŸœ˜ K˜—š‘™™ MšœΔ™ΔM™ —™ šœ9™9N™N™NN™N™N™!N™8N™/N™0———M™J™$šœ*Ÿœ#˜PK˜Kšœ˜Kšœ"˜"K˜Kšœ˜Kšœ˜Kšœ(˜(—K˜J™-KšœŸœŸœ ˜šœ ŸœŸœ˜Kšœ ŸœŸœ˜KšœŸœ˜ KšœŸœ˜ KšœŸœ˜ Kšœ Ÿœ˜Kšœ ŸœŸœŸœ˜K˜—K˜šžœ%˜5JšŸœ$ŸœŸœŸœŸœŸœ Ÿœ™x™-J™J™J™0J™—Kšœ ˜ KšœŸœ˜Kšœ Ÿœ˜KšœŸœ˜K˜Kšœ8˜8KšœŸœŸœŸœ ŸœŸœŸœ Ÿœ˜oK˜K˜6KšœŸœ˜"K˜šŸœ ŸœŸœ˜KšœE˜EKšœ7˜7KšŸœŸœ˜ K˜K˜—Kšœ+’œ˜IK˜KšœŸœŸœ˜6šŸœŸœŸœ "˜J™΅KšœŸœŸœŸœ˜š ŸœŸœŸœŸœŸœ ŸœŸ˜;Kš ŸœŸœ!ŸœŸœŸœ˜:K˜KšŸœ˜—KšŸœŸœ˜ K˜J™—šžœŸ œŸœŸœŸœŸœŸœ˜:šžœŸœŸœŸœŸœŸœŸœŸœ˜:š ŸœŸœŸœŸœŸœŸœŸ˜8KšŸœŸœŸœŸœ˜*KšŸœ˜—KšŸœŸœ˜K˜—J™Tš ŸœŸœŸœŸœŸœ ŸœŸ˜;Kš ŸœŸœŸœŸœŸœ˜3KšŸœ˜—KšŸœŸœ˜ K˜K˜—šžœŸ œ ŸœŸœŸœŸœŸœŸœŸœŸœ ŸœŸœ˜š ŸœŸœŸœŸœŸœŸœŸ˜=KšœŸœŸœŸœ˜Kšœ Ÿœ˜šŸœŸœ ŸœŸœŸœŸœŸœŸœ˜>Kšœ.˜.KšŸœ˜K˜—KšœŸœ ˜š ŸœŸœŸœŸœŸœŸœ˜9Kšœ.˜.KšŸœ˜K˜—Kšœ Ÿœ˜šŸœŸœ˜K˜Kšœ Ÿœ˜KšŸœ˜K˜—KšŸœ˜—K˜K™—šžœŸœ3˜PJ™}Kšœ ŸœŸœŸœ˜Kšœ?˜?K˜K™[šŸœ˜ K˜`Kšœ˜—K˜TšŸœŸœŸœ˜Kšœ’œ ˜JK˜—Kšœ: $˜^Kšœ>˜>K˜K˜—šœŸœ˜&K˜—šžœŸ œŸœŸœ˜;K˜K˜TK˜kK˜K˜—š žœŸœŸœŸœŸœŸœ˜;KšŸœL˜RK˜—Lš‘™J™*Kšœ&˜&Kšœ6˜6Kšœ&Ÿœ˜>Kš œ ŸœŸœŸœŸœŸœ˜'Kš œ ŸœŸœŸœŸœŸœ˜%K˜K˜AKšœ9’œ˜MK˜K˜YKšœI’œ˜cJ™K˜)K˜0K˜/J™J™KšœE ˜]K˜CKšœ>˜>Kšœ>˜>KšœI ˜XKšœA˜AJ™KšŸœ˜—…—Π2JŸ