DIRECTORY Atom, CodeTimer, EBButtonClasses, EBConcreteTypes, EBEditors, EBEditorsExtras, EBEvent, EBLanguage, EBMesaLisp, EBTypes, EmbeddedButtons, IO, KeyMapping, KeyTypes, Process, RefTab, Rope, ScreenCoordsTypes, SimpleFeedback, SpecialKeySyms, TIPPrivate, TIPTypes, UserInput, UserInputDiscrimination, UserInputOps, UserInputOpsExtras2, ViewerClasses, ViewerOps; EBImpl: CEDAR MONITOR -- for parseInfo -- IMPORTS Atom, CodeTimer, EBLanguage, EBMesaLisp, IO, KeyMapping, RefTab, Rope, SimpleFeedback, TIPPrivate, UserInputDiscrimination, UserInputOps, UserInputOpsExtras2, ViewerOps EXPORTS EBButtonClasses, EBEditorsExtras, EBEditors, EBTypes, EmbeddedButtons = BEGIN ActiveButton: TYPE = EBTypes.ActiveButton; ActiveDoc: TYPE = EBTypes.ActiveDoc; ButtonClass: TYPE = EBButtonClasses.ButtonClass; ButtonInfo: TYPE = EBTypes.ButtonInfo; ButtonInfoRec: TYPE = EBTypes.ButtonInfoRec; ButtonInfoPrivateRec: PUBLIC TYPE = EBConcreteTypes.ButtonInfoPrivateRec; EBLanguageProc: TYPE = EBLanguage.EBLanguageProc; ROPE: TYPE = Rope.ROPE; Variable: TYPE = REF VariableObj; VariableObj: TYPE = EBLanguage.VariableObj; VariableTable: TYPE = EBConcreteTypes.VariableTable; Viewer: TYPE = ViewerClasses.Viewer; TargetTableEntry: TYPE = REF TargetTableEntryRep; TargetTableEntryRep: TYPE = EBConcreteTypes.TargetTableEntryRep; Error: PUBLIC PROC [screenMessage: ROPE, fileMessage: ROPE ¬ NIL] = { SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $UserError, screenMessage]; }; ActiveDocObj: TYPE = EBTypes.ActiveDocObj; ActiveDocClass: TYPE = EBEditors.ActiveDocClass; ActiveDocPrivateObj: PUBLIC TYPE = EBConcreteTypes.ActiveDocPrivateObj; Event: TYPE ~ EBEvent.Event; EventRep: PUBLIC TYPE ~ EBEvent.EventRep; -- for EBTypes Handle: TYPE = UserInput.Handle; CreateActiveDoc: PUBLIC PROC [theDoc: REF, docClass: ActiveDocClass] RETURNS [theActiveDoc: ActiveDoc] = { nameTable: RefTab.Ref ¬ RefTab.Create[]; targetTable: RefTab.Ref ¬ RefTab.Create[]; theActiveDoc ¬ NEW[ActiveDocObj ¬ [ theDoc: theDoc, private: NEW[ActiveDocPrivateObj ¬ [ class: docClass, nameTable: nameTable, targetTable: targetTable]]]]; }; GetHandle: PUBLIC PROC [event: Event] RETURNS [UserInput.Handle] = { RETURN[event.handle]; }; GetEvent: PUBLIC PROC [input: LIST OF REF] RETURNS [Event ¬ NIL] ~ { IF input#NIL AND UserInputDiscrimination.IsHandle[input.first] AND input.rest#NIL THEN { WITH input.rest.first SELECT FROM action: EBEvent.Action => IF input.rest.rest=NIL THEN { handle: Handle _ UserInputDiscrimination.NarrowHandle[input.first]; RETURN[NEW[EventRep ¬ [handle, action]]]; }; ENDCASE; }; }; ValidEvent: PUBLIC PROC [event: Event] RETURNS [BOOL] ~ { RETURN[event#NIL]; }; Button1: KeyTypes.KeySym ~ SpecialKeySyms.Button1; Button2: KeyTypes.KeySym ~ SpecialKeySyms.Button2; Button3: KeyTypes.KeySym ~ SpecialKeySyms.Button3; MouseMotion: PUBLIC PROC [event: Event] RETURNS [BOOL ¬ FALSE] = { SELECT event.action.kind FROM $IntegerPosition, $Position, $FakePosition => RETURN[TRUE]; ENDCASE; }; MouseAction: PUBLIC PROC [event: Event] RETURNS [BOOL ¬ FALSE] ~ { SELECT event.action.kind FROM $Key, $KeyStillDown => { keySyms: KeyMapping.KeySyms ~ KeyMapping.GetKeySyms[UserInputOps.GetMapping[event.handle], event.action.keyCode]; FOR i: BYTE IN[0..keySyms.n) DO SELECT keySyms[i] FROM Button1, Button2, Button3 => RETURN[TRUE]; ENDCASE; ENDLOOP; }; $IntegerPosition, $Position, $FakePosition => RETURN[TRUE]; ENDCASE; }; MouseAllUp: PUBLIC PROC [event: Event] RETURNS [BOOL] ~ { RETURN[ UserInputOps.GetKeySymState[event.handle, Button1]=up AND UserInputOps.GetKeySymState[event.handle, Button2]=up AND UserInputOps.GetKeySymState[event.handle, Button3]=up ]; }; MouseCoords: PUBLIC PROC [event: Event] RETURNS [EBEditors.MousePosition] ~ { RETURN[UserInputOpsExtras2.GetPosition[event.handle]]; }; HandleEvent: PUBLIC PROC [event: Event, button: ActiveButton, doc: ActiveDoc] ~ { buttonInfo: ButtonInfo ~ GetButtonInfo[button, doc]; IF buttonInfo#NIL THEN { class: ButtonClass ~ buttonInfo.private.class; IF class#NIL AND class.handleEvent#NIL THEN -- Pass event to button class class.handleEvent[event, buttonInfo.private.instanceData, buttonInfo]; }; }; parseInfo: TIPPrivate.TIPParseInfo ~ TIPPrivate.CreateParseInfo[name: "EBImpl.ParseEvent"]; MatchEvent: ENTRY PROC [table: TIPTypes.TIPTable, event: Event] RETURNS [LIST OF REF] ~ { ENABLE UNWIND => NULL; parseInfo.inCreek ¬ event.handle; parseInfo.tableHead ¬ table; RETURN[TIPPrivate.WideMatchEvent[parseInfo, event.action­]]; }; stdCoords: ScreenCoordsTypes.TIPScreenCoords ~ TIPPrivate.stdCoords; ParseEvent: PUBLIC PROC [table: TIPTypes.TIPTable, event: Event] RETURNS [LIST OF REF] ~ { result: LIST OF REF ~ MatchEvent[table, event]; FOR list: LIST OF REF ¬ result, list.rest UNTIL list=NIL DO IF list.first=stdCoords THEN { [] ¬ ViewerOps.MouseInViewer[stdCoords]; EXIT }; ENDLOOP; RETURN[result]; }; ButtonDataFromRope: PUBLIC PROC [rope: ROPE, instantiateNow: BOOL ¬ FALSE, button: ActiveButton ¬ NIL, doc: ActiveDoc ¬ NIL] RETURNS [val: REF] = { CodeTimer.StartInt[$ButtonDataFromRope, $EmbeddedButtons]; val ¬ NEW[ButtonInfoRec ¬ [ button: button, doc: doc, private: NEW [ButtonInfoPrivateRec ¬ [ rope: rope, changed: FALSE, class: NIL, nameChecked: FALSE, name: $Unchecked, fieldValuesOK: FALSE, language: NIL, languageVersion: 0, instanceData: NIL, hasComplained: FALSE]]]]; CodeTimer.StopInt[$ButtonDataFromRope, $EmbeddedButtons]; }; RopeFromButtonData: PUBLIC PROC [val: REF] RETURNS [rope: Rope.ROPE ¬ ""] = { CodeTimer.StartInt[$RopeFromButtonData, $EmbeddedButtons]; WITH val SELECT FROM buttonInfo: ButtonInfo => { IF buttonInfo.private.class = NIL OR ~buttonInfo.private.changed OR buttonInfo.private.class.unparseInstanceData = NIL THEN rope ¬ buttonInfo.private.rope ELSE { SELECT buttonInfo.private.language FROM $Poppy => { [] ¬ buttonInfo.private.class.unparseInstanceData[buttonInfo.private.instanceData, buttonInfo, $Poppy, 1]; rope ¬ Rope.Concat["Poppy1 ", EBLanguage.PoppyUnparse[buttonInfo.private.symbols, buttonInfo.private.order]]; }; $ButtonLisp => rope ¬ Rope.Cat[Atom.GetPName[buttonInfo.private.class.name], " ", buttonInfo.private.class.unparseInstanceData[buttonInfo.private.instanceData, buttonInfo, $ButtonLisp, 1]]; ENDCASE; }; }; ENDCASE => Error["EmbeddedButtons: Internal error. ButtonData is wrong type (RopeFromButtonData)"]; CodeTimer.StopInt[$RopeFromButtonData, $EmbeddedButtons]; }; ParseFeedbackField: EBLanguage.FieldParseProc = { rope: ROPE ¬ stream.GetRope[]; val ¬ ButtonFeedbackFromRope[rope]; }; UnparseFeedbackField: EBLanguage.FieldUnparseProc = { WITH val SELECT FROM ft: FeedbackTable => RETURN[ft.rope]; ENDCASE => RETURN[""]; }; ButtonFeedbackFromRope: PUBLIC PROC [rope: ROPE, instantiateNow: BOOL ¬ FALSE] RETURNS[val: REF] = { val ¬ NEW[FeedbackTableRec ¬ [ rope: rope, table: NIL]]; }; ButtonFeedbackToRope: PUBLIC PROC [val: REF] RETURNS [rope: Rope.ROPE _ NIL] = { WITH val SELECT FROM ft: FeedbackTable => RETURN[ft.rope]; ENDCASE => Error["EmbeddedButtons: Internal error. ButtonFeedback is wrong type (ButtonFeedbackToRope)"]; }; RegisterButtonClass: PUBLIC PROC [buttonClassName: ATOM, buttonClass: ButtonClass] = { [] ¬ RefTab.Store[classTable, buttonClassName, buttonClass]; }; RegisterNameValuePair: PUBLIC PROC [name: ATOM, value: REF, buttonInfo: ButtonInfo] = { [] ¬ RefTab.Store[buttonInfo.doc.private.nameTable, name, value]; }; CheckListOfATOM: PROC [list: LIST OF REF] RETURNS [ok: BOOL ¬ TRUE] = { FOR l: LIST OF REF ¬ list, l.rest UNTIL l = NIL DO IF NOT ISTYPE[l.first, ATOM] THEN { Error["EmbeddedButtons: Message handlers (targets) must be atoms"]; RETURN[FALSE]; }; ENDLOOP; }; UnknownLanguage: PROC [] = { Error["PassEventToApplication: Unknown button language"]; }; GetApplications: PUBLIC PROC [buttonInfo: ButtonInfo] RETURNS [applications: LIST OF ATOM] = { SELECT buttonInfo.private.language FROM $Poppy => { targetsRope: ROPE ~ EBLanguage.GetFieldRope[buttonInfo.private.symbols, $MessageHandler]; val: REF ~ EBMesaLisp.Parse[IO.RIS[targetsRope]].val; tail: LIST OF ATOM; IF ISTYPE[val, LIST OF REF] THEN { FOR list: LIST OF REF ¬ NARROW[val], list.rest UNTIL list = NIL DO IF NOT ISTYPE[list.first, ATOM] THEN RETURN[NIL]; -- one bad apple... [applications, tail] ¬ AddAtom[NARROW[list.first], applications, tail]; ENDLOOP; } ELSE { IF ISTYPE[val, ATOM] THEN applications ¬ LIST[NARROW[val, ATOM]] ELSE applications ¬ NIL; }; }; ENDCASE => {UnknownLanguage[]; RETURN[NIL]}; }; AddAtom: PUBLIC PROC [entity: ATOM, entityList, ptr: LIST OF ATOM] RETURNS [newList, newPtr: LIST OF ATOM] = { IF ptr = NIL THEN { IF NOT entityList = NIL THEN ERROR; newPtr ¬ newList ¬ CONS[entity, NIL]; RETURN; } ELSE { newList ¬ entityList; ptr.rest ¬ CONS[entity, NIL]; newPtr ¬ ptr.rest; }; }; DefaultBehavior: PROC [class: ButtonClass, instanceData: REF, buttonInfo: ButtonInfo] = INLINE { IF class # NIL AND class.defaultBehavior # NIL THEN class.defaultBehavior[instanceData, buttonInfo]; }; PassEventToApplication: PUBLIC PROC [event: LIST OF REF, buttonInfo: ButtonInfo, application: ATOM ¬ $MessageHandlers] = { targets: LIST OF ATOM; IF application = $MessageHandlers THEN targets ¬ GetApplications[buttonInfo] ELSE targets ¬ LIST[application]; ForkedEvent[event, buttonInfo, targets]; }; ForkedEvent: PROC [event: LIST OF REF, buttonInfo: ButtonInfo, targets: LIST OF ATOM] = { anyTargetsFound: BOOL ¬ FALSE; target: ATOM; found: BOOL; class: ButtonClass; instanceData: REF; val, results: REF; class ¬ buttonInfo.private.class; instanceData ¬ buttonInfo.private.instanceData; IF event = NIL OR targets = NIL THEN {DefaultBehavior[class, instanceData, buttonInfo]; RETURN}; FOR l: LIST OF ATOM ¬ targets, l.rest UNTIL l = NIL DO -- for each application... target ¬ l.first; IF target = $Default THEN { DefaultBehavior[class, instanceData, buttonInfo]; anyTargetsFound ¬ TRUE; LOOP; }; [found, val] ¬ RefTab.Fetch[buttonInfo.doc.private.targetTable, target]; IF found THEN { -- this document is linked to a particular application window anyTargetsFound ¬ TRUE; WITH val SELECT FROM entry: TargetTableEntry => { IF entry.notifyProc # NIL THEN { results ¬ entry.notifyProc­[buttonInfo, event, entry.viewer, entry.applicationData]; ResultsFeedback[results, buttonInfo]; } ELSE IF entry.viewer # NIL THEN { ViewerOps.NotifyViewer[entry.viewer, event]; }; }; ENDCASE => { Error["EmbeddedButtons: Internal error. Target table entry is wrong type (PassEventToApplication)"]; RETURN; }; } ELSE { -- Let class do default handling and then try globally-registered applications IF class # NIL AND class.defaultBehavior # NIL THEN class.defaultBehavior[instanceData, buttonInfo]; [found, val] ¬ RefTab.Fetch[registeredNotifyTable, target]; IF found THEN { anyTargetsFound ¬ TRUE; WITH val SELECT FROM proc: REF RegisteredNotifyProc => { IF proc # NIL THEN results ¬ proc­[event, buttonInfo] ELSE results ¬ $Error; ResultsFeedback[results, buttonInfo]; }; ENDCASE => { Error["EmbeddedButtons: Internal error. Registered notify proc is wrong type (PassEventToApplication)"]; RETURN; }; } ELSE Error[Rope.Concat["This message handler (target) not found: ", Atom.GetPName[target]]]; }; ENDLOOP; }; okEvent: LIST OF REF = LIST[$Done, $OK]; errorEvent: LIST OF REF = LIST[$Done, $Error]; warningEvent: LIST OF REF = LIST[$Done, $Warning]; ResultsFeedback: PROC [results: REF, buttonInfo: ButtonInfo] ~ { feedbackEvent: LIST OF REF; WITH results SELECT FROM list: LIST OF REF => feedbackEvent ¬ list; ENDCASE => { IF results = NIL THEN feedbackEvent ¬ okEvent ELSE IF results = $Error THEN feedbackEvent ¬ errorEvent ELSE IF results = $Warning THEN feedbackEvent ¬ warningEvent ELSE feedbackEvent ¬ LIST[results]; }; FeedbackNotify[feedbackEvent, buttonInfo]; }; FeedbackTable: TYPE = REF FeedbackTableRec; FeedbackTableRec: TYPE = RECORD [ rope: ROPE, table: REF FeedbackTableObj ]; FeedbackTableObj: TYPE = LIST OF FeedbackTableEntry; FeedbackTableEntry: TYPE = REF FeedbackTableEntryRec; FeedbackTableEntryRec: TYPE = RECORD [ event: LIST OF REF, action: LIST OF REF ]; MatchFeedbackTableEntry: PROC [feedbackEvent: LIST OF REF, table: FeedbackTable] RETURNS [bestAction: LIST OF REF ¬ NIL] = { bestActionMatches: INT ¬ 0; FOR l: LIST OF FeedbackTableEntry ¬ table.table­, l.rest UNTIL l = NIL DO m: INT; m ¬ IF Subset[l.first.event, feedbackEvent] THEN Matches[feedbackEvent, l.first.event] ELSE 0; IF m > bestActionMatches THEN { bestAction ¬ l.first.action; bestActionMatches ¬ m; }; ENDLOOP; }; Subset: 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]; }; 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]; }; Matches: 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; }; CheckFeedbackTable: PROC [buttonInfo: ButtonInfo] RETURNS [table: FeedbackTable ¬ NIL] = { buttonFeedback: REF; SELECT buttonInfo.private.language FROM $Poppy => { buttonFeedback ¬ EBLanguage.GetFieldRef[buttonInfo.private.symbols, $Feedback]; }; ENDCASE => RETURN; IF buttonFeedback = NIL THEN RETURN[NIL]; -- this button doesn't specify any feedback WITH buttonFeedback SELECT FROM t: FeedbackTable => { table ¬ t; IF table.table = NIL THEN { table.table ¬ ParseFeedbackTable[table.rope]; IF table.table = NIL THEN RETURN[NIL]; }; }; ENDCASE => { Error["EmbeddedButtons: Internal error. Button feedback table is wrong type (FeedbackNotify)"]; RETURN[NIL]; }; }; refTRUE: REF BOOL ¬ NEW[BOOL ¬ TRUE]; FeedbackNotify: PUBLIC PROC [feedbackEvent: LIST OF REF, buttonInfo: ButtonInfo] = { table: FeedbackTable; bestAction: LIST OF REF; CodeTimer.StartInt[$FeedbackNotify, $EmbeddedButtons]; table ¬ CheckFeedbackTable[buttonInfo]; IF table = NIL THEN RETURN; bestAction ¬ MatchFeedbackTableEntry[feedbackEvent, table]; IF bestAction = NIL OR (bestAction.first = NIL AND bestAction.rest = NIL) THEN { CodeTimer.StopInt[$FeedbackNotify, $EmbeddedButtons]; RETURN; }; ActionToEditor[bestAction, buttonInfo, feedbackEvent]; CodeTimer.StopInt[$FeedbackNotify, $EmbeddedButtons]; }; Context: TYPE = EBTypes.Context; feedbackContext: Context ¬ EBLanguage.CreateContext[]; InButton: PUBLIC PROC [x, y: INTEGER, buttonInfo: ButtonInfo] RETURNS[BOOL] = { IF buttonInfo.doc.private.class.inButton # NIL THEN RETURN[buttonInfo.doc.private.class.inButton[buttonInfo.button, buttonInfo.doc, x, y]]; RETURN[TRUE]; }; MapButtons: PUBLIC PROC [doc: ActiveDoc, proc: EachButtonProc] RETURNS [aborted: BOOL ¬ FALSE] = { IF doc.private.class.mapRef # NIL THEN aborted ¬ doc.private.class.mapRef[doc, proc]; }; EachButtonProc: TYPE = EmbeddedButtons.EachButtonProc; RegisterPoppyProc: PUBLIC PROC [procedureName: ATOM, proc: EBLanguageProc, interpreted: BOOL ¬ TRUE] = { EBLanguage.RegisterProc[procedureName, proc, interpreted]; }; FeedbackToEditor: PUBLIC PROC [feedback: REF, buttonInfo: ButtonInfo] RETURNS [REF] = { RETURN[buttonInfo.doc.private.class.feedback[buttonInfo.button, buttonInfo.doc, feedback]]; }; ActionToEditor: PUBLIC PROC [action: LIST OF REF, buttonInfo: ButtonInfo, feedbackEvent: LIST OF REF] = { receivers: LIST OF REF ¬ LIST[buttonInfo.doc.private.class.name]; CodeTimer.StartInt[$ActionToEditor, $EmbeddedButtons]; EBLanguage.SetSystemValue[feedbackContext, $MessageReceiver, receivers]; EBLanguage.SetSystemValue[feedbackContext, $Feedback, refTRUE]; IF ISTYPE[action.first, LIST OF REF] THEN { FOR l: LIST OF REF ¬ action, l.rest UNTIL l = NIL DO WITH l.first SELECT FROM lora: LIST OF REF => { message: REF ¬ EBLanguage.Evaluate[lora, buttonInfo, feedbackEvent, feedbackContext]; IF message # NIL THEN [] ¬ FeedbackToEditor[message, buttonInfo]; }; ENDCASE => Error["EmbeddedButtons: Feedback table actions must be lists!"]; ENDLOOP; } ELSE { message: REF ¬ EBLanguage.Evaluate[action, buttonInfo, feedbackEvent, feedbackContext]; IF message # NIL THEN [] ¬ FeedbackToEditor[message, buttonInfo]; }; CodeTimer.StopInt[$ActionToEditor, $EmbeddedButtons]; }; SetButtonValue: PUBLIC PROC [buttonInfo: ButtonInfo, variable: ATOM ¬ $Value, value: REF] = { IF variable = $Value THEN { class: ButtonClass ¬ buttonInfo.private.class; instanceData: REF ¬ buttonInfo.private.instanceData; IF class # NIL AND class.setValue # NIL THEN { changed: BOOL ¬ class.setValue[instanceData, value, buttonInfo]; IF changed THEN MarkButtonAsChanged[buttonInfo]; }; } ELSE { variableTable: VariableTable ¬ NARROW[GetFieldRef[$Variables, buttonInfo]]; IF variableTable = NIL THEN RETURN ELSE { realVar: Variable ¬ EBLanguage.GetVariable[variableTable, variable]; IF realVar = NIL THEN { Error[IO.PutFR["Button %g has non-existent or incorrect definition for variable %g. Can't SetButtonValue", [atom[buttonInfo.private.name]], [atom[variable]] ]]; } ELSE { realVar.value ¬ value; EBLanguage.SetVariable[variableTable, variable, realVar]; }; }; }; }; NotifyProc: TYPE = EmbeddedButtons.NotifyProc; RegisteredNotifyProc: TYPE = EmbeddedButtons.RegisteredNotifyProc; LinkDocToApplication: PUBLIC PROC [doc: ActiveDoc, target: ATOM, targetViewer: Viewer ¬ NIL, applicationData: REF ¬ NIL, notifyProc: NotifyProc ¬ NIL] = { refNotifyProc: REF NotifyProc ¬ IF notifyProc = NIL THEN NIL ELSE NEW[NotifyProc ¬ notifyProc]; tte: TargetTableEntry ¬ NEW[TargetTableEntryRep ¬ [ viewer: targetViewer, applicationData: applicationData, notifyProc: refNotifyProc]]; [] ¬ RefTab.Store[doc.private.targetTable, target, tte]; }; RegisterApplication: PUBLIC PROC [target: ATOM, notifyProc: RegisteredNotifyProc] = { refRegisteredNotifyProc: REF RegisteredNotifyProc ¬ IF notifyProc = NIL THEN NIL ELSE NEW[RegisteredNotifyProc ¬ notifyProc]; [] ¬ RefTab.Store[registeredNotifyTable, target, refRegisteredNotifyProc]; }; GetValue: PUBLIC PROC [name: ATOM, doc: ActiveDoc, variable: ATOM ¬ $Value] RETURNS [val: REF ANY] = { MapGetKeyValue: EmbeddedButtons.EachButtonProc = { class: ButtonClass; instanceData: REF; thisName: ATOM; exists: BOOL ¬ FALSE; [thisName, exists] ¬ GetName[button, doc]; IF exists AND thisName = name THEN { buttonInfo: ButtonInfo ¬ GetButtonInfo[button, doc]; -- does a full parse, if needed val ¬ GetButtonValue[buttonInfo, variable]; -- stores value in nameTable, if appropriate }; }; found: BOOL ¬ FALSE; IF variable = $Value THEN { [found, val] ¬ RefTab.Fetch[doc.private.nameTable, name]; }; IF val = NIL THEN [] ¬ doc.private.class.mapRef[doc, MapGetKeyValue]; }; GetButtonName: PUBLIC PROC [buttonInfo: ButtonInfo] RETURNS [name: ATOM] = { exists: BOOL ¬ FALSE; [name, exists] ¬ GetNameInternal[buttonInfo]; IF NOT exists THEN name ¬ NIL; }; GetNameInternal: PROC [buttonInfo: ButtonInfo] RETURNS [name: ATOM, exists: BOOL ¬ FALSE] = { IF buttonInfo # NIL THEN { IF buttonInfo.private.nameChecked THEN { name ¬ buttonInfo.private.name; exists ¬ TRUE; } ELSE { success: BOOL ¬ ConfirmFields[buttonInfo]; IF success THEN { nameRope: ROPE ¬ GetFieldRope[$Name, buttonInfo]; nameVal: REF ¬ EBMesaLisp.Parse[IO.RIS[nameRope]].val; IF nameVal # NIL THEN { IF ISTYPE[nameVal, ATOM] THEN { name ¬ NARROW[nameVal]; exists ¬ TRUE; buttonInfo.private.nameChecked ¬ TRUE; buttonInfo.private.name ¬ name; } ELSE Error["GetName: Invalid Name field!"]; }; }; }; }; }; GetButtonValue: PUBLIC PROC [buttonInfo: ButtonInfo, variable: ATOM ¬ $Value] RETURNS [value: REF ¬ NIL] = { IF variable = $Value THEN { class: ButtonClass ¬ buttonInfo.private.class; instanceData: REF ¬ buttonInfo.private.instanceData; IF class # NIL AND class.getValue # NIL THEN value ¬ class.getValue[instanceData, buttonInfo]; } ELSE { variableTable: VariableTable ¬ NARROW[GetFieldRef[$Variables, buttonInfo]]; IF variableTable = NIL THEN RETURN[NIL] ELSE { realVar: Variable ¬ EBLanguage.GetVariable[variableTable, variable]; IF realVar = NIL THEN { value ¬ NIL; Error[IO.PutFR["Button %g has non-existent or incorrect definition for variable %g. Can't GetButtonValue", [atom[buttonInfo.private.name]], [atom[variable]] ]]; } ELSE value ¬ realVar.value; }; }; }; SetValue: PUBLIC PROC [name: ATOM, val: REF, doc: ActiveDoc, variable: ATOM ¬ $Value] = { MapSetKeyValue: EBEditors.EachButtonProc = { thisName: ATOM; exists: BOOL ¬ TRUE; [thisName, exists] ¬ GetName[button, doc]; IF exists AND thisName = name THEN { buttonInfo: ButtonInfo ¬ GetButtonInfo[button, doc]; -- fully instantiates if necessary IF buttonInfo = NIL THEN RETURN; SetButtonValue[buttonInfo, variable, val]; }; }; [] ¬ doc.private.class.mapRef[doc, MapSetKeyValue]; }; GetRawButtonInfo: PROC [button: ActiveButton, doc: ActiveDoc] RETURNS [buttonInfo: ButtonInfo ¬ NIL] = { data: REF ¬ doc.private.class.getRef[$ButtonData, button, doc]; IF data = NIL THEN { Error[IO.PutFR1["Some button in doc %g has a NIL $ButtonData property (Instantiated?)", [rope[doc.private.class.getDocName[doc]]] ]]; buttonInfo ¬ NIL; } ELSE { WITH data SELECT FROM b: ButtonInfo => buttonInfo ¬ b; r: ROPE => { buttonInfo ¬ NARROW[ButtonDataFromRope[r]]; doc.private.class.setRef[$ButtonData, button, doc, buttonInfo, FALSE]; }; ENDCASE => { Error["Don't know what this ButtonData is!", "A button was pressed whose ButtonData was an unknown type."]; buttonInfo ¬ NIL; }; }; }; GetButtonInfo: PROC [button: ActiveButton, doc: ActiveDoc] RETURNS [buttonInfo: ButtonInfo] = { CodeTimer.StartInt[$GetButtonInfo, $EmbeddedButtons]; buttonInfo ¬ GetRawButtonInfo[button, doc]; IF buttonInfo # NIL THEN { success: BOOL ¬ FALSE; buttonInfo.button ¬ button; buttonInfo.doc ¬ doc; success ¬ ConfirmButtonInfo[buttonInfo]; IF NOT success THEN buttonInfo ¬ NIL; }; CodeTimer.StopInt[$GetButtonInfo, $EmbeddedButtons]; }; GetName: PROC [button: ActiveButton, doc: ActiveDoc] RETURNS [name: ATOM, exists: BOOL ¬ FALSE] = { buttonInfo: ButtonInfo ¬ GetRawButtonInfo[button, doc]; -- no parsing, but may already be parsed IF buttonInfo # NIL THEN { IF buttonInfo.private.nameChecked THEN { name ¬ buttonInfo.private.name; exists ¬ TRUE; } ELSE { success: BOOL ¬ ConfirmFields[buttonInfo]; IF success THEN { nameRope: ROPE ¬ GetFieldRope[$Name, buttonInfo]; nameVal: REF ¬ EBMesaLisp.Parse[IO.RIS[nameRope]].val; IF nameVal # NIL THEN { IF ISTYPE[nameVal, ATOM] THEN { name ¬ NARROW[nameVal]; exists ¬ TRUE; buttonInfo.private.nameChecked ¬ TRUE; buttonInfo.private.name ¬ name; } ELSE Error["GetName: Invalid Name field!"]; }; }; }; }; }; GetButtonClass: PUBLIC PROC [buttonInfo: ButtonInfo] RETURNS [buttonClass: ButtonClass ¬ NIL] = { buttonClass ¬ buttonInfo.private.class; }; GetDocClassName: PUBLIC PROC [doc: ActiveDoc] RETURNS[name: ATOM] = { name ¬ doc.private.class.name; }; GetFieldRope: PUBLIC PROC [key: ATOM, buttonInfo: ButtonInfo] RETURNS [rope: ROPE] = { rope ¬ EBLanguage.GetFieldRope[buttonInfo.private.symbols, key]; }; GetFieldAtom: PUBLIC PROC [key: ATOM, buttonInfo: ButtonInfo] RETURNS [atom: ATOM] = { atom ¬ EBLanguage.GetFieldAtom[buttonInfo.private.symbols, key]; }; GetFieldRef: PUBLIC PROC [key: ATOM, buttonInfo: ButtonInfo] RETURNS [ref: REF] = { ref ¬EBLanguage.GetFieldRef[buttonInfo.private.symbols, key]; }; SetFieldRope: PUBLIC PROC [key: ATOM, rope: ROPE, buttonInfo: ButtonInfo] = { EBLanguage.SetFieldRope[buttonInfo.private.symbols, buttonInfo.private.order, key, rope]; }; SetFieldAtom: PUBLIC PROC [key: ATOM, atom: ATOM, buttonInfo: ButtonInfo] = { EBLanguage.SetFieldAtom[buttonInfo.private.symbols, buttonInfo.private.order, key, atom]; }; SetFieldRef: PUBLIC PROC [key: ATOM, ref: REF, buttonInfo: ButtonInfo] = { EBLanguage.SetFieldRef[buttonInfo.private.symbols, buttonInfo.private.order, key, ref]; }; GetAtom: PUBLIC PROC [key: ATOM, buttonInfo: ButtonInfo] RETURNS [atom: ATOM ¬ NIL] = { r: REF ¬ buttonInfo.doc.private.class.getRef[key, buttonInfo.button, buttonInfo.doc]; IF r = NIL THEN RETURN; WITH r SELECT FROM a: ATOM => atom ¬ a; ENDCASE => Error["EmbeddedButtons: Value returned to GetAtom was not an ATOM."]; }; SetAtom: PUBLIC PROC [key: ATOM, atom: ATOM, buttonInfo: ButtonInfo, edited: BOOL] = { buttonInfo.doc.private.class.setRef[key, buttonInfo.button, buttonInfo.doc, atom, edited]; }; GetRope: PUBLIC PROC [key: ATOM, buttonInfo: ButtonInfo] RETURNS [rope: ROPE] = { r: REF ¬ buttonInfo.doc.private.class.getRef[key, buttonInfo.button, buttonInfo.doc]; IF r = NIL THEN RETURN; WITH r SELECT FROM a: ROPE => rope ¬ a; ENDCASE => Error["EmbeddedButtons: Value returned to GetRope was not a ROPE."]; }; SetRope: PUBLIC PROC [key: ATOM, rope: ROPE, buttonInfo: ButtonInfo, edited: BOOL] = { buttonInfo.doc.private.class.setRef[key, buttonInfo.button, buttonInfo.doc, rope, edited]; }; GetRef: PUBLIC PROC [key: ATOM, buttonInfo: ButtonInfo] RETURNS [ref: REF] = { ref ¬ buttonInfo.doc.private.class.getRef[key, buttonInfo.button, buttonInfo.doc]; }; SetRef: PUBLIC PROC [key: ATOM, ref: REF, buttonInfo: ButtonInfo, edited: BOOL] = { buttonInfo.doc.private.class.setRef[key, buttonInfo.button, buttonInfo.doc, ref, edited]; }; GetText: PUBLIC PROC [buttonInfo: ButtonInfo] RETURNS [text: ROPE] = { text ¬ buttonInfo.doc.private.class.getText[buttonInfo.button, buttonInfo.doc]; }; GetDocName: PUBLIC PROC [buttonInfo: ButtonInfo] RETURNS [name: ROPE] = { name ¬ buttonInfo.doc.private.class.getDocName[buttonInfo.doc]; }; ParseFeedbackTable: PROC [r: ROPE] RETURNS [f: REF FeedbackTableObj ¬ NIL] = { s: IO.STREAM; CodeTimer.StartInt[$ParseFeedbackTable, $EmbeddedButtons]; s ¬ IO.RIS[r]; f ¬ ParseCurrentFeedbackTable[s]; CodeTimer.StopInt[$ParseFeedbackTable, $EmbeddedButtons]; }; ParseCurrentFeedbackTable: PROC [s: IO.STREAM] RETURNS [f: REF FeedbackTableObj ¬ NIL] = { object: REF; head: FeedbackTableObj; tail: FeedbackTableObj; char: CHAR; f ¬ NEW[FeedbackTableObj ¬ NIL]; [] ¬ s.SkipWhitespace[]; char ¬ s.PeekChar[]; IF char # '( AND char # '< THEN { Error["EmbeddedButtons: The value of Feedback: must be in parens or angle brackets"]; RETURN; }; object ¬ EBMesaLisp.Parse[s].val; IF NOT ISTYPE[object, LIST OF REF] THEN { Error["EmbeddedButtons: The value of Feedback: must be a list"]; RETURN; }; head ¬ LIST[NIL]; tail ¬ head; FOR l: LIST OF REF ¬ NARROW[object], l.rest UNTIL l = NIL DO first: LIST OF REF; IF NOT ISTYPE[l.first, LIST OF REF] THEN { Error["EmbeddedButtons: Entries in a feedback table must be event-action pairs"]; RETURN; }; first ¬ NARROW[l.first]; IF NOT ISTYPE[first.first, ATOM] AND NOT ISTYPE[first.first, LIST OF REF] THEN { Error["EmbeddedButtons: Invalid event in feedback table"]; RETURN; }; IF NOT ISTYPE[first.rest.first, LIST OF REF] THEN { Error["EmbeddedButtons: Invalid action in feedback table"]; RETURN; }; tail.rest ¬ LIST[NEW[FeedbackTableEntryRec ¬ [ event: IF ISTYPE[first.first, ATOM] THEN LIST[first.first] ELSE NARROW[first.first], action: NARROW[first.rest.first]]]]; tail ¬ tail.rest; ENDLOOP; f­ ¬ head.rest; }; ConfirmButtonInfo: PROC [buttonInfo: ButtonInfo] RETURNS [success: BOOL ¬ TRUE] = { IF buttonInfo.private.fieldValuesOK THEN { CodeTimer.StartInt[$ConfirmButtonInfoQuick, $EmbeddedButtons]; success ¬ TRUE; IF buttonInfo.private.instanceData = NIL THEN { buttonInfo.private.instanceData ¬ buttonInfo.private.class.instantiate[ buttonInfo: buttonInfo, language: buttonInfo.private.language, languageVersion: buttonInfo.private.languageVersion]; }; CodeTimer.StopInt[$ConfirmButtonInfoQuick, $EmbeddedButtons]; } ELSE { CodeTimer.StartInt[$ConfirmButtonInfo, $EmbeddedButtons]; success ¬ ConfirmSymbols[buttonInfo]; IF NOT success THEN {CodeTimer.StopInt[$ConfirmButtonInfo, $EmbeddedButtons]; RETURN}; IF buttonInfo.private.instanceData = NIL THEN { buttonInfo.private.instanceData ¬ buttonInfo.private.class.instantiate[ buttonInfo: buttonInfo, language: buttonInfo.private.language, languageVersion: buttonInfo.private.languageVersion]; }; CodeTimer.StopInt[$ConfirmButtonInfo, $EmbeddedButtons]; }; }; ConfirmFields: PROC [buttonInfo: ButtonInfo] RETURNS [success: BOOL ¬ TRUE] = { buttonClassRope: ROPE; buttonClassName: ATOM; stream: IO.STREAM; languageName: ROPE; IF buttonInfo.private.class # NIL THEN RETURN; -- field names are OK CodeTimer.StartInt[$ConfirmFields, $EmbeddedButtons]; stream ¬ IO.RIS[buttonInfo.private.rope]; languageName ¬ EBMesaLisp.ReadWWord[stream]; BEGIN IF NOT Rope.Equal[languageName, "Poppy1"] THEN { Error[Rope.Concat["Unknown button language: ", languageName]]; CodeTimer.StopInt[$ConfirmFields, $EmbeddedButtons]; RETURN[FALSE]; }; buttonInfo.private.language ¬ $Poppy; buttonInfo.private.languageVersion ¬ 1; [buttonInfo.private.symbols, buttonInfo.private.order] ¬ EBLanguage.PoppyParseFieldNames[stream]; buttonClassRope ¬ EBLanguage.GetFieldRope[buttonInfo.private.symbols, $Class]; buttonClassName ¬ Atom.MakeAtom[buttonClassRope]; buttonInfo.private.class ¬ LookupButtonClass[buttonClassName]; IF buttonInfo.private.class = NIL THEN GOTO NoSuchClass; buttonInfo.private.fieldValuesOK ¬ FALSE; EXITS NoSuchClass => { IF NOT buttonInfo.private.hasComplained THEN { Error[Rope.Concat["Button class not registered: ", buttonClassRope], Rope.Concat["The button class for this button has not been registered: ", buttonClassRope]]; buttonInfo.private.hasComplained ¬ TRUE; }; CodeTimer.StopInt[$ConfirmFields, $EmbeddedButtons]; RETURN[FALSE]; }; END; CodeTimer.StopInt[$ConfirmFields, $EmbeddedButtons]; }; ConfirmSymbols: PROC [buttonInfo: ButtonInfo] RETURNS [success: BOOL ¬ TRUE] = { IF buttonInfo.private.fieldValuesOK THEN RETURN; -- field names and values are OK CodeTimer.StartInt[$ConfirmSymbols, $EmbeddedButtons]; IF buttonInfo.private.class = NIL THEN { -- field names and values are both needed buttonClassRope: ROPE; buttonClassName: ATOM; stream: IO.STREAM ¬ IO.RIS[buttonInfo.private.rope]; languageName: ROPE ¬ EBMesaLisp.ReadWWord[stream]; BEGIN IF NOT Rope.Equal[languageName, "Poppy1"] THEN { Error[Rope.Concat["Unknown button language: ", languageName]]; CodeTimer.StopInt[$ConfirmSymbols, $EmbeddedButtons]; RETURN[FALSE]; }; buttonInfo.private.language ¬ $Poppy; buttonInfo.private.languageVersion ¬ 1; [buttonInfo.private.symbols, buttonInfo.private.order] ¬ EBLanguage.PoppyParse[stream]; buttonClassRope ¬ EBLanguage.GetFieldRope[buttonInfo.private.symbols, $Class]; buttonClassName ¬ Atom.MakeAtom[buttonClassRope]; buttonInfo.private.class ¬ LookupButtonClass[buttonClassName]; IF buttonInfo.private.class = NIL THEN GOTO NoSuchClass; EXITS NoSuchClass => { IF NOT buttonInfo.private.hasComplained THEN { Error[Rope.Concat["Button class not registered: ", buttonClassRope], Rope.Concat["The button class for this button has not been registered: ", buttonClassRope]]; buttonInfo.private.hasComplained ¬ TRUE; }; CodeTimer.StopInt[$ConfirmSymbols, $EmbeddedButtons]; RETURN[FALSE]; }; END; } ELSE { EBLanguage.PoppyParseFieldValues[buttonInfo.private.symbols]; }; buttonInfo.private.fieldValuesOK ¬ TRUE; CodeTimer.StopInt[$ConfirmSymbols, $EmbeddedButtons]; }; LookupButtonClass: PROC [buttonClassName: ATOM] RETURNS [buttonClass: ButtonClass ¬ NIL] = { found: BOOL; b: REF ANY; [found, b] ¬ RefTab.Fetch[classTable, buttonClassName]; IF NOT ISTYPE[b, ButtonClass] THEN { Error["EmbeddedButtons: Internal error. Button class is wrong type (LookupButtonClass).", ]; RETURN; }; buttonClass ¬ NARROW[b]; }; MarkButtonAsChanged: PUBLIC PROC [buttonInfo: ButtonInfo] = { buttonInfo.private.changed ¬ TRUE; buttonInfo.doc.private.class.setRef[$ButtonData, buttonInfo.button, buttonInfo.doc, buttonInfo, TRUE]; }; classTable: RefTab.Ref ¬ RefTab.Create[]; registeredNotifyTable: RefTab.Ref ¬ RefTab.Create[]; EBLanguage.RegisterFieldParseProc[$Feedback, ParseFeedbackField]; EBLanguage.RegisterFieldUnparseProc[$Feedback, UnparseFeedbackField]; END. ψ EBImpl.mesa Copyright Σ 1988, 1989, 1990, 1992 by Xerox Corporation. All rights reserved. Goodisman, August 15, 1989 5:10:44 pm PDT Kenneth A. Pier, October 30, 1990 5:15 pm PST Bier, November 10, 1992 10:48 am PST Doug Wyatt, April 21, 1992 11:25 am PDT Contents: The implementation of EmbeddedButtons, A framework for adding standardized behaviors to any application that is willing to support a set of button objects and implement the behavior access routines. Error Reporting The $EmbeddedButtons is set up by ButtonApplicationsImpl's initialization. IF fileMessage = NIL THEN fileMessage ¬ screenMessage; SimpleFeedback.Append[$EmbeddedButtons, oneLiner, $InternalError, fileMessage]; Used By Editors HandleRep: PUBLIC TYPE ~ UserInputPrivate.Rep; -- used only by MouseCoords routine below RETURN[event.handle.mousePosition]; Have button class unparse fields into the symbol table. Now unparse the symbol table. PROC [stream: IO.STREAM] RETURNS [val: REF] For registration with EBLanguage.RegisterFieldParseProc. PROC[val: REF] RETURNS [rope: ROPE]; For registration with EBLanguage.RegisterFieldUnparseProc. "table" will be computed when the button is used. Used By Button Classes Should change to include button for traceback. Usually, when a button is not connected to any application, it will exhibit some default behavior when clicked so that the user knows it is working. Radio Buttons, for instance, change their state when clicked, as a default behavior. Process.Detach[FORK ForkedEvent[event, buttonInfo, targets]]; -- unfortunately this causes synchronization problems with Tioga (sigh), Bier February 19, 1990 Search the table for the most specific match to this event. Returns TRUE iff all REFs of a (or their equivalents, see ExactMatch) are also in b. 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. Get the feedback table feedbackEvent may come from several places including (1) Translating raw actions through ButtonClassesCommon.tip which produces the atoms Up, Down, Red, Yellow, Blue, MouseMoved and produces mouse coordinates. (2) The SetValueProc of a given ButtonClass. For example, Multi-state buttons send ($Value, ). Radio buttons send ($State, ) or ($StateStill, ). Used By Feedback Procedures Otherwise, we have a list like (1 ApplyLook ) or like . Evaluate as needed and pass to the editor once. Used By Applications Tries to find the value in the table of recently queried values. Otherwise, searches the document. In the current implementation, only value $Value is cached. Other values require searching the document. PROC [button: ActiveButton, doc: ActiveDoc] RETURNS [done: BOOL _ FALSE] Convenience Routines Gets the rope corresponding to the key in the specification of the button. Gets the atom corresponding to the key in the specification of the button. Gets the ref corresponding to the key in the specification of the button. Gets the rope corresponding to the key in the specification of the button. Gets the atom corresponding to the key in the specification of the button. Gets the ref corresponding to the key in the specification of the button. Internal Routines Pick apart the ButtonData into fields, making an entry in "symbols" for each field. Don't parse the value section of each field. We expect ButtonData to be in the form: Poppy1 Class: : : etc. Pick apart the ButtonData into fields, making an entry in "symbols" for each field. Parse the value section of each field. We expect ButtonData to be in the form: Poppy1 Class: : : etc. Field names are OK. Just parse the values. Tell the editor that the document has changed by setting the ButtonData property on the button Initialization $ButtonFeedback is an obsolete property. Where still used, this property is in the following format: buttonFeedback _ "languageVersion ( feedbackTable )" languageVersion _ 19890713 feedbackTable _ feedbackTableEntry | feedbackTableEntry feedbackTable feedbackTableEntry _ ( event entryActions ) event _ userEvent | applicationEvent | ( qualifiersAndEvent ) | ( applicationEvents ) userEvent _ ATOM [usually $Up or $Down] applicationEvent _ ATOM applicationEvents _ applicationEvent | applicationEvent ApplicationEvents qualifiersAndEvent _ qualifier | qualifier qualifiersAndEvent | userEvent | userEvent qualifiersAndEvent qualifier _ ATOM [usually $Red, $Control, $Shift, etc.] entryActions _ entryAction | ( entryActionList ) entryActionList _ entryAction | entryAction entryActionList entryAction _ action | handlerAction handlerAction _ ( handler handlerParameters ) handler _ ATOM [the name of the handler] handlerParameters _ handlerParameter | handlerParameter handlerParameters handlerParameter _ value value _ ATOM, BOOL, INT, REAL, or ROPE [a button value] action _ ROPE [editor specific formatted feedback ROPE] Κ(K•NewlineDelimiter –"cedarcode" style™codešœ ™ Kšœ ΟeœB™NKšœ)™)K™-K™$K™'K˜KšΟnœΙ™ΡK™—šΟk ˜ Jš œŠŸœœ œ œ œœœœœœ œj˜δK˜—šžœŸœŸœΟc˜)JšŸœ*Ÿœ}˜°KšŸœHŸ˜UK˜KšœŸœ˜*Kšœ Ÿœ˜$Kšœ Ÿœ˜0Kšœ Ÿœ˜&KšœŸœ˜,KšœŸœŸœ(˜IKšœŸœ˜1KšŸœŸœŸœ˜Kšœ ŸœŸœ ˜!Kšœ Ÿœ˜+KšœŸœ!˜4KšœŸœ˜$KšœŸœŸœ˜1KšœŸœŸœŸ˜@—K˜IheadšΟx™š žœŸœŸœŸœŸœŸœ˜EK™JKšŸœŸœŸœ™6K˜MK™OK˜—Lš‘™KšœŸœ˜*KšœŸœ˜0KšœŸ œ'˜GKšœŸœ˜Kšœ ŸœŸœ ˜8KšœŸœ˜ Kšœ ŸœŸœ )™XK˜šžœŸ œ ŸœŸœ˜jK˜(K˜*K˜šœŸœ˜#K˜šœ Ÿœ˜$K˜K˜K˜——K˜K˜—K˜š ž œŸœŸœŸœœ˜DKšŸœ˜K˜K˜—šžœŸœŸœ ŸœŸœŸœŸœ Ÿœ˜Dš ŸœŸœŸœ/Ÿœ ŸœŸœ˜XšŸœŸœŸ˜!šœŸœŸœŸœŸ˜7K˜CKšŸœŸœ˜)K˜—KšŸœ˜—K˜—K˜K˜—š ž œŸœŸœŸœŸœ˜9JšŸœŸœ˜Kšœ˜K™—K˜2K˜2˜2K˜—š ž œŸœŸœŸœŸœŸœ˜BšŸœŸ˜Kšœ.ŸœŸœ˜;KšŸœ˜—K˜K˜—š ž œŸœŸœŸœŸœŸœ˜BšŸœŸ˜˜K˜ršŸœŸœŸœŸ˜šŸœ Ÿ˜KšœŸœŸœ˜*KšŸœ˜—KšŸœ˜—K˜—Kšœ.ŸœŸœ˜;KšŸœ˜—Kšœ˜K™—š ž œŸœŸœŸœŸœ˜9šŸœ˜Kšœ6Ÿœ˜:Kšœ6Ÿœ˜:K˜5K˜—Kšœ˜K˜—šž œŸœŸœŸœ˜MKšŸœ™#KšŸœ0˜6Kšœ˜K˜—šž œŸœŸœ9˜QK˜4šŸœ ŸœŸœ˜K˜.š ŸœŸœŸœŸœŸœ ˜IK˜F—K˜—K˜K˜—˜[K˜—šž œŸœŸœ*ŸœŸœŸœŸœ˜YKšŸœŸœŸœ˜K˜!K˜KšŸœ6˜˜>Kšœ Ÿœ˜šŸœ#ŸœŸœ˜/šœ"’$œ˜GKšœ˜Kšœ'˜'Kšœ5˜5—K˜—Kšœ=˜=K˜—šŸœ˜Kšœ9˜9Kšœ ’œ ˜%KšŸœŸœ Ÿœ;Ÿœ˜VšŸœ#ŸœŸœ˜/šœ"’$œ˜GKšœ˜Kšœ'˜'Kšœ5˜5—K˜—Kšœ8˜8K˜—K˜K˜—š ž œŸœŸœ ŸœŸœ˜OKšœ™K™'K™LKšœŸœ˜KšœŸœ˜KšœŸœŸ˜KšœŸ˜K™Kš ŸœŸœŸœŸœ ˜DK˜Kšœ5˜5Kšœ ŸœŸœ˜)K˜,šŸ˜šŸœŸœ$Ÿœ˜0Kšœ>˜>Kšœ4˜4KšŸœŸœ˜K˜—K˜%K˜'KšœD’œ ˜aK˜NK˜1Kš’œ’œ#˜>KšŸœŸœŸœŸœ ˜8Kš’!œ’Πbk’˜)šŸ˜˜šŸœŸœ"Ÿœ˜.Kšœ‘˜‘Kšœ#Ÿœ˜(K˜—Kšœ4˜4KšŸœŸœ˜K˜——KšŸœ˜—Kšœ4˜4K˜K˜—š žœŸœŸœ ŸœŸœ˜PKšœ|™|K™'K™LK™KšŸœ"ŸœŸœ  ˜QK˜Kšœ6˜6šŸœŸœŸœ )˜RKšœŸœ˜KšœŸœ˜Kš œŸœŸœŸœŸœ˜4KšœŸœ ˜2šŸ˜šŸœŸœ$Ÿœ˜0Kšœ>˜>Kšœ5˜5KšŸœŸœ˜K˜—K˜%K˜'KšœD’ œ ˜WK˜NK˜1Kš’œ’œ#˜>KšŸœŸœŸœŸœ ˜8šŸ˜˜šŸœŸœ"Ÿœ˜.Kšœ‘˜‘Kšœ#Ÿœ˜(K˜—Kšœ5˜5KšŸœŸœ˜K˜——KšŸœ˜—K˜—šŸœ˜K™+Kšœ=˜=K˜—Kš’!œ’£’˜(Kšœ5˜5K˜K˜—š žœŸœŸœŸœŸœ˜\KšœŸœ˜ KšœŸœŸœ˜ K˜7šŸœŸœŸœŸœ˜$Kšœ]˜]KšŸœ˜K˜—KšœŸœ˜K˜K˜—šžœŸ œ˜=K™^KšœŸœ˜"Kšœ`Ÿœ˜fK˜—Lš‘™K˜)K˜4KšœA˜AK˜EJ™KšŸœ˜K˜™eK™K™4K™K™EK™+K™UK™(K™K™I™K™K™ K™—K™7K™0K™;K™$K™-K™(K™IK™K™7K™7K™K˜K˜—K˜—…—}άΊ