DIRECTORY Feedback, Imager USING [ClipRectangleI, Color, Context, MaskRectangleI, SetColor, white], ImagerBackdoor USING [MakeStipple], InputFocus, Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuEntry, MouseButton, ReplaceMenuEntry, SetGuarded], MultiCursors, Process, SimpleFeedback, TIPUser USING [TIPScreenCoords], UserInput, UserInputInsertActions, ViewerClasses USING [Column, HScrollOp, NotifyProc, ScrollOp, Viewer], ViewerLocks USING [CallUnderWriteLock, Wedged], ViewerMenus USING [Adjust, Color, Close, Destroy, Grow, Left, Right, Top], ViewerOps USING [MouseInViewer, PaintViewer], ViewerPrivate, ViewerSpecs USING [captionHeight, nColumns, scrollBarW, windowBorderSize], ViewersWorld, ViewersWorldInstance, ViewersWorldRefType, ViewersWorldTypes, WindowManager; WindowManagerImpl: CEDAR MONITOR IMPORTS Imager, ImagerBackdoor, InputFocus, Menus, MultiCursors, Process, SimpleFeedback, UserInputInsertActions, ViewerLocks, ViewerMenus, ViewerOps, ViewerPrivate, ViewerSpecs, ViewersWorld, ViewersWorldInstance EXPORTS ViewerPrivate, ViewersWorldRefType, WindowManager SHARES Menus, ViewerClasses ~ BEGIN OPEN ViewerSpecs; CursorType: TYPE = MultiCursors.CursorType; Column: TYPE = ViewerClasses.Column; HScrollOp: TYPE = ViewerClasses.HScrollOp; NotifyProc: TYPE = ViewerClasses.NotifyProc; ScrollOp: TYPE = ViewerClasses.ScrollOp; Viewer: TYPE = ViewerClasses.Viewer; ViewersWorldObj: PUBLIC TYPE = ViewersWorldTypes.ViewersWorldObj; Zone: TYPE ~ {none, caption, menu, vscroll, hscroll}; Priority: TYPE = Process.Priority; VMgrPriority: PUBLIC Priority ¬ 3; -- "priorityForeground" in PrincOps, "priorityUserNormal" in PCedar CursorZone: PROC[v: Viewer, mousePos: TIPUser.TIPScreenCoords] RETURNS[Zone] ~ { x: INTEGER ~ mousePos.mouseX; y: INTEGER ~ mousePos.mouseY; IF v.caption AND v.parent=NIL AND y>=(v.wh-ViewerSpecs.captionHeight) THEN RETURN[caption]; IF v.menu#NIL AND y>=(v.cy+v.ch) THEN RETURN[menu]; IF v.scrollable AND x { SetC[bullseye, device]; AddEntry[feedbackViewer, feedbackZone, FALSE]; }; vscroll => IF viewer.class.scroll # NIL AND viewer.init THEN { left, right: INTEGER; [left, right] ¬ viewer.class.scroll[viewer, query, 0]; AddEntry[feedbackViewer, feedbackZone, FALSE, left, right]; }; hscroll => IF viewer.class.hscroll # NIL AND viewer.init THEN { left, right: INTEGER; [left, right] ¬ viewer.class.hscroll[viewer, query, 0]; AddEntry[feedbackViewer, feedbackZone, FALSE, left, right]; }; ENDCASE => SetC[textPointer, device]; -- no new feedback InputFocus.CaptureButtons[proc: ProcessWindowManagerInput, tip: ViewerPrivate.WindowManagerTIPTable, viewer: feedbackViewer]; RETURN; }; SetC[textPointer, device]; -- no new feedback }; scrollLow: Imager.Color ¬ ImagerBackdoor.MakeStipple[8120H]; scrollVisible: Imager.Color ¬ ImagerBackdoor.MakeStipple[5A5AH]; scrollHigh: Imager.Color ¬ ImagerBackdoor.MakeStipple[2180H]; FeedbackEchoProcess: PROC [col: Column] = { DO entry: FeedbackEchoEntry ¬ RemEntry[col]; viewer: Viewer ¬ entry.viewer; inner: PROC = { IF viewer.iconic OR viewer.destroyed THEN RETURN; IF entry.remove THEN SELECT entry.zone FROM caption => { ViewerPrivate.ClearMenu[windowMenu, viewer, FALSE]; ViewerOps.PaintViewer[viewer, caption]; }; menu => ViewerPrivate.ClearMenu[viewer.menu, viewer]; vscroll => { removeScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ IF viewer.border THEN windowBorderSize ELSE 0; y: INTEGER ~ viewer.cy; w: INTEGER ~ viewer.cx-x; h: INTEGER ~ viewer.ch; Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, x, y, w, h]; }; ViewerPrivate.PaintWindow[viewer, removeScrollbar]; }; hscroll => { removeScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ viewer.cx; y: INTEGER ~ IF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0; w: INTEGER ~ viewer.cw; h: INTEGER ~ ViewerSpecs.scrollBarW; Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, x, y, w, h]; }; ViewerPrivate.PaintWindow[viewer, removeScrollbar]; }; ENDCASE ELSE SELECT entry.zone FROM caption => DrawCaptionMenu[viewer, TRUE]; menu => {}; vscroll => { drawScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ IF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0; y: INTEGER ~ viewer.cy; w: INTEGER ~ ViewerSpecs.scrollBarW; h: INT ~ viewer.ch; -- for highest precision arithmetic d1: INTEGER ~ (entry.left*h+50)/100; -- use highest precision arithmetic d2: INTEGER ~ (entry.right*h+50)/100; -- use highest precision arithmetic Imager.SetColor[context, scrollLow]; Imager.MaskRectangleI[context, x, y, w, h-d2]; Imager.SetColor[context, scrollVisible]; Imager.MaskRectangleI[context, x, y+h-d2, w, d2-d1]; Imager.SetColor[context, scrollHigh]; Imager.MaskRectangleI[context, x, y+h-d1, w, d1]; }; IF entry.left IN [0..100] AND entry.right IN [0..100] THEN ViewerPrivate.PaintWindow[viewer, drawScrollbar]; }; hscroll => { drawScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ viewer.cx; y: INTEGER ~ IF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0; w: INT ~ viewer.cw; -- for highest precision arithmetic h: INTEGER ~ ViewerSpecs.scrollBarW; d1: INTEGER ~ (entry.left*w+50)/100; -- use highest precision arithmetic d2: INTEGER ~ (entry.right*w+50)/100; -- use highest precision arithmetic Imager.SetColor[context, scrollLow]; Imager.MaskRectangleI[context, x, y, d1, h]; Imager.SetColor[context, scrollVisible]; Imager.MaskRectangleI[context, x+d1, y, d2-d1, h]; Imager.SetColor[context, scrollHigh]; Imager.MaskRectangleI[context, x+d2, y, w-d2, h]; }; IF entry.left IN [0..100] AND entry.right IN [0..100] THEN ViewerPrivate.PaintWindow[viewer, drawScrollbar]; }; ENDCASE; }; IF viewer # NIL THEN ViewerLocks.CallUnderWriteLock[inner, viewer ! ViewerLocks.Wedged, ABORTED => CONTINUE]; ENDLOOP; }; AddEntry: ENTRY PROC [viewer: Viewer, zone: Zone, remove: BOOL ¬ FALSE, left, right: INTEGER ¬ 0] = { IF NOT viewer.iconic AND NOT viewer.destroyed THEN { fbq: FeedbackEchoQueue ~ feedBackQueues[viewer.column]; new: FeedbackEchoList ~ LIST[[viewer, zone, remove, left, right]]; IF fbq.tail = NIL THEN fbq.head ¬ new ELSE fbq.tail.rest ¬ new; fbq.tail ¬ new; NOTIFY fbq.feedbackChange; }; }; RemEntry: ENTRY PROC [col: Column] RETURNS [FeedbackEchoEntry] = { fbq: FeedbackEchoQueue ~ feedBackQueues[col]; DO head: FeedbackEchoList ¬ fbq.head; IF head # NIL THEN { IF (fbq.head ¬ head.rest) = NIL THEN fbq.tail ¬ NIL; head.rest ¬ NIL; RETURN [head.first]; }; WAIT fbq.feedbackChange; ENDLOOP; }; feedBackQueues: REF FeedBackQueuesRep ~ NEW[FeedBackQueuesRep[ViewerSpecs.nColumns]]; FeedBackQueuesRep: TYPE = RECORD [SEQUENCE n: Column OF FeedbackEchoQueue]; FeedbackEchoQueue: TYPE = REF FeedbackEchoQueueRep; FeedbackEchoQueueRep: TYPE = RECORD [head, tail: FeedbackEchoList ¬ NIL, feedbackChange: CONDITION]; FeedbackEchoList: TYPE = LIST OF FeedbackEchoEntry; FeedbackEchoEntry: TYPE = RECORD [ viewer: Viewer, zone: Zone, remove: BOOL, left, right: INTEGER]; vCursor: ARRAY Menus.MouseButton OF CursorType ¬ [red: scrollUp, yellow: scrollRight, blue: scrollDown]; vScrollOp: ARRAY Menus.MouseButton OF ScrollOp ¬ [red: up, yellow: thumb, blue: down]; hCursor: ARRAY Menus.MouseButton OF CursorType ¬ [red: scrollLeft, yellow: scrollUp, blue: scrollRight]; hScrollOp: ARRAY Menus.MouseButton OF HScrollOp ¬ [red: left, yellow: thumb, blue: right]; ProcessWindowResults: PUBLIC ViewerClasses.NotifyProc = { SimpleFeedback.Append[$Viewers, $oneLiner, $Error, "WindowManager ProcessWindowResults called (not ProcessWindowManagerInput). Bug?"]; ProcessWindowManagerInput[self, input]; }; ProcessWindowManagerInput: PUBLIC PROC [self: Viewer, input: LIST OF REF ANY, device: REF ¬ NIL, user: REF ¬ NIL, display: REF ¬ NIL] = { viewer: Viewer ¬ self; -- the current viewer zone: Zone ¬ none; mouse: TIPUser.TIPScreenCoords ¬ NIL; shft, ctrl: BOOL ¬ FALSE; FOR l: LIST OF REF ANY ¬ input, l.rest UNTIL l=NIL DO WITH l.first SELECT FROM coords: TIPUser.TIPScreenCoords => { client: BOOL ¬ FALSE; mouse ¬ coords; IF feedbackZone#none THEN [viewer, client] ¬ ViewerOps.MouseInViewer[mouse]; IF client OR viewer=NIL THEN zone ¬ none ELSE zone ¬ CursorZone[viewer, mouse]; }; atom: ATOM => { button: Menus.MouseButton ¬ red; action: {nil, move, down, slide, up} ¬ nil; SELECT atom FROM $M => action ¬ move; $RD => { button ¬ red; action ¬ down }; $RM => { button ¬ red; action ¬ slide }; $RU => { button ¬ red; action ¬ up }; $YD => { button ¬ yellow; action ¬ down }; $YM => { button ¬ yellow; action ¬ slide }; $YU => { button ¬ yellow; action ¬ up }; $BD => { button ¬ blue; action ¬ down }; $BM => { button ¬ blue; action ¬ slide }; $BU => { button ¬ blue; action ¬ up }; $Control => ctrl ¬ TRUE; $Shift => shft ¬ TRUE; ENDCASE; IF action=nil THEN LOOP; PostNewFeedback[viewer, zone, device]; IF zone=caption THEN SELECT button FROM yellow => mouse.mouseX ¬ growEntry.xPos+1; blue => mouse.mouseX ¬ closeEntry.xPos+1; ENDCASE; SELECT zone FROM -- zone specific ops caption => SELECT action FROM down, slide => ViewerPrivate.MarkMenu[windowMenu, viewer, mouse]; up => ViewerPrivate.HitMenu[windowMenu, viewer, mouse, button, shft, ctrl]; ENDCASE; menu => SELECT action FROM down, slide => ViewerPrivate.MarkMenu[viewer.menu, viewer, mouse]; up => ViewerPrivate.HitMenu[viewer.menu, viewer, mouse, button, shft, ctrl]; ENDCASE; vscroll => SELECT action FROM move => SetC[scrollUpDown, device]; down, slide => SetC[vCursor[button], device]; up => HitVScroll[viewer, mouse.mouseY, device, vScrollOp[button], shft, ctrl]; ENDCASE; hscroll => SELECT action FROM move => SetC[scrollLeftRight, device]; down, slide => SetC[hCursor[button], device]; up => HitHScroll[viewer, mouse.mouseX, device, hScrollOp[button], shft, ctrl]; ENDCASE; ENDCASE; }; ENDCASE; ENDLOOP; }; HitVScroll: PROC [viewer: Viewer, wy: INTEGER, device: REF, op: ScrollOp, shift, control: BOOL] = { IF viewer.class.scroll#NIL THEN { h: CARD ~ viewer.ch; -- CARD for highest precision arithmetic amount: CARD ¬ h-(wy-viewer.cy); -- from top of client area. left, right: INTEGER; IF op=thumb THEN amount ¬ (amount*100+h/2)/h; AddEntry[viewer, vscroll, TRUE]; [] ¬ viewer.class.scroll[viewer, op, amount, shift, control]; [left, right] ¬ viewer.class.scroll[viewer, query, 0]; AddEntry[viewer, vscroll, FALSE, left, right]; }; SetC[scrollUpDown, device]; -- put the cursor back }; HitHScroll: PROC [viewer: Viewer, wx: INTEGER, device: REF, op: HScrollOp, shift, control: BOOL] = { IF viewer.class.hscroll#NIL THEN { w: CARD ~ viewer.cw; amount: CARD ¬ wx-viewer.cx; -- from left of client area left, right: INTEGER; IF op=thumb THEN amount ¬ (amount*100+w/2)/w; AddEntry[viewer, hscroll, TRUE]; [] ¬ viewer.class.hscroll[viewer, op, amount, shift, control]; [left, right] ¬ viewer.class.hscroll[viewer, query, 0]; AddEntry[viewer, hscroll, FALSE, left, right]; }; SetC[scrollLeftRight, device]; -- put the cursor back }; DrawCaptionMenu: PUBLIC PROC [v: Viewer, guard: BOOL] = { drawCaption: PROC [context: Imager.Context] ~ { wbs: INTEGER ~ IF v.border THEN windowBorderSize ELSE 0; x: INTEGER ~ wbs; y: INTEGER ~ v.wh-captionHeight; w: INTEGER ~ v.ww-2*wbs; h: INTEGER ~ captionHeight; Imager.ClipRectangleI[context, x, y, w, h]; Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, x, y, w, h]; IF guard THEN Menus.SetGuarded[destroyEntry, v.guardDestroy OR (v.newVersion AND v.link=NIL AND NOT v.saveInProgress)]; ViewerPrivate.DrawMenu[windowMenu, context, x, y+h]; }; IF v.visible AND NOT v.iconic THEN ViewerPrivate.PaintWindow[v, drawCaption]; }; waitCount: PUBLIC INTEGER ¬ 0; SetC: PROC [cursor: CursorType, device: REF] = INLINE { IF waitCount=0 THEN WITH device SELECT FROM atom: ATOM => MultiCursors.SetACursor[cursor, atom]; ENDCASE => IF device = NIL THEN MultiCursors.SetACursor[cursor, NIL]; }; WaitCursor: PUBLIC ENTRY PROC [cursor: CursorType ¬ hourGlass] = { ENABLE UNWIND => NULL; vWorld: ViewersWorld.Ref ¬ ViewersWorldInstance.GetWorld[]; IF vWorld.inputEnabled THEN MultiCursors.SetACursor[cursor, NIL]; waitCount ¬ waitCount + 1; }; UnWaitCursor: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; waitCount ¬ MAX[0, waitCount - 1]; IF waitCount=0 THEN RestoreCursor[]; }; NotYetImplemented: SIGNAL = CODE; RestoreCursor: PUBLIC PROC = TRUSTED { vWorld: ViewersWorld.Ref ¬ ViewersWorldInstance.GetWorld[]; IF vWorld.inputEnabled THEN { handle: UserInput.Handle ¬ ViewersWorld.GetInputHandle[vWorld]; UserInputInsertActions.InsertFakePosition[handle, 0] }; }; colorDisplayOn: PUBLIC BOOL ¬ FALSE; -- color display status StartColorViewers: PUBLIC PROC [screenPos: WindowManager.ScreenPos, bitsPerPixel: CARDINAL] = { IF bitsPerPixel # CARDINAL.LAST THEN ERROR; IF colorDisplayOn THEN RETURN; Menus.ReplaceMenuEntry[windowMenu, tNopEntry, tColorEntry]; [] ¬ ViewerPrivate.EnableColor[0]; colorDisplayOn ¬ TRUE; }; StopColorViewers: PUBLIC PROC = { IF NOT colorDisplayOn THEN RETURN; Menus.ReplaceMenuEntry[windowMenu, tColorEntry, tNopEntry]; ViewerPrivate.DisableColor[]; colorDisplayOn ¬ FALSE; }; destroyEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "Destroy", proc: ViewerMenus.Destroy, fork: FALSE, documentation: "Edits will be discarded..."]; adjustEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "Adjust", proc: ViewerMenus.Adjust, fork: FALSE]; topEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "Top", proc: ViewerMenus.Top, fork: FALSE]; leftEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "<--", proc: ViewerMenus.Left, fork: FALSE]; rightEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "-->", proc: ViewerMenus.Right, fork: FALSE]; tNopEntry: Menus.MenuEntry ¬ Menus.CreateEntry["", NIL]; tColorEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "Color", proc: ViewerMenus.Color, fork: FALSE]; growEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "Grow", proc: ViewerMenus.Grow, fork: FALSE]; closeEntry: Menus.MenuEntry ¬ Menus.CreateEntry[name: "Close", proc: ViewerMenus.Close, fork: FALSE]; windowMenu: PUBLIC Menus.Menu ¬ Menus.CreateMenu[]; Menus.AppendMenuEntry[windowMenu, destroyEntry]; Menus.AppendMenuEntry[windowMenu, adjustEntry]; Menus.AppendMenuEntry[windowMenu, topEntry]; Menus.AppendMenuEntry[windowMenu, leftEntry]; Menus.AppendMenuEntry[windowMenu, rightEntry]; Menus.AppendMenuEntry[windowMenu, tNopEntry]; Menus.AppendMenuEntry[windowMenu, growEntry]; Menus.AppendMenuEntry[windowMenu, closeEntry]; TRUSTED { FOR c: Column IN [Column.FIRST..feedBackQueues.n) DO feedBackQueues[c] ¬ NEW[FeedbackEchoQueueRep]; Process.Detach[FORK FeedbackEchoProcess[c]]; ENDLOOP; }; END. Κ WindowManagerImpl.mesa Copyright Σ 1985, 1986, 1988, 1989, 1991 by Xerox Corporation. All rights reserved. Doug Wyatt, July 22, 1985 2:18:21 pm PDT Russ Atkinson (RRA) July 1, 1985 6:47:27 pm PDT Michael Plass, September 29, 1989 4:16:48 pm PDT Ken Pier (KAP), November 13, 1988 9:11:33 pm PST Bier, November 7, 1991 2:49 pm PST Christian Jacobi, March 3, 1992 2:46 pm PST Willie-s, November 21, 1991 1:20 pm PST Note: mousePos is relative to lower left corner of v. This procedure is called under the notifier (and therefore needs no monitor protection) to process an input event into feedback actions. Prior to Cedar6.0 the feedback was "immediate", which tended to garbage up viewers in the process of painting. As part of the lock rationalization, feedback is required to acquire viewer locks as well. However, viewer locks must not force the input notifier to wedge, so we give the feedback chore to some other processes. Add a feedback entry This procedure is the base of a process that services caption, menu, and scrollbar feedback. There is a separate process for each column to allow reasonable feedback during long operations (like Save) in the other column(s). If this is not enough, a separate process could be spawned for each viewer that needs feedback. Preliminary results indicate that this is enough. We are careful to serialize all of the feedback for a given viewer properly. There is still a minor amount of feedback that gets done by other modules that are called by this one. MenusImpl.MarkMenu & MenusImpl.HitMenu (both exported to ViewerPrivate) can invert menu entries. At some time the feedback processes in this module could take over that chore. KAP for PCedar November 13, 1988 entry.left and entry.right are in [0..100] and represent the bottom and top percentage of the document that is visible in the viewer KAP for PCedar November 13, 1988 entry.left and entry.right are in [0..100] and represent the left and right percentage of the document that is visible in the viewer [self: Viewer, input: LIST OF REF ANY] RETURNS[BOOL] not monitored since notifier is synchronous We have an input event to a Viewers area like a scrollbar or menu. We have passed the event through WindowManagerTIPTable (see InputFocusImpl). In this routine, we take appropriate action. KAP for PCedar November 13, 1988 KAP for PCedar November 13, 1988 This is now a private call; use ColorDisplayManager instead. Fork a feedback process for each column (even the "static" column) Κx•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ ΟeœI™TKšœ(™(K™/K™0K™0K™"K™+K™'—K˜šΟk ˜ K˜ KšœžœC˜OKšœžœ˜#Kšœ ˜ Kšœžœh˜sKšœ ˜ Kšœ˜K˜Kšœžœ˜ K˜ Kšœ˜Kšœžœ3˜FKšœ žœ˜/Kšœ žœ9˜JKšœ žœ˜-Kšœ˜Kšœ žœ9˜JK˜ K˜Kšœ˜K˜Kšœ˜—K˜šΠblœžœž˜ KšžœΞ˜ΥKšžœ2˜9Kšžœ˜Kšœžœžœ ˜K˜Kšœ žœ˜+Kšœžœ˜$Kšœ žœ˜*Kšœ žœ˜,Kšœ žœ˜(Kšœžœ˜$Kšœžœžœ%˜A—K˜Kšœžœ+˜5Kšœ žœ˜"K˜KšΟn œžœΟcC˜gK˜š  œžœ/žœ ˜PK™5Kšœžœ˜Kšœžœ˜Kš žœ žœ žœžœ%žœžœ ˜\Kš žœžœžœžœžœ˜3Kš žœžœžœžœžœžœ ˜JKš žœžœžœžœžœžœ ˜KKšžœ˜ Kšœ˜—K˜K˜šœžœ˜K˜—š œžœ&žœ˜CKšœΞ™ΞKšžœžœžœ ˜Kšžœžœžœžœ˜;šžœžœ˜Kšžœžœ-žœ˜RKšœ'žœ˜-Kšœ˜Kšœ˜—Kšœ˜Kšœ˜šžœžœ˜Kšœ™šžœž˜šœ˜Kšœ˜Kšœ'žœ˜.Kšœ˜—š œ žœžœžœ žœ˜>Kšœ žœ˜K˜6Kšœ'žœ˜;Kšœ˜—š œ žœžœžœ žœ˜?Kšœ žœ˜K˜7Kšœ'žœ˜;Kšœ˜—Kšžœ‘˜8—Kšœ~˜~Kšžœ˜K˜—Kšœ‘˜-K˜K˜—Kšœ5Οfœ˜˜>Kšœ7˜7Kšœžœ˜.K˜—Kšœ‘˜5Kšœ˜—K˜š œžœžœžœ˜9šœ žœ˜/Kš œžœžœ žœžœ˜8Kšœžœ˜Kšœžœ˜ Kšœžœ˜Kšœžœ˜Kšœ+˜+K˜'Kšœ+˜+šžœž˜ Kš œ.žœžœžœžœžœ˜i—Kšœ4˜4K˜—šžœ žœžœ ž˜"Kšœ*˜*—Kšœ˜K˜—šœ žœžœ˜K˜—š œžœžœžœ˜7šžœ ž˜šžœžœž˜Kšœžœ*˜4Kš žœžœ žœžœ!žœ˜E——šœ˜K˜——š  œžœžœžœ%˜BKšžœžœžœ˜Kšœ;˜;Kšžœžœ!žœ˜AK˜Kšœ˜K˜—š  œžœžœžœ˜#Kšžœžœžœ˜Kšœ žœ˜"Kšžœ žœ˜$Kšœ˜K˜—Kš œžœžœ˜!K˜š  œžœžœžœ˜&Kšœ;˜;šžœž˜Kšœ?˜?Kšœ4˜4K˜—Kšœ˜K˜—šœžœžœžœ‘˜