DIRECTORY Basics USING [bitsPerWord], BasicTime USING [Now, GMT, Period, Update], Cursors, Imager, ImagerBackdoor, ImagerFont, ImagerTerminal, InputFocus, Interminal, PopUpMenu USING [], PrincOps USING [BBptr, BitAddress, BBTableSpace], PrincOpsUtils USING [AlignedBBTable, BITBLT], Process, Real USING [RoundC], Rope USING [ROPE], RuntimeError, Terminal, TIPUser, UserProfile, VFonts, ViewerClasses, ViewerLocks, ViewerPrivate; PopUpMenuImpl: CEDAR MONITOR IMPORTS BasicTime, Cursors, Imager, ImagerBackdoor, ImagerFont, ImagerTerminal, InputFocus, Interminal, PrincOpsUtils, Process, Real, RuntimeError, Terminal, TIPUser, UserProfile, VFonts, ViewerLocks, ViewerPrivate EXPORTS PopUpMenu = BEGIN localTipTable: Rope.ROPE = "PopUp.tip"; remoteTipTable: Rope.ROPE = "[Cedar]PopUpMenu>PopUp.tip"; mSaved: REF; -- pointer to save area bits mSaved2: REF; -- pointer to save area bits mHeight: INTEGER; -- menu height in mLines mWidthPixels: INTEGER; -- menu width in pixels mWidthWords: INTEGER; mWidthWords2: INTEGER; mContext: Imager.Context; -- menu context mX, mY: INTEGER; -- menu position lastMouse: Interminal.MousePosition; -- at start; remember between calls is only creaturecomfort mOnColor: BOOL; -- menu position; NOT current cursor position later lastRemoved: BasicTime.GMT _ BasicTime.Now[]; --for crazy heuristics allDone: CONDITION; reallyAllDone: BOOL _ TRUE; moduleFree: CONDITION; moduleOccupied: BOOL _ TRUE; --until initalized escaped: BOOL _ FALSE; blackBorderThick: INTEGER = 2; whiteBorderThick: INTEGER = 2; borderThick: INTEGER = blackBorderThick+whiteBorderThick; font: Imager.Font _ NIL; lineHeight: INTEGER; gSelection: CARDINAL _ 0; gTitleLines: CARDINAL; gLabel: Rope.ROPE; gChoice: LIST OF Rope.ROPE; gDefault: CARDINAL; gMouse: REF _ NIL; virtual: Terminal.Virtual _ Terminal.Current[]; frameBuffer: Terminal.FrameBuffer; frameBuffer2: Terminal.FrameBuffer; bitBlitTable: PrincOps.BBTableSpace; bitBlitTable2: PrincOps.BBTableSpace; blit: PrincOps.BBptr; blit2: PrincOps.BBptr; tipTable: TIPUser.TIPTable; BroadCast: ENTRY PROC [] = BEGIN reallyAllDone _ TRUE; BROADCAST allDone END; BroadCastTimeOut: ENTRY PROC [key: REF] = BEGIN reallyAllDone _ timedOut _ timeOutKey=key; IF reallyAllDone THEN BROADCAST allDone END; Wait: ENTRY PROC [] = BEGIN WHILE ~reallyAllDone DO WAIT allDone ENDLOOP; END; Enter: ENTRY PROC [] = BEGIN WHILE moduleOccupied DO WAIT moduleFree ENDLOOP; moduleOccupied _ TRUE END; Leave: ENTRY PROC [] = BEGIN moduleOccupied _ FALSE; BROADCAST moduleFree END; InitFontGlobals: UserProfile.ProfileChangedProc = BEGIN lineHeightX: INT _ MAX[-whiteBorderThick, MIN[20, UserProfile.Number[key: "PopUpMenu.LineHeightChange", default: 0]] ]; font _ VFonts.EstablishFont[ family: UserProfile.Token[key: "PopUpMenu.FontFamily", default: "Helvetica"], size: UserProfile.Number[key: "PopUpMenu.FontSize", default: 10], bold: UserProfile.Boolean[key: "PopUpMenu.FontBold", default: FALSE], italic: UserProfile.Boolean[key: "PopUpMenu.FontItalic", default: FALSE], defaultOnFailure: TRUE ]; lineHeight _ VFonts.FontHeight[font]+whiteBorderThick+lineHeightX; END; InitTipTable: PROC [] = BEGIN tryAgain: BOOL_FALSE; tipTable _ TIPUser.InstantiateNewTIPTable[localTipTable ! RuntimeError.UNCAUGHT => {tryAgain _ TRUE; CONTINUE} ]; IF tryAgain THEN tipTable _ TIPUser.InstantiateNewTIPTable[remoteTipTable]; END; BLTScreenToSaveArea: PROC [frameBuffer: Terminal.FrameBuffer, mWidthWords: INTEGER, mSaved: REF, blit: PrincOps.BBptr] = TRUSTED BEGIN screenPoint: PrincOps.BitAddress; screenPoint.word _ frameBuffer.base + (LONG[mY]*frameBuffer.wordsPerLine) + (LONG[mX]*frameBuffer.bitsPerPixel)/Basics.bitsPerWord; screenPoint.bit _ (mX*frameBuffer.bitsPerPixel) MOD Basics.bitsPerWord; blit.src _ screenPoint; blit.srcDesc.srcBpl _ frameBuffer.bitsPerPixel*frameBuffer.width; blit.dst _ [LOOPHOLE[mSaved],,0]; blit.dstBpl _ mWidthWords*Basics.bitsPerWord; blit.width_ mWidthPixels*frameBuffer.bitsPerPixel; blit.height _ mHeight; PrincOpsUtils.BITBLT[blit]; END; BLTSavedToScreen: PROC [frameBuffer: Terminal.FrameBuffer, mWidthWords: INTEGER, mSaved: REF, blit: PrincOps.BBptr] = TRUSTED BEGIN blit.dst _ blit.src; blit.dstBpl _ blit.srcDesc.srcBpl; blit.src _ [LOOPHOLE[mSaved],,0]; blit.srcDesc.srcBpl _ mWidthWords*Basics.bitsPerWord; PrincOpsUtils.BITBLT[blit]; END; SaveScreen: PROC [] = BEGIN BLTScreenToSaveArea[frameBuffer, mWidthWords, mSaved, blit]; IF frameBuffer2#NIL THEN BLTScreenToSaveArea[frameBuffer2, mWidthWords2, mSaved2, blit2]; END; RestoreScreen: PROC [] = BEGIN BLTSavedToScreen[frameBuffer, mWidthWords, mSaved, blit]; IF frameBuffer2#NIL THEN BLTSavedToScreen[frameBuffer2, mWidthWords2, mSaved2, blit2]; END; AllocateBitmap: PROC [w: CARDINAL] RETURNS [REF] = INLINE BEGIN Words: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF CARDINAL]; RETURN[NEW[Words[w]]]; END; SetUpTimeOutWatcher: PROC[i: INT] = BEGIN timedOut _ FALSE; IF i<=0 OR i>10000 THEN timeOutKey _ NIL --upper limit to prevent overflow ELSE { timeOutKey _ NEW[BasicTime.GMT _ BasicTime.Update[BasicTime.Now[], i]]; TRUSTED {Process.Detach[FORK TimeOutWatcherProcess[key: timeOutKey]]}; } END; timedOut: BOOL; timeOutKey: REF BasicTime.GMT _ NIL; --NIL means no time out expected; SetUpTimeOutWatcher is only writer TimeOutWatcherProcess: PROC [key: REF BasicTime.GMT] = BEGIN DO Process.Pause[Process.MsecToTicks[500]]; IF timeOutKey#key THEN RETURN; --invalid invocation of TimeOutWatcherProcess IF ~moduleOccupied THEN RETURN; --not timed out IF BasicTime.Period[from: key^, to: BasicTime.Now[]]>0 THEN { --time out BroadCastTimeOut[key]; }; ENDLOOP; END; PopUpNotify: ViewerClasses.NotifyProc = BEGIN ProtectedNotify: PROC [input: LIST OF REF ANY] = BEGIN IF reallyAllDone THEN BroadCast[] ELSE { FOR list: LIST OF REF ANY _ input, list.rest WHILE list#NIL DO WITH input.first SELECT FROM coords: TIPUser.TIPScreenCoords => MoveCursor[coords^.mouseX, frameBuffer.height-coords^.mouseY, coords^.color]; atom: ATOM => IF atom=$Done THEN BroadCast[] ELSE IF atom=$Escaped THEN { escaped _ TRUE; BroadCast[]; }; ENDCASE => NULL; ENDLOOP }; END; ProtectedNotify[input ! RuntimeError.UNCAUGHT => GOTO failed]; EXITS failed => BroadCast[]; END; PrepareMenu: PROC [] = BEGIN ComputedSize: PROC [label: Rope.ROPE, gChoice: LIST OF Rope.ROPE] RETURNS [lines: CARDINAL_0, width: CARDINAL_0] = INLINE BEGIN IF label#NIL THEN { lines _ 1; width _ Real.RoundC[ImagerFont.RopeWidth[font, label].x]; }; gTitleLines _ lines; FOR l: LIST OF Rope.ROPE _ gChoice, l.rest WHILE l#NIL DO lines _ lines+1; width _ MAX[width, Real.RoundC[ImagerFont.RopeWidth[font, l.first].x]]; ENDLOOP END; -- ComputedSize SetUpBW: PROC [] = BEGIN mOnColor _ FALSE; frameBuffer _ Terminal.GetBWFrameBuffer[virtual]; frameBuffer2 _ NIL; mContext _ ImagerTerminal.BWContext[vt: virtual, pixelUnits: TRUE]; END; -- SetUpBW SetUpColor: PROC [] = INLINE BEGIN m: Terminal.ColorMode = Terminal.GetColorMode[virtual]; IF m.full THEN { frameBuffer _ Terminal.GetColorFrameBufferA[virtual]; frameBuffer2 _ Terminal.GetColorFrameBufferB[virtual]; mContext _ ViewerPrivate.CreateContext[color]; } ELSE IF m.bitsPerPixelChannelA#0 THEN { frameBuffer _ Terminal.GetColorFrameBufferA[virtual]; frameBuffer2 _ NIL; mContext _ ViewerPrivate.CreateContext[color]; } ELSE SetUpBW[]; --ERROR, but what do else? IF frameBuffer=NIL THEN SetUpBW[]; END; -- SetUpColor mLines: CARDINAL; -- textlines of menu including header [lines: mLines, width: mWidthPixels] _ ComputedSize[gLabel, gChoice]; mWidthPixels _ mWidthPixels + 2*borderThick; mHeight _ mLines*lineHeight + 2*borderThick; IF mOnColor THEN SetUpColor[] ELSE SetUpBW[]; mWidthWords _ (mWidthPixels*frameBuffer.bitsPerPixel+Basics.bitsPerWord-1)/Basics.bitsPerWord; mSaved _ AllocateBitmap[mWidthWords*mHeight]; IF frameBuffer2#NIL THEN { mWidthWords2 _ (mWidthPixels*frameBuffer2.bitsPerPixel+Basics.bitsPerWord-1)/Basics.bitsPerWord; mSaved2 _ AllocateBitmap[mWidthWords2*mHeight]; }; mX _ MIN[MAX[0, lastMouse.mouseX], frameBuffer.width-mWidthPixels]; mY _ MIN[MAX[0, lastMouse.mouseY], frameBuffer.height-mHeight]; END; -- PrepareMenu RequestSelection: PUBLIC PROC [ label: Rope.ROPE _ NIL, choice: LIST OF Rope.ROPE, default: NAT _ 0, timeOut: NAT _ 0, mouse: REF _ NIL ] RETURNS [selection: INT _ 0] = BEGIN ENABLE UNWIND => Leave[]; PrepareRequestSelection: PROC [] = BEGIN Cursors.SetCursor[menu]; IF gMouse=NIL THEN { IF BasicTime.Period[from: lastRemoved, to: BasicTime.Now[]]>0 THEN lastMouse _ Interminal.GetMousePosition[]; } ELSE WITH gMouse SELECT FROM m: TIPUser.TIPScreenCoords => { frameBuffer: Terminal.FrameBuffer _ IF m.color THEN Terminal.GetColorFrameBufferA[virtual] ELSE Terminal.GetBWFrameBuffer[virtual]; lastMouse _ [mouseX: m.mouseX, mouseY: frameBuffer.height-m.mouseY, color: m.color]; }; m: REF Interminal.MousePosition => lastMouse _ m^; ENDCASE => lastMouse _ Interminal.GetMousePosition[]; mOnColor _ lastMouse.color; PrepareMenu[]; END; LockedRequestSelection: PROC [] = BEGIN escaped _ FALSE; SetUpScreen[]; CheatDefaultMousePos[]; reallyAllDone _ FALSE; SetUpTimeOutWatcher[timeOut]; Wait[]; RemoveMenu[]; lastRemoved _ BasicTime.Now[]; END; Enter[]; virtual _ Terminal.Current[]; InputFocus.PushInputFocus[]; InputFocus.CaptureButtons[PopUpNotify, tipTable]; gDefault _ default; gLabel _ label; gChoice _ choice; gMouse _ mouse; gSelection _ 0; DO PrepareRequestSelection[]; IF mOnColor THEN ViewerLocks.CallUnderColumnLock[LockedRequestSelection, color] ELSE ViewerLocks.CallUnderViewerTreeLock[LockedRequestSelection]; IF ~escaped THEN EXIT; gMouse _ $Escaped ENDLOOP; InputFocus.ReleaseButtons[]; InputFocus.PopInputFocus[]; IF timedOut THEN selection _ -1 ELSE selection _ gSelection; gMouse _ NIL; --just once! Leave[]; END; --RequestSelection SetUpScreen: PROC [] = BEGIN PaintMenu: PROC [] = INLINE BEGIN titelAddHight: CARDINAL = 3; baseLine: CARDINAL = 4; cH: CARDINAL _ mHeight-borderThick+baseLine; mContext.ClipRectangleI[mX, frameBuffer.height-mY-mHeight, mX+mWidthPixels, frameBuffer.height-mY]; mContext.TranslateT[[mX, frameBuffer.height-mY-mHeight]]; mContext.SetColor[Imager.white]; mContext.MaskBox[[0, 0, mWidthPixels, mHeight]]; mContext.SetColor[Imager.black]; mContext.MaskBox[[0, 0, mWidthPixels, blackBorderThick]]; mContext.MaskBox[[0, 0, blackBorderThick, mHeight]]; mContext.MaskBox[[0, mHeight-blackBorderThick, mWidthPixels, mHeight]]; mContext.MaskBox[[mWidthPixels-blackBorderThick, 0, mWidthPixels, mHeight]]; mContext.SetFont[font]; IF gLabel#NIL THEN { cH _ cH-lineHeight; mContext.SetXYI[borderThick, cH+titelAddHight]; mContext.ShowRope[rope: gLabel]; }; mContext.SetFont[font]; FOR l: LIST OF Rope.ROPE _ gChoice, l.rest WHILE l#NIL DO cH _ cH-lineHeight; mContext.SetXYI[borderThick, cH]; mContext.ShowRope[rope: l.first]; ENDLOOP; mContext.SetColor[ImagerBackdoor.invert]; IF gTitleLines=1 THEN mContext.MaskBox[ [blackBorderThick, mHeight-borderThick-lineHeight+1, mWidthPixels-blackBorderThick, mHeight-blackBorderThick] ] END; -- PaintMenu PrepareMenu[]; IF mOnColor THEN Terminal.ModifyColorFrame[virtual, SaveScreen] ELSE SaveScreen[]; PaintMenu[]; END; -- SetUpScreen RemoveMenu: PROC [] = INLINE BEGIN IF mOnColor THEN Terminal.ModifyColorFrame[virtual , RestoreScreen] ELSE RestoreScreen[]; mSaved _ mSaved2 _ NIL; frameBuffer _ frameBuffer2 _ NIL; END; -- RemoveMenu CheatDefaultMousePos: PROC [] = TRUSTED INLINE BEGIN m: Interminal.MousePosition; m _ Interminal.GetMousePosition[]; -- includes color bit IF gDefault#0 THEN { m.mouseX _ mX+mWidthPixels/2; m.mouseY _ ((gDefault-(1-gTitleLines))*lineHeight+mY+lineHeight/2); Interminal.SetMousePosition[m]; }; MoveCursor[m.mouseX, m.mouseY, m.color]; END; MoveCursor: PROC [x, y: INTEGER, col: BOOL] = BEGIN InvertPictureLine: PROC [pos: CARDINAL] = INLINE { cH: CARDINAL = mHeight-borderThick-lineHeight*pos; Imager.MaskBox[mContext, [borderThick, cH, mWidthPixels-borderThick, cH+lineHeight]]; }; sel: CARDINAL; IF col#mOnColor THEN sel _ 0 ELSE IF x<=(mX+blackBorderThick) OR x>=(mX+mWidthPixels-blackBorderThick) THEN sel _ 0 ELSE IF y<=(mY+borderThick) OR (y>=mY+mHeight-borderThick) THEN sel_0 ELSE sel _ (1-gTitleLines)+(y-borderThick-mY)/lineHeight; IF gSelection#sel THEN { IF gSelection#0 THEN InvertPictureLine[gSelection+gTitleLines]; gSelection _ sel; IF gSelection#0 THEN InvertPictureLine[gSelection+gTitleLines]; }; END; -- MoveCursor --module initialization TRUSTED { blit _ PrincOpsUtils.AlignedBBTable[@bitBlitTable]; blit.flags.disjoint _ TRUE; blit.flags.disjointItems _ TRUE; blit2 _ PrincOpsUtils.AlignedBBTable[@bitBlitTable2]; blit2.flags.disjoint _ TRUE; blit2.flags.disjointItems _ TRUE; }; UserProfile.CallWhenProfileChanges[InitFontGlobals]; InitTipTable[]; Leave[]; --initializes monitor locks END. ŽPopUpMenuImpl.mesa Copyright c 1983, 1985 by Xerox Corporation. All rights reserved. by Christian Jacobi, November 9, 1983 12:14 pm Last Edited by: Christian Jacobi, September 12, 1985 10:15:04 am PDT --global variables are protected with MONITOR Enter and Leave --initialized to mouse.color, but might be changed --EmergencyProcess for debugging purposes, if you think the notify proc hangs EmergencyProcess: PROC = BEGIN virtual: Terminal.Virtual _ Terminal.Current[]; DO -- forever Process.Pause[Process.SecondsToTicks[1]]; IF Terminal.GetKeys[virtual][ESC]=down THEN BroadCast[]; ENDLOOP; END; --BroadCast and Wait for scheduling interaction menu --Enter and Leave for scheduling module entrance --the hell, if font and lineHeight are not changed atomic... --do not catch errors again; land in debugger --globals mY, mX, mHeight, mWidthPixels --monitored call only --does not modify timeOutKey --never called with key=NIL --TimeOutWatcherProcess is NOT monitored --made sequential by viewer package --computes lines and width (in pixels) of menu and save area mContext _ ImagerTerminal.ColorContext[vt: virtual, pixelUnits: TRUE]; mContext _ ImagerTerminal.ColorContext[vt: virtual, pixelUnits: TRUE]; --PrepareMenu -- Only use real cursor coordinates if interval since last pupup menu is big enough -- Reuse same position if immediately following, and is different menu; -- Some few cases change position even if not clear, to make it possible -- to visualize area below pop up menu. --default: use mouseposition or last position according of time --else reuse last mouse position --RequestSelection --SetUpScreen --sets the cursor such that it points into the default field --use Interminal to do this: uses current intead of buffered mouseposition --MoveCursor --Cursors.GetCursorInfo[].hotX.. is already included in x, y by window package... --For debugging: TRUSTED {Process.Detach[FORK EmergencyProcess[]]}; Κ˜codešœ™Kšœ Οmœ7™BKšœ.™.KšœD™D—K˜šΟk ˜ Kšœžœ˜Kšœ žœžœ˜+K˜K˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ ˜ K˜ Kšœ žœ˜Kšœ žœ#˜1Kšœžœžœ˜-Kšœ˜Kšœžœ ˜Kšœžœžœ˜K˜ K˜ K˜K˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜K˜—šΟb œžœž˜šžœ˜KšœΞ˜Ξ—Kšžœ ˜—Kšžœ˜K˜Kšœžœ˜'Kšœžœ/˜HK˜Kšœ=™=K˜KšœžœΟc˜+Kšœ žœ ˜,Kšœ žœ ˜+Kšœžœ ˜.Kšœ žœ˜Kšœžœ˜Kšœ ˜+Kšœžœ ˜"Kšœ& ;˜ašœ žœ 5˜FKšœ4™4—Kšœžœ ˜DK˜Kšœ ž œ˜Kšœž œ˜Kšœ ž œ˜Kšœžœžœ ˜/Kšœ žœžœ˜K˜Kšœžœ˜Kšœžœ˜Kšœ žœ%˜9Kšœžœ˜Kšœ žœ˜K˜Kšœ žœ˜Kšœ žœ˜Kšœ žœ˜Kšœ žœžœžœ˜Kšœ žœ˜Kšœžœžœ˜K˜Kšœ/˜/Kšœ"˜"Kšœ#˜#K˜Kšœ%˜%Kšœ%˜%Kšœ˜Kšœ˜K˜Kšœ˜K˜Kš M™MšΟnœžœ™Kšž™Kšœ/™/šžœ  ™ Kšœ)™)Kšžœžœžœ ™8Kšžœ™—Kšžœ™K™—Kšœ4™4K˜š‘ œžœžœ˜Kšž˜Kšœžœ˜Kšž œ˜Kšžœ˜—K˜š‘œžœžœžœ˜)Kšž˜Kšœ+˜+Kšžœžœž œ˜'Kšžœ˜—K˜š‘œžœžœ˜Kšž˜šžœž˜Kšžœ˜ Kšžœ˜—Kšžœ˜K˜—Kšœ0™0K˜š‘œžœžœ˜Kšž˜šžœž˜Kšžœ ˜Kšžœ˜—Kšœž˜Kšžœ˜—K˜š‘œžœžœ˜Kšž˜Kšœžœ˜Kšž œ ˜Kšžœ˜—K˜šŸœ"˜1Kšžœ˜šœ žœžœžœ˜2KšœB˜BKšœ˜—šœ˜JšœN˜NJšœB˜BJšœ>žœ˜EJšœBžœ˜IJšœž˜Jšœ˜—KšœB˜BKšœ=™=Kšžœ˜—K˜š‘ œžœ˜Kšž˜Kšœ žœžœ˜šœ:˜:Kšœ žœžœžœ˜4Kšœ˜—Kšžœ žœ;˜KKšœ-™-Kšžœ˜K˜—š‘œžœ2žœ žœ˜yKšœ'™'Kšžœž˜ Kšœ!˜!šœ&˜&Kšœžœ ˜%Kšœžœ3˜8—Kšœ0žœ˜GK˜KšœA˜AKšœ žœ ˜!Kšœ-˜-Kšœ2˜2K˜Kšœžœ˜Kšžœ˜—K˜š‘œžœ2žœ žœ˜vKšžœž˜ Kšœ˜Kšœ"˜"Kšœ žœ ˜!Kšœ5˜5Kšœžœ˜Kšžœ˜—K˜š‘ œžœ˜Kšž˜Kšœ<˜<šžœžœž˜Kšœ@˜@—Kšžœ˜—K˜š‘ œžœ˜Kšž˜Kšœ9˜9šžœžœž˜Kšœ=˜=—Kšžœ˜—K˜š ‘œžœžœžœžœ˜3Kšžœž˜ Kšœžœžœžœžœžœžœžœ˜=Kšžœžœ ˜Kšžœ˜K˜—š‘œžœžœ˜#Kšœ™Kšž˜Kšœ ž˜Kš žœžœ žœžœ !˜Jšžœ˜Kšœ žœ žœ(˜GKšžœžœ*˜FK˜—Kšžœ˜—K˜Kšœ žœ˜šœ žœ žœžœ˜%Kš D˜D—K˜š‘œžœžœ žœ˜6Kšœ™Kš ™Kš (™(Kšž˜šž˜Kšœ(˜(Kšžœžœžœ -˜LKšžœžœžœ ˜/šžœ5žœ  ˜HKšœ˜K˜—Kšžœ˜—Kšžœ˜—K˜K˜šŸ œ˜(Kšœ#™#Kšž˜K˜š ‘œžœ žœžœžœžœ˜0Kšž˜Kšžœžœ ˜"šžœ˜šžœžœžœžœžœžœžœž˜>šžœ žœž˜šœ#˜#KšœM˜M—šœžœ˜Kšžœ žœ ˜šžœžœžœ˜Kšœ žœ˜Kšœ ˜ Kšœ˜——Kšžœžœ˜—Kšž˜—K˜—Kšž˜—K˜Kšœ%žœžœ ˜>Kšžœ˜Kšžœ˜—K˜š‘ œžœ˜Kšž˜K˜š ‘ œžœžœ žœžœžœ˜BKšžœ žœ žœ˜1Kšœ<™