DIRECTORY BiScrollers, CodeTimer, Commander, EBEditors, EBEditorsExtras, EBNullDoc, EBTypes, EmbeddedButtons, Feedback, FeedbackTypes, GGActive, GGAlign, GGBasicTypes, GGControlPanelTypes, GGCoreTypes, GGEmbedTypes, GGEvent, GGEventExtras, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGProps, GGScene, GGSliceOps, GGState, GGUIUtility, GGUserInput, GGWindow, Imager, ImagerTransformation, InputFocus, IO, List, MultiCursors, Rope, ScreenCoordsTypes, TIPUser, ViewerClasses, ViewerOps; GGActiveImpl: CEDAR PROGRAM IMPORTS BiScrollers, CodeTimer, Commander, EBEditors, EBEditorsExtras, EBNullDoc, EmbeddedButtons, Feedback, GGAlign, GGEventExtras, GGMouseEvent, GGMultiGravity, GGProps, GGScene, GGSliceOps, GGState, GGUIUtility, GGUserInput, ImagerTransformation, InputFocus, IO, List, MultiCursors, TIPUser, ViewerOps EXPORTS GGActive, GGInterfaceTypes = BEGIN ActiveDoc: TYPE = EBTypes.ActiveDoc; ActiveButton: TYPE = EBTypes.ActiveButton; BoundBox: TYPE = GGModelTypes.BoundBox; Color: TYPE = Imager.Color; ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes FeatureData: TYPE = GGModelTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; MsgRouter: TYPE = FeedbackTypes.MsgRouter; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; StrokeEnd: TYPE = Imager.StrokeEnd; TIPScreenCoords: TYPE = ScreenCoordsTypes.TIPScreenCoords; TIPScreenCoordsRec: TYPE = ScreenCoordsTypes.TIPScreenCoordsRec; Transformation: TYPE = ImagerTransformation.Transformation; UserInputProc: TYPE = GGEvent.UserInputProc; Viewer: TYPE = ViewerClasses.Viewer; InitPaletteApplication: PROC = { EmbeddedButtons.RegisterApplication[$Palette, PaletteNotify]; }; PaletteNotify: EmbeddedButtons.RegisteredNotifyProc = { ggData: GGData ¬ NARROW[buttonInfo.doc.theDoc]; sliceD: SliceDescriptor ¬ NARROW[buttonInfo.button]; FOR list: LIST OF REF ¬ events, list.rest UNTIL list = NIL DO SELECT list.first FROM $TransferFillColor => [] ¬ GGMouseEvent.SetFillColorRemote[ggData, sliceD]; $TransferStrokeColor => [] ¬ GGMouseEvent.SetStrokeColorRemote[ggData, sliceD]; $TransferBothColors => { [] ¬ GGMouseEvent.SetFillColorRemote[ggData, sliceD]; [] ¬ GGMouseEvent.SetStrokeColorRemote[ggData, sliceD]; }; ENDCASE; ENDLOOP; }; ggActiveDocClass: EBEditors.ActiveDocClass ¬ NEW[EBEditors.ActiveDocClassObj ¬ [ name: $Gargoyle, getRef: GGGetRef, setRef: GGSetRef, mapRef: GGMapRef, getDocName: GGGetDocName, feedback: GGButtonFeedback, -- Feedback is the name of an interface inButton: GGInButton ]]; LookupDoc: PUBLIC PROC [ggData: GGData] RETURNS [doc: ActiveDoc] ~ { IF ggData.behavior.activeDoc = NIL THEN { doc ¬ EBEditors.CreateActiveDoc[ggData, ggActiveDocClass]; ggData.behavior.activeDoc ¬ doc; } ELSE doc ¬ NARROW[ggData.behavior.activeDoc] }; NullDoc: PUBLIC PROC [ggData: GGData] RETURNS [doc: ActiveDoc] ~ { doc ¬ EBNullDoc.Create["GGNullDoc"]; [] _ EBNullDoc.CreateBOOLButton[doc, "Midpoints", FALSE]; [] _ EBNullDoc.CreateBOOLButton[doc, "ShowAlignments", FALSE]; [] _ EBNullDoc.CreateBOOLButton[doc, "DoubleBuffer", TRUE]; [] _ EBNullDoc.CreateBOOLButton[doc, "Active", FALSE]; -- scene itself is not active [] _ EBNullDoc.CreateBOOLButton[doc, "Editable", TRUE]; -- scene is editable [] _ EBNullDoc.CreateBOOLButton[doc, "Palette", FALSE]; [] _ EBNullDoc.CreateEnumeratedButton[doc, "ScreenStyle", $SpecifiedFonts, LIST[$SpecifiedFonts, $AlternateFonts, $WYSIWYG]]; [] _ EBNullDoc.CreateBOOLButton[doc, "Gravity", TRUE]; [] _ EBNullDoc.CreateREALButton[doc, "GravityExtent", 25.0]; [] _ EBNullDoc.CreateEnumeratedButton[doc, "GravityType", $PreferPoints, LIST[$PreferLines, $PreferPoints]]; [] _ EBNullDoc.CreateBOOLButton[doc, "Auto", FALSE]; EBNullDoc.Instantiate[doc]; }; <> GGInButton: EBEditors.InButtonProc = { RETURN[TRUE]; }; GGGetDocName: EBEditors.GetDocNameProc ~ { ggData: GGData ¬ NARROW[doc.theDoc]; name ¬ GGState.GetFullName[ggData]; }; GGGetRef: EBEditors.GetRefProc = { sliceD: SliceDescriptor ¬ NARROW[button]; isUnique: BOOL ¬ TRUE; [ref, isUnique] ¬ GGProps.Get[sliceD.slice, sliceD.parts, key]; IF ref = NIL THEN { -- try inheritance ggData: GGData ¬ NARROW[doc.theDoc]; rootSlice: Slice ¬ ggData.rootSlice; [ref, isUnique] ¬ GGProps.Get[rootSlice, NIL, key]; }; }; GGSetRef: EBEditors.SetRefProc = { sliceD: SliceDescriptor ¬ NARROW[button]; GGProps.Put[sliceD.slice, sliceD.parts, key, ref]; }; GGMapRef: EBEditors.MapRefProc = { ForEachButton: PROC [leaf: Slice] RETURNS [done: BOOL ¬ FALSE] = { leafD: SliceDescriptor ¬ GGSliceOps.NewParts[leaf, NIL, slice]; buttonData: REF ¬ GGProps.Get[leaf, leafD.parts, $ButtonData].val; IF buttonData = NIL THEN RETURN; done ¬ mapProc[leafD, doc]; }; ggData: GGData ¬ NARROW[doc.theDoc]; aborted ¬ GGScene.WalkSlices[ggData.scene, leaf, ForEachButton]; }; GGButtonFeedback: EBEditors.FeedbackProc = { ggData: GGData ¬ NARROW[doc.theDoc]; feedbackRef: REF; WITH feedback SELECT FROM rope: Rope.ROPE => feedbackRef ¬ GGUIUtility.ParseFeedbackRope[rope]; ENDCASE => feedbackRef ¬ feedback; WITH feedbackRef SELECT FROM a: ATOM => QueueButtonEvent[LIST[a], ggData, button]; l: LIST OF REF => HandleList[l, ggData, button]; ENDCASE => ERROR; RETURN[feedbackRef]; }; NoOpFileIn: GGProps.FileinProc = { val ¬ IO.GetRope[s]; }; ButtonDataFileOut: GGProps.FileoutProc = { rope: Rope.ROPE; rope ¬ EBEditors.RopeFromButtonData[val]; s.PutRope[rope]; }; ButtonDataFileIn: GGProps.FileinProc = { rope: Rope.ROPE ¬ IO.GetRope[s]; val ¬ EBEditors.ButtonDataFromRope[rope, FALSE]; }; ButtonDataCopy: GGProps.CopyProc = { valAsRope: Rope.ROPE ¬ EBEditors.RopeFromButtonData[val]; copy ¬ EBEditors.ButtonDataFromRope[valAsRope, FALSE]; }; GargoyleHandler: EmbeddedButtons.RegisteredNotifyProc = { viewer: ViewerClasses.Viewer; inputFocus: InputFocus.Focus; inputFocus ¬ InputFocus.GetInputFocus[]; IF inputFocus # NIL THEN { viewer ¬ inputFocus.owner; IF viewer # NIL AND viewer.class.flavor = $ActionArea THEN { ggData: GGData ¬ NARROW[BiScrollers.ClientDataOfViewer[viewer]]; HandleList[events, ggData, buttonInfo.button]; }; }; }; GargoyleAsControlPanelHandler: EmbeddedButtons.RegisteredNotifyProc = { ggData: GGData ¬ NARROW[buttonInfo.doc.theDoc]; HandleList[events, ggData, buttonInfo.button]; }; ControlPanelButtonHandler: PUBLIC PROC [ggData: GGData, events: LIST OF REF, buttonInfo: EBTypes.ButtonInfo] = { HandleList[events, ggData, buttonInfo.button]; }; TransferButtonDashes: UserInputProc = { sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; dashed: BOOL ¬ FALSE; pattern: SequenceOfReal ¬ NIL; offset, length: REAL ¬ 0.0; isUnique: BOOL ¬ TRUE; targetGGData: GGData ¬ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [dashed, pattern, offset, length, isUnique] ¬ GGSliceOps.GetDashed[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetDashed[ggData, dashed, pattern, offset, length]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonDashes failed: the button has multiple dash patterns"]; }; TransferButtonStrokeWidth: UserInputProc = { sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; strokeWidth: REAL; isUnique: BOOL ¬ TRUE; targetGGData: GGData ¬ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [strokeWidth, isUnique] ¬ GGSliceOps.GetStrokeWidth[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetStrokeWidth[ggData, strokeWidth]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonStrokeWidth failed: the button has multiple dash patterns"]; }; TransferButtonStrokeEnd: UserInputProc = { sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; strokeEnd: StrokeEnd; isUnique: BOOL ¬ TRUE; targetGGData: GGData ¬ GGState.GetGGInputFocus[]; IF targetGGData = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetStrokeColorRemote failed: Place input focus in a Gargoyle viewer"]; RETURN; }; [strokeEnd, isUnique] ¬ GGSliceOps.GetStrokeEnd[sliceD.slice, sliceD.parts]; IF isUnique THEN { GGEventExtras.SetStrokeEnd[ggData, strokeEnd]; } ELSE Feedback.Append[targetGGData.router, oneLiner, $Complaint, "TransferButtonStrokeEnd failed: the button has multiple dash patterns"]; }; ActiveInputHandler: PUBLIC PROC [self: Viewer, ggData: GGData, input: LIST OF REF, notify: PROC [LIST OF REF]] = { event: EBTypes.Event; IF ggData.embed.beingBorn THEN RETURN; -- don't handle input while window is being built CodeTimer.StartInt[$RawInputNotify, $Gargoyle]; IF EBEditors.ValidEvent[event ¬ EBEditors.GetEvent[input]] THEN { -- raw input bs: BiScrollers.BiScroller ~ BiScrollers.QuaBiScroller[self]; mouseAction: BOOL ~ EBEditors.MouseAction[event]; mouseAllUp: BOOL ~ EBEditors.MouseAllUp[event]; button: ActiveButton ¬ NIL; -- will be set to the active button, if any doc: ActiveDoc ¬ NIL; IF GGState.GetActive[ggData] -- activity enabled AND ggData.behavior.rawInputMode=$None -- not in $Gargoyle mode AND mouseAction -- it's some kind of mouse action THEN { -- test for an active button at the cursor position GetMousePosition: PROC [mc: TIPScreenCoordsRec] RETURNS [mouseGG: Point] ~ { viewerToClient: Transformation ~ bs.class.style.GetTransforms[bs].viewerToClient; mouseBiScroller: TIPScreenCoords ~ NEW[TIPScreenCoordsRec ¬ mc]; v: Viewer; inClient: BOOL; [v, inClient] ¬ ViewerOps.MouseInViewer[mouseBiScroller]; IF NOT inClient THEN Feedback.PutFL[ggData.router, oneLiner, $Debug, "GG: %g,%g; ", LIST[[integer[mouseBiScroller.mouseX]], [integer[mouseBiScroller.mouseY]]] ]; mouseGG ¬ ImagerTransformation.Transform[m: viewerToClient, v: [mouseBiScroller.mouseX, mouseBiScroller.mouseY]]; }; mouseGG: Point ~ GetMousePosition[EBEditors.MouseCoords[event]]; feature: FeatureData; IF EBEditorsExtras.MouseMotion[event] AND EBEditors.MouseAllUp[event] THEN { feature ¬ NIL; } ELSE { feature ¬ GGMultiGravity.FacesPreferred[mouseGG, 18.0, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData].feature; }; IF feature#NIL THEN { doc ¬ LookupDoc[ggData]; WITH feature.shape SELECT FROM sliceD: SliceDescriptor => { buttonData: REF ~ GGGetRef[$ButtonData, sliceD, doc]; IF buttonData#NIL THEN button ¬ sliceD; -- found an active button }; ENDCASE; }; }; IF button#NIL THEN { EBEditors.HandleEvent[event, button, doc]; } ELSE { IF GGState.GetReadOnly[ggData] THEN { IF MultiCursors.GetACursor[NIL]#textPointer THEN MultiCursors.SetACursor[textPointer, NIL]; } ELSE { result: LIST OF REF ¬ EBEditors.ParseEvent[ggData.tipTable, event]; result _ CopyEventCoordinates[result]; IF mouseAction THEN { cursorType: MultiCursors.CursorType; cursorType ¬ ggData.controls.cursor; IF cursorType#MultiCursors.GetACursor[NIL] THEN MultiCursors.SetACursor[cursorType, NIL]; }; IF NOT mouseAllUp THEN ggData.behavior.rawInputMode ¬ $Gargoyle; IF result#NIL THEN notify[result]; }; }; IF mouseAllUp THEN ggData.behavior.rawInputMode ¬ $None; } ELSE notify[input]; CodeTimer.StopInt[$RawInputNotify, $Gargoyle]; }; CopyEventCoordinates: PROC [event: LIST OF REF] RETURNS [newEvent: LIST OF REF _ NIL] = { tail: LIST OF REF _ NIL; FOR list: LIST OF REF _ event, list.rest UNTIL list = NIL DO IF tail = NIL THEN tail _ newEvent _ CONS[list.first, NIL] ELSE tail _ tail.rest _ CONS[list.first, NIL]; WITH list.first SELECT FROM z: TIPScreenCoords => tail.first _ NEW [Imager.VEC _ [z.mouseX, z.mouseY] ]; ENDCASE; ENDLOOP; }; QueueButtonEvent: PROC [event: LIST OF REF, ggData: GGData, button: REF] = { newEvent: LIST OF REF; IF event.first = $SelectButton OR event.first = $BeginButton OR event.first = $TransferButtonDashes OR event.first = $TransferButtonStrokeWidth OR event.first = $TransferButtonStrokeEnd THEN { sliceD: SliceDescriptor ¬ NARROW[button]; newEvent ¬ LIST[event.first, sliceD]; -- make a copy of the eventList, because EmbeddedButtons resuses the original } ELSE IF event.first = $ButtonFillColorFromIntensity OR event.first = $ButtonStrokeColorFromIntensity THEN { sliceD: SliceDescriptor ¬ NARROW[button]; newEvent ¬ LIST[event.first, event.rest.first, sliceD]; -- make a copy of the eventList, because EmbeddedButtons resuses the original } ELSE newEvent ¬ List.Append[event]; -- make a copy of the eventList, because EmbeddedButtons resuses the original GGUserInput.EventNotify[ggData, newEvent]; }; HandleList: PROC [list: LIST OF REF, ggData: GGData, button: REF] = { WITH list.first SELECT FROM l: LIST OF REF => { FOR list ¬ list, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM childList: LIST OF REF => HandleList[childList, ggData, button]; ENDCASE => SIGNAL SyntaxError[msg: "Input list mixes LIST elements and ATOM elements"]; ENDLOOP; }; ENDCASE => QueueButtonEvent[list, ggData, button]; }; SyntaxError: PUBLIC SIGNAL[msg: Rope.ROPE] = CODE; GGActiveHandler: Commander.CommandProc = {}; ggActionAreaClass: ViewerClasses.ViewerClass ~ ViewerOps.FetchViewerClass[$ActionArea]; ggActionAreaClass.tipTable ¬ TIPUser.TransparentTIPTable[]; -- all viewers made after this call will be able to be active GGProps.Register[$ButtonData, ButtonDataFileIn, ButtonDataFileOut, ButtonDataCopy]; InitPaletteApplication[]; GGUserInput.RegisterAction[$TransferButtonDashes, TransferButtonDashes, none]; GGUserInput.RegisterAction[$TransferButtonStrokeWidth, TransferButtonStrokeWidth, none]; GGUserInput.RegisterAction[$TransferButtonStrokeEnd, TransferButtonStrokeEnd, none]; Commander.Register[key: "GGActive", proc: GGActiveHandler, doc: "Does nothing. Active Gargoyle is already loaded", clientData: NIL, interpreted: TRUE]; EmbeddedButtons.RegisterApplication[$Gargoyle, GargoyleHandler]; EmbeddedButtons.RegisterApplication[$GargoyleAsControlPanel, GargoyleAsControlPanelHandler]; END. Œ GGActiveImpl.mesa Copyright Ó 1988, 1989, 1991, 1992 by Xerox Corporation. All rights reserved. Contents: Routines that support embedded buttons within Gargoyle. Kenneth A. Pier, October 1, 1992 2:02 pm PDT Bier, March 16, 1993 3:01 pm PST Doug Wyatt, April 20, 1992 12:24 pm PDT Color Palette Mini-Application PROC[events: LIST OF REF, buttonInfo: ButtonInfo]; ActiveDoc Class Gargoyle does us a favor here. When a new file is loaded into a viewer, ggData.behavior.activeDoc _ NIL. Thus, we can tell if we need to create a new activeDoc. How do we find out what buttons and variables should be added? Hard code? User profile? Track GGStateImpl?. KAP. September 30, 1992. PROC [button: ActiveButton, doc: ActiveDoc, x, y: INTEGER] RETURNS [BOOL]; scale: REAL _ GGState.GetScaleUnit[ggData]; Feedback.PutF[ggData.router, oneLiner, $DuringMouse, "Cursor on button at [%g, %g]", [real[pt.x/scale]], [real[pt.y/scale]] ]; PROC [button: ActiveButton, doc: ActiveDoc, x, y: INTEGER] RETURNS [BOOL]; PROC [doc: ActiveDoc] RETURNS [name: ROPE]; PROC [key: ATOM, button: ActiveButton, doc: ActiveDoc] RETURNS [ref: REF]; PROC [key: ATOM, button: ActiveButton, doc: ActiveDoc, ref: REF]; PROC [doc: ActiveDoc, docClass: ActiveDocClass, mapProc: EachButtonProc]; PROC[button: ActiveButton, doc: ActiveDoc, feedback: REF] RETURNS [REF]; Property support PROC [s: STREAM] RETURNS [val: REF]; PROC [s: STREAM, val: REF] RETURNS [vf: ValFormat _ delimited]; PROC [s: STREAM] RETURNS [val: REF]; PROC [val: REF] RETURNS [copy: REF]; Gargoyle as a Button Application An obsolete comment: However, if the combination $EBApplications $GetKeyValue $ occurs, the named value is retrieved from the active document. This allows a button to base its operation on any of the values in the document. PROC [events: LIST OF REF, buttonInfo: ButtonInfo]; events will be a list of Gargoyle actions, such as (LineWidth "2.3"). PROC [events: LIST OF REF, buttonInfo: ButtonInfo]; events will be a list of Gargoyle actions, such as (LineWidth "2.3"). Send the event to the named ggData, but extract any needed info from the named buttonInfo to prepare the event. PROC [ggData: GGData, event: LIST OF REF]; PROC [ggData: GGData, event: LIST OF REF]; PROC [ggData: GGData, event: LIST OF REF]; Notify procedures If the cursor is moving, then do nothing (for now). This will help EmbeddedButtons keep up on large complex illustrations. Here is the plan: Use UserInputOpsExtras2.GetPosition: If the coordinates of this event are the most recent coordinates TIP has seen, then check to see if the cursor is over a button and update the cursor pattern accordingly. Otherwise, ignore this event. cursorCoords, latestCursorCoords: Point ¬ [0,0]; handle: UserInput.Handle ¬ EBEditorsExtras.GetHandle[event]; cursorCoords ¬ UserInputOpsExtras2.GetPosition[handle]; latestCursorCoords ¬ UserInputOpsExtras2.GetLatestPosition[handle]; IF PointsEqual[latestCursorCoords, cursorCoords] THEN feature ¬ GGMultiGravity.FacesPreferred[mouseGG, 18.0, GGAlign.emptyAlignBag, ggData.hitTest.sceneBag, ggData].feature ELSE feature ¬ NIL; Ignore this gargoyle action and display the "read-only" cursor, currently textPointer The only other SetACursor call in Gargoyle is in GGWindowImpl.SetCursorLooks. This copies the list, substituting REF Imager.VEC for TIPScreenCoords. This is useful and also avoids the problem of TIP overwritting the TIPScreenCoords storage! Initialization Ê2–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ ÏeœC™NKšÏbœ:™BK™,K™ K™'K˜—šÏk ˜ Jšœ©ŸœQ˜üK˜—šÏn œŸœŸ˜JšŸœÿŸœ(˜°KšŸœŸ˜+K˜Kšœ Ÿœ Ÿœ ˜$KšœŸœ Ÿœ ˜*Kšœ Ÿœ˜'KšœŸœ˜Kšœ Ÿ œ$Ïc˜[KšœŸœŸœ¡˜VKšœ Ÿœ˜-KšœŸœ˜'KšœŸœ˜1Kšœ Ÿœ˜*KšœŸœ˜!KšœŸœ˜!KšœŸœ˜2KšœŸœ˜!KšœŸœ ˜5Kšœ Ÿœ˜#KšœŸœ%˜:KšœŸœ(˜@KšœŸœ'˜;KšœŸœ˜,KšœŸœ˜$—Idefault™L™š œŸœ˜ Kšœ=˜=K˜K˜—š  œ*˜7KšŸœ ŸœŸœŸœ™2KšœŸœ˜/KšœŸœ˜4š ŸœŸœŸœŸœŸœŸœŸ˜=šŸœ Ÿ˜K˜KK˜Ošœ˜K˜5K˜7K˜—KšŸœ˜—KšŸœ˜—K˜—L™L™šœ-Ÿœ ˜PKšœ˜K˜K˜K˜Kšœ˜Kšœ¡'˜CK˜K˜K˜—š  œŸ œŸœ˜DKšœeŸœ:™¢šŸœŸœŸœ˜)K˜:K˜ K˜—KšŸœŸœ˜,K˜K˜—šžœŸ œŸœ˜BK˜$K™K™‡Kšœ2Ÿœ˜9Kšœ7Ÿœ˜>Kšœ5Ÿœ˜;Kšœ/Ÿœ¡˜TKšœ1Ÿœ¡˜LKšœ0Ÿœ˜7KšœKŸœ.˜}Kšœ0Ÿœ˜6K˜K˜2K˜eKš Ÿœ ŸœŸœŸœŸœ˜#šŸœ˜KšœŸœ ™+šœT™TKšœ)™)—KšŸœŸœ˜ K˜—K˜Kš ˜K˜—š  œ˜&KšŸœ.ŸœŸœŸœ™JKšŸœŸœ˜ K˜K˜—š  œ˜*KšŸœŸœŸœ™+KšœŸœ ˜$K˜#K˜K˜—š œ˜"KšŸœŸœ(ŸœŸœ™JKšœŸœ ˜)Kšœ ŸœŸœ˜K˜?šŸœŸœŸœ¡˜&KšœŸœ ˜$K˜$Kšœ)Ÿœ˜3K˜—K˜—š œ˜"KšŸœŸœ-Ÿœ™AKšœŸœ ˜)Kšœ2˜2K˜—š œ˜"KšŸœE™Iš   œŸœŸœŸœŸœ˜BKšœ3Ÿœ ˜?Kšœ Ÿœ3˜BKšŸœŸœŸœŸœ˜ K˜Kšœ˜—KšœŸœ ˜$K˜@K˜K˜—š œ˜,KšŸœ1ŸœŸœŸœ™HKšœŸœ ˜$Kšœ Ÿœ˜šŸœ ŸœŸ˜Kšœ Ÿœ6˜EKšŸœ˜"—šŸœ ŸœŸ˜KšœŸœŸœ˜5KšœŸœŸœŸœ"˜0KšŸœŸœ˜—KšŸœ˜K˜—K˜L™š  œ ž œ˜"KšŸœŸœŸœŸœ™$KšœŸœ ˜K˜K˜—š œ ž œ˜*KšŸœŸœŸœŸœ™?Kšœ Ÿœ˜K˜)Kšœ˜K˜K˜—š œ ž œ˜(KšŸœŸœŸœŸœ™$Kšœ ŸœŸœ ˜ K˜Kšœ)Ÿœ˜0K˜K˜—š œ˜$KšŸœŸœŸœŸœ™$KšœŸœ%˜9Kšœ/Ÿœ˜6K˜—K˜™ Kšœñ™ñK™—š œ*˜9KšŸœ ŸœŸœŸœ™3KšœG™GK˜K˜K˜K˜(šŸœŸœŸœ˜K˜šŸœ ŸœŸœ#Ÿœ˜¢ œ¢ œ ˜qKšœ˜—K˜@K˜šŸœ$ŸœŸœ˜LK™{Kšœ Ÿœ˜K™6K™ÉK™0K™