DIRECTORY EditSpan USING [ChangeLooks, Delete, Insert, Split], InputFocus USING [CaptureButtons, ReleaseButtons], Interminal USING [], Menus USING [MouseButton], MessageWindow USING [Append, Blink], NodeProps USING [DoSpecs], Process USING [Detach], Rope USING [Cat, Fetch, IsEmpty, Length, MaxLen, ROPE, Substr], TEditDisplay USING [InvalidateBranch], TEditDocument USING [Selection, TEditDocumentData], TEditDocumentPrivate USING [DoLoadFile], TEditSelectionPrivate USING [ResolveToChar], TEditSelection USING [Alloc, Free], TextEdit USING [InsertRope, PutProp], TextLooks USING [Looks, RopeToLooks], TextNode USING [LastChild, Location, MaxLen, Ref, Span], TiogaButtons USING [TiogaButton, TiogaButtonList, TiogaButtonProc, TiogaButtonRec], TiogaButtonsExtra, TiogaExtraOps USING [RemProp], TiogaFileOps USING [AddLooks, Ref, SetFormat], TiogaOps USING [CancelSelection, FirstChild, GetProp, GetRope, LastWithin, Location, Lock, Next, PutProp, Ref, Root, SetSelection, StepForward, Unlock, ViewerDoc], TIPUser USING [TIPScreenCoords, TIPTable], ViewerClasses USING [InitProc, Lock, NotifyProc, Viewer, ViewerClass, ViewerClassRec, ViewerRec], ViewerOps USING [AddProp, CreateViewer, FetchProp, FetchViewerClass, MouseInViewer, PaintViewer, RegisterViewerClass]; TiogaButtonsImpl: CEDAR PROGRAM IMPORTS EditSpan, InputFocus, MessageWindow, NodeProps, Process, Rope, TEditDisplay, TEditDocumentPrivate, TEditSelection, TEditSelectionPrivate, TextEdit, TextLooks, TextNode, TiogaExtraOps, TiogaFileOps, TiogaOps, ViewerOps EXPORTS TiogaButtons, TiogaButtonsExtra ~ BEGIN OPEN TiogaButtons; ROPE: TYPE ~ Rope.ROPE; NodeItself: INT ~ -1; CreateViewer: PUBLIC PROC [info: ViewerClasses.ViewerRec] RETURNS [v: ViewerClasses.Viewer] ~ { v _ ViewerOps.CreateViewer[flavor: $TiogaButtons, info: info]; }; LoadViewer: PUBLIC PROC [viewer: ViewerClasses.Viewer, fileName: ROPE] ~ { name: ROPE; IF viewer = NIL OR viewer.class.flavor # $TiogaButtons THEN ERROR WrongViewerClass; name _ viewer.name; -- DoLoadFile will change this on us, so save it first [] _ TEditDocumentPrivate.DoLoadFile[parent: viewer, fileName: fileName]; viewer.tipTable _ viewer.class.tipTable; -- DoLoadFile does violence to this too! viewer.name _ name; ViewerOps.PaintViewer[viewer: viewer, hint: caption]; }; CreateButtonForEachNode: PUBLIC PROC [viewer: ViewerClasses.Viewer, firstLevelOnly: BOOL _ FALSE, subtreeAsButton: BOOL _ TRUE, proc: TiogaButtonProc _ NIL, clientData: REF ANY _ NIL, fork: BOOLEAN _ TRUE] ~ { root: TiogaOps.Ref _ TiogaOps.ViewerDoc[viewer]; LockedCreateButtonForEachNode: PROC [root: TiogaOps.Ref] ~ { node: TiogaOps.Ref _ TiogaOps.FirstChild[root]; WHILE node # NIL DO button: TiogaButton ~ NEW[TiogaButtonRec _ [ startLoc: [node, NodeItself], endLoc: [IF subtreeAsButton THEN TiogaOps.LastWithin[node] ELSE node, NodeItself], proc: proc, clientData: clientData, fork: fork] ]; IF proc # NIL THEN AddButtonProp[node, button] ELSE RemButtonProp[node]; IF firstLevelOnly THEN node _ TiogaOps.Next[node] ELSE node _ TiogaOps.StepForward[node]; ENDLOOP; }; CallWithLock[LockedCreateButtonForEachNode, root] }; CreateButton: PUBLIC PROC [viewer: ViewerClasses.Viewer, rope: ROPE _ NIL, format: ROPE _ NIL, looks: ROPE _ NIL, proc: TiogaButtonProc _ NIL, clientData: REF ANY _ NIL, fork: BOOLEAN _ TRUE] RETURNS [button: TiogaButton] ~ { IF viewer = NIL OR viewer.class.flavor # $TiogaButtons THEN ERROR WrongViewerClass; { root: TiogaOps.Ref _ TiogaOps.ViewerDoc[viewer]; LockedCreateButton: PROC [root: TiogaOps.Ref] ~ { node: TiogaOps.Ref ~ LockedCreateNode[root, rope, viewer]; IF NOT format.IsEmpty THEN SetFormat[node, format]; IF NOT looks.IsEmpty THEN FOR i: INT IN [0..looks.Length) DO AddLooks[node, 0, TextNode.MaxLen, looks.Fetch[i], root]; ENDLOOP; button _ NEW[TiogaButtonRec _ [startLoc: [node, NodeItself], endLoc: [node, NodeItself], proc: proc, clientData: clientData, fork: fork]]; IF proc # NIL THEN AddButtonProp[node, button] ELSE RemButtonProp[node]; }; CallWithLock[LockedCreateButton, root] }; }; WrongViewerClass: PUBLIC ERROR = CODE; CreateButtonFromNode: PUBLIC PROC [node: TiogaOps.Ref, start: INT _ 0, end: INT _ INT.LAST, proc: TiogaButtonProc _ NIL, clientData: REF ANY _ NIL, fork: BOOLEAN _ TRUE] RETURNS [button: TiogaButton] ~ { root: TiogaOps.Ref ~ TiogaOps.Root[node]; ref: REF ANY _ TiogaOps.GetProp[root, $Viewer]; viewer: ViewerClasses.Viewer _ IF ref # NIL AND ISTYPE[ref, ViewerClasses.Viewer] THEN NARROW[ref] ELSE NIL; IF viewer = NIL OR viewer.class.flavor # $TiogaButtons THEN ERROR WrongViewerClass; { LockedCreateButtonFromNode: PROC [root: TiogaOps.Ref] ~ { button _ NEW[TiogaButtonRec _ [startLoc: [node, start], endLoc: [node, end], proc: proc, clientData: clientData, fork: fork]]; IF proc # NIL THEN AddButtonProp[node, button] ELSE RemButtonProp[node]; }; IF start = 0 AND end = INT.LAST THEN start _ end _ NodeItself; IF node # NIL THEN CallWithLock[LockedCreateButtonFromNode, root] }; }; AppendToButton: PUBLIC PROC [button: TiogaButton, rope: ROPE _ NIL, looks: ROPE _ NIL, proc: TiogaButtonProc _ NIL, clientData: REF ANY _ NIL, fork: BOOLEAN _ TRUE] RETURNS [TiogaButton] ~ { node: TiogaOps.Ref ~ button.startLoc.node; root: TiogaOps.Ref ~ TiogaOps.Root[node]; LockedAppendToButton: PROC [root: TiogaOps.Ref] ~ { lookVector: TextLooks.Looks ~ TextLooks.RopeToLooks[looks]; start, length: INT; [start, length] _ AppendRopeToNode[root, node, rope, lookVector]; button _ NEW[TiogaButtonRec _ [startLoc: [node, start], endLoc: [node, start+length-1], proc: proc, clientData: clientData, fork: fork]]; IF proc # NIL THEN AddButtonProp[node, button]; }; CallWithLock[LockedAppendToButton, root]; RETURN [button]; }; DeleteButton: PUBLIC PROC [button: TiogaButton] ~ { IF button # NIL THEN { root: TiogaOps.Ref ~ TiogaOps.Root[button.startLoc.node]; LockedDeleteButton: PROC [root: TiogaOps.Ref] ~ { EditSpan.Delete[ root: TextNodeRef[root], del: TextNodeSpan[button.startLoc, button.endLoc]]; IF button.startLoc.where # NodeItself THEN AdjustButtonProps[button]; }; CallWithLock[LockedDeleteButton, root]; }; }; FindTiogaButton: PUBLIC PROC [this: ViewerClasses.Viewer, loc: TiogaOps.Location] RETURNS [button: TiogaButton] ~ { list: TiogaButtonList; ref: REF ANY _ TiogaOps.GetProp[loc.node, $TiogaButtonList]; IF ref = NIL OR ~ISTYPE[ref, TiogaButtonList] THEN RETURN [NIL]; list _ NARROW[ref]; WHILE list # NIL DO t: TiogaButton ~ list.first; IF t.startLoc.node # loc.node THEN RETURN [NIL]; IF t.startLoc.where = NodeItself THEN RETURN [t]; IF loc.where >= t.startLoc.where AND t.endLoc.where >= loc.where THEN RETURN [t]; list _ list.rest; ENDLOOP; RETURN [NIL]; }; GetRope: PUBLIC PROC [button: TiogaButton] RETURNS [rope: ROPE] ~ { IF button # NIL THEN { rope _ TiogaOps.GetRope[button.startLoc.node]; IF button.startLoc.where # NodeItself THEN rope _ rope.Substr[button.startLoc.where, button.endLoc.where-button.startLoc.where+1]; }; }; SetStyleFromRope: PUBLIC PROC [v: ViewerClasses.Viewer, styleRope: ROPE] ~ { root: TextNode.Ref ~ TextNodeRef[TiogaOps.ViewerDoc[v]]; TextEdit.PutProp[node: root, name: $StyleDef, value: NodeProps.DoSpecs[$StyleDef, styleRope], root: root]; }; ChangeButtonLooks: PUBLIC PROC [button: TiogaButton, addLooks, removeLooks: ROPE _ NIL] ~ { IF button # NIL THEN { root: TiogaOps.Ref _ TiogaOps.Root[button.startLoc.node]; InnerSetLooks: PROC [root: TiogaOps.Ref] ~ { add: TextLooks.Looks _ TextLooks.RopeToLooks[addLooks]; remove: TextLooks.Looks _ TextLooks.RopeToLooks[removeLooks]; EditSpan.ChangeLooks[TextNodeRef[root], TextNodeSpan[button.startLoc, button.endLoc], remove, add]; }; CallWithLock[InnerSetLooks, root]; }; }; CallWithLock: PROC [proc: PROC [root: TiogaOps.Ref], root: TiogaOps.Ref] ~ { IF root # NIL THEN { TiogaOps.Lock[root]; proc[root ! UNWIND => TiogaOps.Unlock[root]]; TiogaOps.Unlock[root]; MarkViewerNotEdited[root]; }; }; MarkViewerNotEdited: PUBLIC PROC [root: TiogaOps.Ref] ~ { DoOne: PROC [v: ViewerClasses.Viewer] ~ { IF v.newVersion THEN { v.newVersion _ FALSE; ViewerOps.PaintViewer[viewer: v, hint: caption]; }; }; WITH TiogaOps.GetProp[root, $Viewer] SELECT FROM v: ViewerClasses.Viewer => { IF v.newVersion THEN { DoOne[v]; FOR x: ViewerClasses.Viewer _ v.link, x.link WHILE x # v AND x # NIL DO DoOne[x]; ENDLOOP; }; }; ENDCASE; }; LockedCreateNode: PROC [root: TiogaOps.Ref, rope: ROPE, viewer: ViewerClasses.Viewer] RETURNS [node: TiogaOps.Ref] ~ { IF TiogaOps.GetProp[root, $InitialTiogaButtons] # NIL THEN { TiogaExtraOps.RemProp[root, $InitialTiogaButtons]; node _ TiogaOpsRef[TextNodeRef[root].child]; } ELSE { IF TiogaOps.FirstChild[root] = NIL THEN { node _ TiogaOpsRef[EditSpan.Insert[root: TextNodeRef[root], old: TextNodeRef[root], where: child]]; TEditDisplay.InvalidateBranch[viewer: viewer, node: TextNodeRef[root]]; } ELSE node _ TiogaOpsRef[EditSpan.Split[TextNodeRef[root], [TextNode.LastChild[TextNodeRef[root]], NodeItself]]]; SetFormat[node, ""]; }; [] _ TextEdit.InsertRope[root: TextNodeRef[root], dest: TextNodeRef[node], rope: rope, destLoc: TextNode.MaxLen]; }; SetFormat: PROC [node: TiogaOps.Ref, format: ROPE] ~ TRUSTED { TiogaFileOps.SetFormat[TiogaFileOpsRef[node], format]; }; AddLooks: PROC [node: TiogaOps.Ref, start, len: INT, look: CHAR ['a..'z], root: TiogaOps.Ref _ NIL] ~ { TiogaFileOps.AddLooks[TiogaFileOpsRef[node], start, len, look, TiogaFileOpsRef[root]]; }; AddButtonProp: PROC [node: TiogaOps.Ref, button: TiogaButton] ~ { list: TiogaButtonList _ NIL; ref: REF ANY _ TiogaOps.GetProp[node, $TiogaButtonList]; IF ref # NIL AND ISTYPE[ref, TiogaButtonList] THEN list _ NARROW[ref]; IF list = NIL THEN list _ LIST[button] ELSE list _ CONS[button, list]; TiogaOps.PutProp[node, $TiogaButtonList, list]; }; RemButtonProp: PROC [node: TiogaOps.Ref] ~ { TiogaExtraOps.RemProp[node, $TiogaButtonList]; }; AdjustButtonProps: PROC [button: TiogaButton] ~ { buttonDelta: INTEGER ~ button.endLoc.where - button.startLoc.where + 1; propListModified: BOOLEAN _ FALSE; propList, list, last: TiogaButtonList; ref: REF ANY ~ TiogaOps.GetProp[button.startLoc.node, $TiogaButtonList]; IF ref = NIL OR ~ISTYPE[ref, TiogaButtonList] THEN RETURN; propList _ list _ NARROW[ref]; WHILE list # NIL DO t: TiogaButton ~ list.first; IF t.startLoc.node = button.startLoc.node AND t.startLoc.where # NodeItself THEN { t.endLoc.where _ t.endLoc.where+1; IF t.startLoc.where IN [button.startLoc.where .. button.endLoc.where] THEN t.startLoc.where _ button.startLoc.where ELSE IF t.startLoc.where > button.endLoc.where THEN t.startLoc.where _ t.startLoc.where - buttonDelta; IF t.endLoc.where IN [button.startLoc.where .. button.endLoc.where] THEN t.endLoc.where _ button.startLoc.where ELSE IF t.endLoc.where > button.endLoc.where THEN t.endLoc.where _ t.endLoc.where - buttonDelta; IF t.startLoc.where # t.endLoc.where THEN t.endLoc.where _ t.endLoc.where-1 ELSE { IF last = NIL THEN propList _ propList.rest ELSE last.rest _ list.rest; propListModified _ TRUE; }; }; last _ list; list _ list.rest; ENDLOOP; IF propListModified THEN TiogaOps.PutProp[button.startLoc.node, $TiogaButtonList, propList]; }; AppendRopeToNode: PROC [root: TiogaOps.Ref, node: TiogaOps.Ref, rope: ROPE, lookVector: TextLooks.Looks] RETURNS [start, length: INT] ~ { [start, length] _ TextEdit.InsertRope[root: TextNodeRef[root], dest: TextNodeRef[node], destLoc: TextNode.MaxLen, rope: rope, inherit: FALSE, looks: lookVector]; }; TextNodeRef: PUBLIC PROC [ref: REF] RETURNS [TextNode.Ref] ~ TRUSTED { RETURN [LOOPHOLE[ref]]; }; TiogaOpsRef: PUBLIC PROC [ref: REF] RETURNS [TiogaOps.Ref] ~ TRUSTED { RETURN [LOOPHOLE[ref]]; }; TiogaFileOpsRef: PUBLIC PROC [ref: REF] RETURNS [TiogaFileOps.Ref] ~ TRUSTED { RETURN [LOOPHOLE[ref]]; }; InitViewer: ViewerClasses.InitProc ~ { root: TiogaOps.Ref; textViewerInitProc[self]; self.tipTable _ buttonTIPTable; root _ TiogaOps.ViewerDoc[self]; TiogaOps.PutProp[root, $InitialTiogaButtons, NEW[BOOLEAN _ TRUE]]; }; Notifier: ViewerClasses.NotifyProc ~ { mouseButton: Menus.MouseButton _ red; shift, control: BOOL _ FALSE; mouse: TIPUser.TIPScreenCoords; mouseX, mouseY: INT; -- for archival purposes feedbackSel: TEditDocument.Selection _ TEditSelection.Alloc[]; FOR list: LIST OF REF ANY _ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => SELECT x FROM $Red => mouseButton _ red; $Yellow => mouseButton _ yellow; $Blue => mouseButton _ blue; $Shift => shift _ TRUE; $Control => control _ TRUE; $Mark => { button: TiogaButton; selectedButton: TiogaButton _ SelectedButton[self]; IF selectedButton = NIL THEN InputFocus.CaptureButtons[Notifier, self.class.tipTable, self] -- to track feedback out of buttons ELSE IF NOT MouseInViewer[self, mouse] THEN { InputFocus.ReleaseButtons[]; CancelFeedback[self]; LOOP; }; [] _ TEditSelectionPrivate.ResolveToChar[ selection: feedbackSel, viewer: self, tdd: NARROW[self.data, TEditDocument.TEditDocumentData], x: mouse.mouseX, y: self.ch - mouse.mouseY]; button _ FindTiogaButton[self, TiogaOpsLoc[feedbackSel.start.pos]]; IF button # NIL THEN IF button = selectedButton THEN LOOP -- optimizing check ELSE EstablishFeedback[self, button] ELSE { InputFocus.ReleaseButtons[]; CancelFeedback[self]; }; }; $Hit => { selectedButton: TiogaButton _ SelectedButton[self]; IF selectedButton # NIL THEN { InputFocus.ReleaseButtons[]; CancelFeedback[self]; IF selectedButton.fork THEN TRUSTED { [] _ Process.Detach[FORK selectedButton.proc[selectedButton, selectedButton.clientData, mouseButton, shift, control]] } ELSE selectedButton.proc[selectedButton, selectedButton.clientData, mouseButton, shift, control]; }; }; ENDCASE => NULL; z: TIPUser.TIPScreenCoords => { mouse _ z; mouseX _ mouse.mouseX; mouseY _ mouse.mouseY; }; ENDCASE => ERROR; ENDLOOP; TEditSelection.Free[feedbackSel]; }; MouseInViewer: PROC [this: ViewerClasses.Viewer, mouse: TIPUser.TIPScreenCoords] RETURNS [BOOLEAN] ~ { viewer: ViewerClasses.Viewer; client: BOOLEAN; [viewer, client] _ ViewerOps.MouseInViewer[mouse]; RETURN [viewer = this AND client]; }; TiogaOpsLoc: PROC [loc: TextNode.Location] RETURNS [TiogaOps.Location] ~ TRUSTED INLINE { RETURN [LOOPHOLE[loc]] }; TextNodeSpan: PROC [start, end: TiogaOps.Location] RETURNS [TextNode.Span] ~ TRUSTED INLINE { RETURN [[LOOPHOLE[start], LOOPHOLE[end]]]; }; SelectedButton: PROC [this: ViewerClasses.Viewer] RETURNS [button: TiogaButton] ~ { val: REF ANY ~ ViewerOps.FetchProp[viewer: this, prop: $TiogaButtonSelected]; IF val # NIL AND ISTYPE[val, TiogaButton] THEN button _ NARROW[val]; }; EstablishFeedback: PROC [this: ViewerClasses.Viewer, button: TiogaButton] ~ { ViewerOps.AddProp[viewer: this, prop: $TiogaButtonSelected, val: button]; TiogaOps.SetSelection[ viewer: this, start: IF button.startLoc.where = NodeItself THEN [button.startLoc.node, 0] ELSE button.startLoc, end: IF button.endLoc.where = NodeItself THEN [button.endLoc.node, GetRope[button].Length] ELSE button.endLoc, level: char, caretBefore: TRUE, pendingDelete: TRUE, which: feedback]; }; CancelFeedback: PROC [this: ViewerClasses.Viewer] ~ { ViewerOps.AddProp[viewer: this, prop: $TiogaButtonSelected, val: NIL]; TiogaOps.CancelSelection[feedback]; }; Hack: TiogaButtonProc ~ { button: TiogaButton _ NARROW[parent]; MessageWindow.Append[Rope.Cat["button contents ", GetRope[button]], TRUE]; MessageWindow.Blink[]; }; buttonTIPTable: TIPUser.TIPTable _ ViewerOps.FetchViewerClass[$Button].tipTable; tiogaButtonClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ ViewerOps.FetchViewerClass[$Text]^]; textViewerInitProc: ViewerClasses.InitProc _ tiogaButtonClass.init; tiogaButtonClass.init _ InitViewer; tiogaButtonClass.save _ NIL; -- avoids saving TiogaButtons properties tiogaButtonClass.notify _ Notifier; tiogaButtonClass.cursor _ bullseye; tiogaButtonClass.icon _ tool; tiogaButtonClass.tipTable _ buttonTIPTable; ViewerOps.RegisterViewerClass[$TiogaButtons, tiogaButtonClass]; END. TiogaButtonsImpl.Mesa Copyright c 1984, 1985 Xerox Corporation. All rights reserved. Rick Beach, May 14, 1986 11:56:37 am PDT Russ Atkinson (RRA) May 21, 1985 0:03:26 am PDT Create TiogaButtons Verify that viewer is a $TiogaButtons class viewer. Creates a button as the last child of the document in viewer. Creates a button from the node. Provides the text of the button. Can be used in place of clientData for many purposes. This crock is necessary because TiogaOps.PutProp uses too low an abstraction for adding properties to a node. Aren't we nice, we get all the split viewers too! create a node as last child of root, document is already locked Create node as sibling of last child of root t is either the deleted button or a proper subset of it, so delete t from the list Put back the modified list Type Conversion Routines (for the client and the implementation) The $TiogaButtons viewer class Mouse coordinates are guaranteed to be in viewer space and we have captured the buttons for future $Marks and $Hits Selection info returned in feedbackSel.start.pos as TextNode.Location Find button within this selection Turn off reverse video Initialization Some Neat things to add to TiogaButtons in the future: a) box style attributes that Tioga would draw boxes around the run of text with this attribute b) rule style attribute that Tioga would draw horizontal or vertical rules c) background color style attribute d) cursor changes when mouse is over a button e) secondary selection to still work from $TiogaButtons viewers ΚJ˜code™Kšœ Οmœ4™?K™(K™/—K™šΟk ˜ Kšœ žœ&˜4Kšœ žœ"˜2Kšœ žœ˜Kšœžœ˜Kšœžœ˜$Kšœ žœ ˜Kšœžœ ˜Kšœžœ'žœ ˜?Kšœ žœ˜&Kšœžœ ˜3Kšœžœ˜(Kšœžœ˜,Kšœžœ˜#Kšœ žœ˜%Kšœ žœ˜%Kšœ žœ*˜8Kšœ žœA˜SK˜Kšœžœ ˜Kšœ žœ˜.Kšœ žœ•˜£Kšœžœ˜*KšœžœN˜aKšœ žœg˜v—K˜KšΠlnœžœž˜KšžœΪ˜αKšžœ ˜'šœž œ˜K™Kšžœžœžœ˜Kšœ žœ˜—head™šΟn œž œ!žœ˜_Kšœ>˜>K˜K˜—š  œž œ*žœ˜JKšœžœ˜ Kš žœ žœžœ%žœžœ˜SKšœΟc6˜KKšœI˜IKšœ)‘(˜QK˜K˜5Kšœ˜K™—š œžœžœ0žœžœžœžœžœžœžœžœžœžœ˜ΡK˜0š œžœ˜Kšœ6˜6K˜K˜—š  œžœ"žœžœ žœ˜gKšœV˜VK˜K˜—š  œžœ.˜AKšœžœ˜Kšœžœžœ,˜8Kš žœžœžœžœžœžœ˜Fšžœž˜ Kšžœžœ˜Kšžœžœ˜—Kšœ/˜/K˜K˜—š  œžœ˜,Kšœ.˜.K˜K˜—š œžœ˜1Kšœ žœ3˜GKšœžœžœ˜"Kšœ&˜&Kšœžœžœ<˜HKš žœžœžœžœžœžœ˜:Kšœžœ˜šžœžœž˜Kšœ˜šžœ(žœžœ˜RK˜"K˜šžœžœ0žœ˜KKšœ(˜(—šžœžœ'žœ˜4Kšœ2˜2K˜—šžœžœ0žœ˜IKšœ&˜&—šžœžœ%žœ˜2Kšœ.˜.K˜—šžœ"ž˜)Kšœ!˜!—šžœ˜K™Ršžœž˜ Kšžœ˜Kšžœ˜—Kšœžœ˜Kšœ˜—Kšœ˜—K˜ K˜Kšž˜—šžœž˜K™KšœC˜C—K˜K˜—š  œžœ0žœžœžœ˜‰Kšœ‡žœ˜‘K˜K˜——™@š   œžœžœžœžœžœ˜FJšžœžœ˜Kšœ˜K˜—š   œžœžœžœžœžœ˜FJšžœžœ˜Kšœ˜K˜—š  œžœžœžœžœžœ˜NJšžœžœ˜Kšœ˜K˜——™š  œ˜&K˜K˜Kšœ˜K˜ Kšœ-žœžœžœ˜BK˜K˜—š œ˜&K˜%Kšœžœžœ˜Kšœ˜Kšœžœ‘˜-Kšœ>˜>šžœžœžœžœžœžœžœž˜@šžœ žœž˜šœžœžœž˜K˜K˜ K˜Kšœžœ˜Kšœžœ˜šœ ˜ K˜K˜3šžœžœžœ˜Kšœ?‘#˜b—šžœžœžœžœ˜-K˜K˜Kšžœ˜Kšœ˜—K™:K™8šœ)˜)Kšœ˜Kšœ ˜ Kšœžœ-˜8Kšœ˜Kšœ˜—KšœE™EK™!KšœC˜Cšžœ žœžœ˜Kšžœžœžœ‘˜8Kšžœ ˜$—šžœ˜K˜K˜Kšœ˜—Kšœ˜—šœ ˜ K™Kšœ3˜3šžœžœžœ˜K˜Kšœ˜šžœžœžœ˜%Kšœžœ]˜uKšœ˜—Kšžœ]˜aKšœ˜—Kšœ˜—Kšžœžœ˜—šœ˜K˜ Kšœ˜Kšœ˜Kšœ˜—Kšžœžœ˜—Kšžœ˜—K˜!Kšœ˜K˜—š  œžœ>žœžœ˜fK˜Kšœžœ˜Kšœ2˜2Kšžœžœ ˜"K˜K˜—š  œžœžœžœ˜YKšžœžœ˜K˜K˜—š  œžœ!žœžœ˜]Kšžœžœ žœ˜*K˜K˜—š œžœžœ˜SKšœžœžœA˜Mš žœžœžœžœž˜.Kšœ žœ˜—K˜K˜—š œžœ6˜MKšœI˜I˜Kšœ ˜ šœžœ#˜,Kšžœ˜Kšžœ˜—šœžœ!˜(Kšžœ-˜1Kšžœ˜—K˜ Kšœ žœ˜Kšœžœ˜K˜—K˜K˜—š œžœ!˜5KšœAžœ˜FK˜#K˜K˜—K˜š œ˜Kšœžœ ˜%KšœDžœ˜JK˜K˜——™KšœP˜P˜-KšžœD˜G—KšœC˜CKšœ#˜#Kšœžœ‘(˜EKšœ#˜#Kšœ#˜#Kšœ˜Kšœ+˜+Kšœ?˜?—K™Kšžœ˜K˜™6K™^K™JK™#K™-K™?——…—>UV