DIRECTORY CD, CDBasics, CDCellsBackdoor, CDColors, CDDrawQueue, CDEnvironment USING [GetTipTable, GetIcon], CDEvents, CDLayers USING [CurrentLayer, LayerWidth], CDInstances, CDProperties, CDSequencer USING [Command, CommandRec, ExecuteCommand], CDValue, CDViewer, CDViewerBackdoor, CDVPrivate, CDVScale, CedarProcess USING [SetPriority, Priority], Cursors USING [CursorType], DebuggerSwap USING [WorryCallDebugger], InputFocus USING [SetInputFocus, PopInputFocus], InterminalBackdoor USING [terminal], IO, PrincOps USING [BBTableSpace], PrincOpsUtils USING [AlignedBBTable], Process USING [Detach, Yield], Rope, RuntimeError USING [UNCAUGHT], SafeStorage USING [ReclaimCollectibleObjects], Terminal USING [GetColorMode], TerminalIO, TIPUser USING [TIPScreenCoords], UserProfile USING [Boolean, ProfileChangedProc, CallWhenProfileChanges], ViewerClasses, ViewerEvents USING [EventProc, RegisterEventProc], ViewerOps USING [CreateViewer, RegisterViewerClass, PaintViewer, BlinkIcon, EnumProc, EnumerateViewers], WindowManager USING [colorDisplayOn]; CDVMain: CEDAR MONITOR IMPORTS CD, CDBasics, CDCellsBackdoor, CDColors, CDDrawQueue, CDEnvironment, CDEvents, CDInstances, CDLayers, CDProperties, CDSequencer, CDValue, CDViewer, CDViewerBackdoor, CDVPrivate, CDVScale, CedarProcess, DebuggerSwap, InputFocus, InterminalBackdoor, IO, PrincOpsUtils, Process, Rope, RuntimeError, SafeStorage, Terminal, TerminalIO, UserProfile, ViewerEvents, ViewerOps, WindowManager EXPORTS CDVPrivate SHARES CDViewerBackdoor, TerminalIO, CDDrawQueue = BEGIN -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- VRef: TYPE = CDVPrivate.VRef; Viewer: TYPE = ViewerClasses.Viewer; viewerClassAtom: ATOM = $ChipNDale; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- tryToPaint: CONDITION; notSupportedColorMode: PUBLIC ERROR = CODE; putNewViewerOnColor: BOOL _ TRUE; allVRefs: PUBLIC LIST OF VRef _ NIL; catchCritical, catchWedging: BOOL _ TRUE; errorRef: REF _ NIL; errorMsg: Rope.ROPE _ NIL; useForShallContinue: CDVPrivate.DebugProc _ DefaultDebug; UseDebug: PUBLIC PROC [proc: CDVPrivate.DebugProc] = { useForShallContinue _ proc }; DefaultDebug: PROC [ref: REF, wedge: BOOL, msg: Rope.ROPE] RETURNS [shallCont: BOOL] = { errorRef _ ref; errorMsg _ msg; shallCont _ catchCritical OR (wedge AND catchWedging); IF ~shallCont THEN DebuggerSwap.WorryCallDebugger["ChipNDale wedge"]; }; ShallContinue: PUBLIC PROC [ref: REF_NIL, wedge: BOOL_FALSE, msg: Rope.ROPE_NIL] RETURNS [yes: BOOL_TRUE] = { sc: CDVPrivate.DebugProc _ useForShallContinue; IF sc#NIL THEN yes _ sc[ref, wedge, msg]; }; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- terminalLock: BOOL _ FALSE; TerminalLock: PROC [] = { terminalLock _ TRUE; viewerClassRec.cursor _ cursorWhileInput }; TerminalFree: PROC [] ={ terminalLock _ FALSE; SetCursor[] }; TrackRef: TYPE = REF TrackRecord; TrackRecord: TYPE = RECORD [ pos: CD.Position ]; track: TrackRef _ NIL; GetTrackRef: PROC [p: CD.Position] RETURNS [t: TrackRef] = INLINE { t _ track; track _ NIL; IF t=NIL THEN t _ NEW[TrackRecord]; t.pos _ p }; DisposeTrackRef: PROC [t: TrackRef] = INLINE { track _ t }; RepaintRectAreaRef: TYPE = REF RepaintRectArea; RepaintRectArea: TYPE = RECORD[ rect: CD.Rect _ CDBasics.universe, erase: BOOL _ FALSE ]; viewerClassRec: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: PaintViewer, notify: NotifyViewer, modify: ModifyViewer, destroy: DestroyViewer, set: CDViewerBackdoor.CallSetProc, get: CDViewerBackdoor.CallGetProc, bltH: left, bltV: bottom, cursor: cursorNoFocus ]]; cursoredCDViewer: PUBLIC Viewer _ NIL; inputFocussedViewer: Viewer _ NIL; lastInputFocussedViewer: Viewer _ NIL; lastDesign: CD.Design _ NIL; cursorWithFocus: Cursors.CursorType = textPointer; cursorNoFocus: Cursors.CursorType = pointDown; cursorWhileInput: Cursors.CursorType = questionMark; SetCursor: PROC [] = INLINE { viewerClassRec.cursor _ IF terminalLock THEN cursorWhileInput ELSE IF cursoredCDViewer=inputFocussedViewer THEN cursorWithFocus ELSE cursorNoFocus; }; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- LastViewer: PUBLIC PROC [] RETURNS [Viewer] = { RETURN [lastInputFocussedViewer] }; SetUpAndRedraw: ENTRY PROC[vRef: VRef] = { ENABLE UNWIND => NULL; IF vRef=NIL THEN RETURN WITH ERROR CD.Error[]; CDDrawQueue.Flush[vRef.ct]; vRef.onVC _ FALSE; --erasing viewer automaticaly makes cursor invisible CDVPrivate.CreateDrawInformation[vRef]; CDDrawQueue.ChangeClipArea[vRef.ct, vRef.dClip]; CDDrawQueue.QueueInsertDrawCommand[vRef.ct, CDDrawQueue.Request[$redraw, CDBasics.universe]]; }; PaintViewer: ViewerClasses.PaintProc = { ENABLE { CDVPrivate.notSupportedColorMode => GOTO oops; RuntimeError.UNCAUGHT => IF ShallContinue[self, TRUE, "CDVMain.PV"] THEN GOTO oops ELSE REJECT; }; vRef: VRef; TrackRefTrack: ENTRY PROC [vRef: VRef, tr: TrackRef] = INLINE { ENABLE UNWIND => NULL; IF vRef.cursorInhibitations=0 THEN { IF vRef.onVC THEN vRef.usedCursor[vRef] ELSE { vRef.startVC _ vRef.designRec.startLC; vRef.firstHorizontalVC _ vRef.designRec.firstHLC; vRef.designRec.currentLayer _ CDLayers.CurrentLayer[vRef.actualDesign]; vRef.defaultWidthVC _ vRef.designRec.widthLC _ CDLayers.LayerWidth[vRef.actualDesign, vRef.designRec.currentLayer]; vRef.onVC _ TRUE; }; vRef.usedCursor _ vRef.designRec.outlineProcLC; vRef.stopVC _ tr.pos; vRef.usedCursor[vRef]; }; DisposeTrackRef[tr]; }; RemoveTrack: ENTRY PROC[vRef: VRef] = INLINE { ENABLE UNWIND => NULL; IF vRef.onVC THEN { vRef.usedCursor[vRef]; vRef.onVC _ FALSE; }; }; IF self.destroyed THEN RETURN; WITH self.data SELECT FROM vr: VRef => vRef _ vr; ENDCASE => RETURN; vRef.viewContext _ context; WITH whatChanged SELECT FROM tr: TrackRef => TrackRefTrack[vRef, tr]; -- called by NotifyViewer atom: ATOM => { IF atom=$RemoveTrack THEN RemoveTrack[vRef] ELSE CDViewerBackdoor.CallFurtherPaint[vRef, atom]; -- called from anywhere, maybe not protected }; area: RepaintRectAreaRef => -- protected by ProtectedRepaint CDVPrivate.RepaintRectAreaInViewer[vRef, area.rect, area.erase]; vSave: ViewerClasses.PaintRectangle => RepaintParts[vRef, vSave]; ENDCASE => { IF whatChanged=NIL THEN { IF vRef.viewer#self THEN RETURN; --initialization not finished SetUpAndRedraw[vRef] -- called from anywhere, maybe not protected } ELSE CDViewerBackdoor.CallFurtherPaint[vRef, whatChanged]; } EXITS oops => NULL; }; RepaintParts: PROC [vr: VRef, vSave: ViewerClasses.PaintRectangle] = { DrawOutside: PROC [r: CD.Rect] = { CDDrawQueue.QueueInsertDrawCommand[vr.ct, CDDrawQueue.Request[$redraw, r]]; }; r: CD.Rect; CDVPrivate.ResetDrawScale[vr]; IF vr.intendedScale#vr.scale THEN { DrawOutside[CDBasics.universe]; RETURN }; r _ CD.Rect[ x1: CDVScale.UngriddedViewerToDesignScalar[vr.scale, vSave.x-(vr.viewer.wx)]+vr.scale.off.x+1, y1: CDVScale.UngriddedViewerToDesignScalar[vr.scale, vSave.y-(vr.viewer.wy)]+vr.scale.off.y+1, x2: CDVScale.UngriddedViewerToDesignScalar[vr.scale, vSave.x+vSave.w-(vr.viewer.wx)]+vr.scale.off.x-1, y2: CDVScale.UngriddedViewerToDesignScalar[vr.scale, vSave.y+vSave.h-(vr.viewer.wy)]+vr.scale.off.y-1 ]; IF CDBasics.NonEmpty[r] THEN CDBasics.DecomposeRect[r: vr.dClip, test: r, outside: DrawOutside] ELSE DrawOutside[CDBasics.universe]; }; SetScaleAndRedraw: CDViewerBackdoor.FurtherPaintProc = { CDDrawQueue.Flush[me.ct]; ResetDrawScale[me]; SetUpAndRedraw[me]; }; ResetDrawScale: PUBLIC ENTRY PROC [vRef: VRef] = { ENABLE UNWIND => NULL; vRef.scale _ vRef.intendedScale; CDVPrivate.CreateDrawInformation[vRef]; CDDrawQueue.ChangeClipArea[vRef.ct, vRef.dClip]; }; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- EnableCursoring: ENTRY PROC[vRef: VRef] = INLINE { ENABLE UNWIND => NULL; vRef.cursorInhibitations _ vRef.cursorInhibitations-1; BROADCAST tryToPaint }; ProtectedRepaint: PROC[vRef: VRef, whatChanged: REF ANY] = { ENABLE RuntimeError.UNCAUGHT => { EnableCursoring[vRef]; IF ShallContinue[vRef, TRUE, "CDVMain.PR"] THEN GOTO oops ELSE REJECT }; DisableCursoring: ENTRY PROC[vRef: VRef] RETURNS [mustRemoveCursor: BOOL] = INLINE { ENABLE UNWIND => NULL; vRef.cursorInhibitations _ vRef.cursorInhibitations+1; WHILE vRef.cursorInhibitations>1 DO vRef.cursorInhibitations _ vRef.cursorInhibitations-1; WAIT tryToPaint; vRef.cursorInhibitations _ vRef.cursorInhibitations+1; ENDLOOP; mustRemoveCursor _ vRef.onVC; }; IF DisableCursoring[vRef].mustRemoveCursor THEN RemoveCursor[vRef]; ViewerOps.PaintViewer[vRef.viewer, client, FALSE, whatChanged ! RuntimeError.UNCAUGHT => IF ShallContinue[vRef, TRUE, "CDVMain.PR2"] THEN CONTINUE ]; EnableCursoring[vRef]; EXITS oops => NULL; }; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ViewerProcess: PROC[vRef: VRef] = { comm: REF CDDrawQueue.Request = NEW[CDDrawQueue.Request]; bBTableSpace1, bBTableSpace2: PrincOps.BBTableSpace; IF vRef.running THEN ERROR; TRUSTED { vRef.pBBptr _ PrincOpsUtils.AlignedBBTable[@bBTableSpace1]; vRef.xBBptr _ PrincOpsUtils.AlignedBBTable[@bBTableSpace2]; }; vRef.running _ TRUE; DO comm^ _ CDDrawQueue.FetchCommand[vRef.ct]; SELECT comm.key FROM $redraw => { paintArea: RepaintRectAreaRef = NEW[RepaintRectArea_[comm.rect, TRUE]]; ProtectedRepaint[vRef, paintArea]; }; $draw => { paintArea: RepaintRectAreaRef = NEW[RepaintRectArea_[comm.rect, FALSE]]; ProtectedRepaint[vRef, paintArea]; }; CDDrawQueue.queueEmpty => { ProtectedRepaint[vRef, $Temporaries]; CedarProcess.SetPriority[CedarProcess.Priority[background]]; SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE]; }; CDDrawQueue.finishedForEver => EXIT; ENDCASE => ProtectedRepaint[vRef, comm]; ENDLOOP; TerminalIO.PutRope["viewer destroyed\n"]; vRef.running _ FALSE; vRef.ct _ NIL; vRef.actualDesign _ NIL; vRef.deviceDrawRef _ NIL; vRef.painterList _ NIL; vRef.properties _ NIL; }; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- SlowDown: PROC [v: Viewer] = { IF v#NIL THEN WITH v.data SELECT FROM vRef: VRef => { vRef.hurryUp _ FALSE; vRef.slowDown _ TRUE; vRef.check _ TRUE; vRef.deviceDrawRef.checkPriority _ TRUE; }; ENDCASE => NULL; }; SpeedUp: PROC [v: Viewer] = { IF v#NIL THEN WITH v.data SELECT FROM vRef: VRef => { vRef.slowDown _ FALSE; vRef.hurryUp _ TRUE; vRef.check _ TRUE; vRef.deviceDrawRef.checkPriority _ TRUE; }; ENDCASE => NULL; }; RemoveCursor: PROC [vRef: VRef] = INLINE { IF vRef.onVC THEN ViewerOps.PaintViewer[vRef.viewer, client, FALSE, $RemoveTrack ! RuntimeError.UNCAUGHT => IF ShallContinue[vRef, TRUE, "CDVMain.RC"] THEN CONTINUE ]; }; ModifyViewer: ViewerClasses.ModifyProc = { -- PROC [self: Viewer, change: ModifyAction] ENABLE UNWIND => NULL; RemoveCursoring: PROC [from, now: Viewer] = TRUSTED { IF from#NIL AND now#NIL THEN WITH from.data SELECT FROM fromVr: VRef => { WITH now.data SELECT FROM nowVr: VRef => IF nowVr.actualDesign=fromVr.actualDesign THEN RETURN; ENDCASE => NULL; Process.Detach[FORK CDViewerBackdoor.CallFurtherNotify[fromVr, NIL]]; }; ENDCASE => NULL; }; SELECT change FROM set, pop => { IF lastInputFocussedViewer#self THEN RemoveCursoring[lastInputFocussedViewer, self]; lastInputFocussedViewer _ inputFocussedViewer _ self; }; kill, push => inputFocussedViewer _ NIL; ENDCASE => NULL; SetCursor[]; }; NotifyViewer: ViewerClasses.NotifyProc = { -- PROC [self: Viewer, input: LIST OF REF ANY] ENABLE RuntimeError.UNCAUGHT => IF ShallContinue[self, TRUE, "CDVMain.Notify"] THEN GOTO oops ELSE REJECT; vRef: VRef; mouse: CD.Position _ [0, 0]; --initialize, there are crazy tiptables. LogicalTrack: PROC [vRef: VRef, pos: CD.Position] = INLINE { IF NOT vRef.designRec.startLCValid THEN { vRef.designRec.startLC _ pos; vRef.designRec.startLCValid _ TRUE; } }; LogicalTrackOff: PROC [vRef: VRef, pos: CD.Position] = INLINE { vRef.designRec.stopLC _ pos; vRef.designRec.startLCValid _ FALSE; }; Track: PROC [vRef: VRef] = INLINE { VisibleTrack: PROC [vRef: VRef, pos: CD.Position] = INLINE { ViewerOps.PaintViewer[vRef.viewer, client, FALSE, GetTrackRef[pos] ]; }; pos: CD.Position = CDVScale.ViewerToDesignPosition[vRef.scale, mouse]; LogicalTrack[vRef, pos]; IF vRef.cursorInhibitations=0 THEN VisibleTrack[vRef, pos]; }; StopTrack: PROC [vRef: VRef] = { pos: CD.Position ~ CDVScale.ViewerToDesignPosition[vRef.scale, mouse]; vRef.hurryUp _ TRUE; LogicalTrackOff[vRef, pos]; RemoveCursor[vRef]; CDViewerBackdoor.CallFurtherNotify[vRef, NIL]; }; WITH self.data SELECT FROM vr: VRef => vRef _ vr; ENDCASE => RETURN; IF self#cursoredCDViewer THEN { tem: Viewer ~ cursoredCDViewer; IF vRef.deviceDrawRef=NIL THEN { RETURN }; IF tem#NIL THEN WITH tem.data SELECT FROM vRef: VRef => RemoveCursor[vRef]; ENDCASE => NULL; IF self.column=color THEN IF Terminal.GetColorMode[InterminalBackdoor.terminal].full THEN { IF self=inputFocussedViewer THEN InputFocus.PopInputFocus[]; RETURN; }; cursoredCDViewer _ self; SetCursor[]; }; WHILE input#NIL DO WITH input.first SELECT FROM atom: ATOM => { IF atom=$Track THEN Track[vRef] ELSE IF atom=$StopTrack THEN StopTrack[vRef] ELSE IF terminalLock THEN { IF atom#$UseCursor THEN ViewerOps.BlinkIcon[viewer: self, millisecondsPerBlink: 100]; RETURN; } ELSE { IF self#inputFocussedViewer THEN { SlowDown[inputFocussedViewer]; InputFocus.SetInputFocus[self]; SpeedUp[self]; IF atom=$CloseReSelectOnlyP THEN RETURN; }; IF atom=$UseCursor THEN { --command involving 2 atoms RemoveCursor[vRef]; input _ input.rest; IF input=NIL THEN RETURN; CDViewerBackdoor.CallFurtherNotify[vRef, input.first] } ELSE { -- all other (standard) commands data: REF _ NIL; StopTrack[vRef]; IF lastDesign#vRef.actualDesign THEN { data_lastDesign; lastDesign_vRef.actualDesign }; TRUSTED {Process.Detach[ FORK CDSequencer.ExecuteCommand[ design: vRef.actualDesign, comm: NEW[CDSequencer.CommandRec _ CDSequencer.CommandRec[ design: vRef.actualDesign, key: atom, pos: vRef.designRec.stopLC, sPos: vRef.designRec.startLC, l: vRef.designRec.currentLayer, ref: vRef, n: vRef.defaultWidthVC, b: vRef.designRec.firstHLC, data: data ]] ! CD.Error => IF ec=designMutability THEN { MessageBad[vRef.actualDesign, atom]; CONTINUE } ] ]}; }; }; }; coords: TIPUser.TIPScreenCoords => { mouse.x _ MIN[MAX[coords.mouseX, 0], vRef.viewer.cw-1]; mouse.y _ MIN[MAX[coords.mouseY, 0], vRef.viewer.ch-1]; }; ENDCASE => NULL; input _ input.rest ENDLOOP; EXITS oops => NULL; }; MessageBad: PROC [design: CD.Design, atom: ATOM_NIL] = TRUSTED { Process.Detach[FORK TerminalIO.PutF["**design %g is immutable; can not execute %g command\n", [rope[CD.DesignName[design]]], [atom[atom]] ]]; }; Caption: PROC [design: CD.Design, label: Rope.ROPE] RETURNS [Rope.ROPE] = { IF design=NIL THEN RETURN["nil design"]; RETURN [IO.PutFR["%g %g %g %g", [rope[label]], [rope[design.technology.name]], [rope[CDCellsBackdoor.PushedCellName[design]]], [rope[(SELECT design.mutability FROM editable => "" , readonly => "[READONLY]", inaccessible => "[IN-ACCESSIBLE]", ENDCASE => "[ACESSIBILITY UNDEFINED]" )]] ]]; }; EventCheckCaptionAndRedraw: CDEvents.EventProc = { FOR l: CDViewer.ViewerList _ CDViewer.ViewersOf[design], l.rest WHILE l#NIL DO WITH l.first.data SELECT FROM vRef: VRef => { IF event=$RenameDesign OR event=$ResetDesign THEN l.first.label _ design.name; l.first.name _ Caption[design, l.first.label]; ViewerOps.PaintViewer[l.first, caption]; IF event=$AfterPop OR event=$AfterPush THEN { CDDrawQueue.QueueInsertDrawCommand[vRef.ct, CDDrawQueue.Request[$redraw, CDBasics.universe]] } }; ENDCASE => NULL; ENDLOOP; }; EventSetTipTable: CDEvents.EventProc = { FOR l: CDViewer.ViewerList _ CDViewer.ViewersOf[NIL], l.rest WHILE l#NIL DO WITH l.first.data SELECT FROM vRef: VRef => { --so now we are sure l.first is a ChipNDale viewer l.first.tipTable _ CDEnvironment.GetTipTable[vRef.actualDesign]; }; ENDCASE => NULL; ENDLOOP; }; UseBBox: PROC [d: CD.Design] RETURNS [bb: CD.Rect _ [0, 0, 1, 1]] = { pushList: LIST OF CD.PushRec _ NIL; specific: CD.CellSpecific _ NIL; contents: CD.InstanceList _ NIL; IF d#NIL THEN pushList _ d.actual; IF pushList#NIL THEN specific _ pushList.first.specific; IF specific#NIL THEN contents _ specific.contents; IF contents=NIL THEN bb _ [0, 0, 800, 400] ELSE bb _ CDInstances.BoundingRectO[contents ! RuntimeError.UNCAUGHT => GOTO oops]; EXITS oops => NULL; }; CreateViewer: PUBLIC PROC[design: CD.Design] RETURNS [v: Viewer] = { vRef: VRef = New[design]; name: Rope.ROPE = CD.DesignName[design]; TRUSTED {Process.Detach[FORK ViewerProcess[vRef]]}; WHILE NOT vRef.running DO Process.Yield[] ENDLOOP; v _ vRef.viewer _ ViewerOps.CreateViewer[ flavor: viewerClassAtom, info: [ name: Caption[design, name], label: name, scrollable: FALSE, icon: CDEnvironment.GetIcon[design], iconic: FALSE, column: ColumnForNewViewer[], tipTable: CDEnvironment.GetTipTable[design], newVersion: design.edited, data: vRef ], paint: TRUE ]; vRef.dClip _ CDVScale.GetClipRecord[vRef.intendedScale, v.cw, v.ch]; CDViewer.ShowAndScale[v, UseBBox[design]]; CDDrawQueue.Flush[vRef.ct]; --I don't trust vRef vRef.scale _ vRef.intendedScale; vRef.dClip _ CDVScale.GetClipRecord[vRef.intendedScale, v.cw, v.ch]; CDDrawQueue.Flush[vRef.ct]; --I don't trust vRef CDDrawQueue.ChangeClipArea[vRef.ct, vRef.dClip]; CDDrawQueue.Flush[vRef.ct]; ViewerOps.PaintViewer[v, all]; EnableCursoring[vRef]; Include[vRef]; }; ColumnForNewViewer: PROC [] RETURNS [col: ViewerClasses.Column_left] = { colorDisplayEmpty: BOOL _ TRUE; CheckColorScreen: ViewerOps.EnumProc = {-- PROC [v: Viewer] RETURNS [BOOL _ TRUE] IF v.column=color AND ~v.iconic AND ~v.offDeskTop THEN RETURN [colorDisplayEmpty _ FALSE] }; IF WindowManager.colorDisplayOn AND putNewViewerOnColor THEN { ViewerOps.EnumerateViewers[CheckColorScreen]; IF colorDisplayEmpty THEN col _ color } }; DestroyViewer: ViewerClasses.DestroyProc = { WITH self.data SELECT FROM vRef: VRef => { Destroy[vRef]; self.data _ NIL; }; ENDCASE => NULL; }; ViewerCorDEvent: ViewerEvents.EventProc = { WITH viewer.data SELECT FROM vRef: VRef => CDDrawQueue.ChangeClipArea[vRef.ct, CDBasics.empty]; ENDCASE => NULL; IF cursoredCDViewer=viewer THEN cursoredCDViewer _ NIL }; ViewerChangeColEvent: ViewerEvents.EventProc = { IF viewer=cursoredCDViewer THEN IF Terminal.GetColorMode[InterminalBackdoor.terminal].full THEN cursoredCDViewer _ NIL; }; SetLabel: ViewerClasses.SetProc ~ { label: Rope.ROPE = WITH data SELECT FROM r: Rope.ROPE => r, t: REF TEXT => Rope.FromRefText[t], ENDCASE => NIL; IF label=NIL THEN RETURN; self.label _ label; self.name _ Caption[CDViewer.DesignOf[self], label]; ViewerOps.PaintViewer[self, caption]; }; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- NullOutline: CDVPrivate.OutLineProc = {}; New: PUBLIC ENTRY PROC [design: CD.Design] RETURNS [vRef: VRef_NIL] = { ENABLE UNWIND => NULL; NewScale: PROC [design: CD.Design] RETURNS [CDVScale.ScaleRec] = { scale: INT = CDValue.FetchInt[boundTo: design, key: $CDxInitScale, propagation: global, ifNotFound: 6]; grid: INT = CDValue.FetchInt[boundTo: design, key: $CDxInitGrid, propagation: global, ifNotFound: design.technology.lambda]; RETURN [ CDVScale.MakeScale[ nscale: MIN[MAX[scale, 0], CDVScale.scaleNum-1], grid: MIN[MAX[grid, 0], 512], off: [0, 0] ]]; }; InitDesignRec: PROC [vRef: VRef] = { FOR l: LIST OF VRef _ allVRefs, l.rest WHILE l#NIL DO IF vRef.actualDesign=l.first.actualDesign THEN { vRef.designRec _ l.first.designRec; RETURN }; ENDLOOP; vRef.designRec _ NEW[CDVPrivate.VPrivatePerDesign _ [ outlineProcLC: NullOutline, currentLayer: CD.errorLayer ]]; CDViewerBackdoor.CallFurtherNotify[vRef, NIL]; }; InitVRef: PROC [design: CD.Design] RETURNS [vRef: VRef] = { b: REF BOOL = NEW[BOOL_FALSE]; vRef _ NEW[CDVPrivate.VRec _ [ actualDesign: design, ct: CDDrawQueue.Create[design, b, CDBasics.empty], scale: NewScale[design], dClip: CDBasics.empty, intendedScale: NewScale[design], stoprequest: b, environment: CDProperties.GetDesignProp[design, $CDxDrawEnvironment]#$FALSE, symbolics: CDProperties.GetDesignProp[design, $CDxDrawSymbolics]#$FALSE, borders: CDProperties.GetDesignProp[design, $CDxSkipBorder]=$FALSE, fontSubstitution: CDProperties.GetDesignProp[design, $CDxSubstituteFonts]=$TRUE, personalColors: CDColors.globalColors, cursorInhibitations: 1, --disabled, not yet ready properties: CD.InitPropRef[] ]]; InitDesignRec[vRef]; }; vRef _ InitVRef[design]; }; Include: ENTRY PROC [vRef: VRef] = { allVRefs _ CONS[vRef, allVRefs]; }; Destroy: PUBLIC ENTRY PROC [vRef: VRef] = { ENABLE UNWIND => NULL; IF vRef#NIL THEN { IF allVRefs#NIL THEN { IF allVRefs.first=vRef THEN allVRefs _ allVRefs.rest ELSE { t: LIST OF VRef _ allVRefs; WHILE t.rest#NIL DO -- Assert t#NIL IF t.rest.first=vRef THEN t.rest _ t.rest.rest ELSE t _ t.rest ENDLOOP } }; CDDrawQueue.Destroy[vRef.ct]; } }; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- NoteProfileChange: UserProfile.ProfileChangedProc = { catchCritical _ UserProfile.Boolean["ChipNDale.CatchLowLevelErrors", TRUE]; catchWedging _ catchCritical OR UserProfile.Boolean["ChipNDale.CatchErrorsWhichCauseDeadlock", TRUE]; putNewViewerOnColor _ UserProfile.Boolean["ChipNDale.FirstViewerOnColor", TRUE]; }; UserProfile.CallWhenProfileChanges[NoteProfileChange]; TerminalIO.AddLock[TerminalLock, TerminalFree]; CDViewerBackdoor.InstallFurtherPaint[keyValue: $changeScale, proc: SetScaleAndRedraw]; CDViewerBackdoor.InstallFurtherPaint[keyValue: $flushed, proc: SetScaleAndRedraw]; CDEvents.RegisterEventProc[$ResetDesign, EventCheckCaptionAndRedraw]; CDEvents.RegisterEventProc[$RenameDesign, EventCheckCaptionAndRedraw]; CDEvents.RegisterEventProc[$AfterPush, EventCheckCaptionAndRedraw]; CDEvents.RegisterEventProc[$AfterPop, EventCheckCaptionAndRedraw]; CDEvents.RegisterEventProc[$MutabilityChange, EventCheckCaptionAndRedraw]; CDEvents.RegisterEventProc[$CDReRegisterTipTables, EventSetTipTable]; ViewerOps.RegisterViewerClass[viewerClassAtom, viewerClassRec]; [] _ ViewerEvents.RegisterEventProc[proc: ViewerCorDEvent, event: close, filter: viewerClassAtom, before: TRUE]; [] _ ViewerEvents.RegisterEventProc[proc: ViewerCorDEvent, event: destroy, filter: viewerClassAtom, before: TRUE]; [] _ ViewerEvents.RegisterEventProc[proc: ViewerChangeColEvent, event: changeColumn, filter: viewerClassAtom, before: TRUE]; CDViewerBackdoor.InstallSetProc[$Label, SetLabel]; -- permit dynamic relabelling of CD viewer END. ÎCDVMain.mesa (part of ChipNDale) Copyright c 1983, 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Created by: Christian Jacobi, June 24, 1983 3:33 pm Last Edited by: Christian Jacobi, April 28, 1987 6:36:44 pm PDT Jean-Marc Frailong October 12, 1987 11:51:45 pm PDT --monitoring rule: aquire the ViewerLock first, the monitor's entry lock only after. -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- types used for parameters to the viewer paint procedure --TrackRef: type to force cursor tracking --the Get and Dispose proc's are a hack to reduce the memory allocator's work --may be called by viewers NotifyViewer proc only; monitored through viewers NotifyViewer proc --RepaintRectAreaRef: type to force drawing a rectangular aera -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --logically inside the viewer's paint proc; --reset viewer data and then sets up a buffered request for redrawing -- erase to allow also backgrounds of arbitrary patterns or colors --PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] --depending on whatChanged, the call must be protected or need not. --Never call with modules entry monitor lock set. --vRef ABSOLUTELY never NIL {proc is local} --Proof hints: vRef.onVC initialized false; vRef.usedCursor not accessed outside -- CDVMains monitorlock (use Grep) --now vRef.onVC is true --vRef ABSOLUTELY never NIL {proc is local} --PaintViewer --here it would have trapped if vRef=NIL -- logicaly local to viewers paint proc (PaintViewer) -- logicaly local to viewers paint proc (PaintViewer) --logically local to ProtectedRepaint and initialization-- --vRef never nil-- --logically local to ProtectedRepaint --is outside to make callable from catch-phrase and initialization --does: --remove cursor and disables any cursoring process --let only one client come through --Caller must guarantee vRef#NIL (Use find; {proc neither exported nor assigned to variable}) --and enters protected region. --vRef never nil; guaranteed from caller {proc is local} --ProtectedRepaint --vRef.fooBBptr is a short pointer! (hardware) therefor must be local to some proc space. --do the garbage collection now, when not to much else is to do, --and also all the allocations of the drawing can be freed --order important --order important --removes visible cursor, if there is --monitores inside viewerpaintproc --RemoveCursoring from "from"; but not if "from" and "now" denote the same design --calling CDViewerBackdoor.CallFurtherNotify is not cosher but --calling with NIL mode is explicitely allowed -- ENTRY ommitted since sequential already by viewer package --makes cursor logically available --makes cursor logically unavailable --uses intermediate layer variable mouse --makes cursor visible --Track --uses intermediate layer variable mouse --NotifyViewer --silly Cedar Viewer package allows calls of notify before --the first call to the paint procedure happened; --but in ChipNDale, some initializations happens in paint procedure only. --luckily at that time cursoredCDViewer#self; so here is the only --place to check. --avoid running in 24 bit per pixel mode -- range test, -- [some crazy tiptables use coords without a mouse action first] -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- repaint captions and sometimes the contents --redraw everything, because -- after pop: cell change may have propagated -- after push: background features must be redrawn greyish --called on viewer creation [un-monitored] --must wait until vRef.fooBBLT is initialized by ViewerProcess --sorry; I would prefere not to paint here, but this is necessary to get --some initializations right; we should flush painting out anyway. -- but redraw does not yet come through... (clip area empty!) --Here we flush because it could be that the following PaintViewer must wait --until previous painting is finished. --Experiment showed that the PaintViewer is not necessary, but that might work --only because the paint from more above is slower than the previous Flush. --I prefere to be on the safe side at cost of speed. --selects colordisplay if it is on and free -- PROC [viewer: Viewer, event: ViewerEvent, before: BOOL] RETURNS [abort: BOOL _ FALSE] --we do this to force Notify to check whether we are in 24 bit per pixel mode Changes the label & caption. Caption is derived from label by adding technology, push level & accessibility. Default label is design name. Use this procedure by: ViewerOps.SetViewer[viewer: viewer, data: "New Label", op: $Label] --New --all critical work is done in procedures, so UNWIND really should work --allVRefs _ LO OPHOLE[List.DRemove[ref: vRef, list: LO OPHOLE[allVRefs]]]; -- PROC [reason: ProfileChangeReason] Êr˜codešœ"™"Kšœ ÏmœI™TKšœ4™4K™?K™3—šÏk ˜ Kšžœ˜K˜ Kšœ˜Kšœ ˜ Kšœ ˜ Kšœžœ˜+K˜ Kšœ žœ˜*Kšœ ˜ Kšœ ˜ Kšœ žœ'˜8Kšœ˜Kšœ ˜ Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ žœ˜+Kšœžœ˜Kšœ žœ˜'Kšœ žœ ˜0Kšœžœ ˜$Kšœ˜Kšœ žœ˜Kšœžœ˜%Kšœžœ˜Kšœ˜Kšœ žœžœ˜Kšœ žœ˜.Kšœ žœ˜Kšœ ˜ Kšœžœ˜ Kšœ žœ7˜HKšœ˜Kšœ žœ ˜2Kšœ žœY˜hKšœžœ˜%—K˜šÏnœžœžœ˜KšœT™Tšžœ˜Kšžœý˜ÿ—Kšžœ ˜Kšžœ,˜2—Kšž˜K˜KšÏcœ œ œ œ œ œ œ œ ˜5Kšœžœ˜Kšœžœ˜$K˜Kšœžœ˜#K˜Kš œ œ œ œ œ œ œ œ ˜5K˜Kšœ ž œ˜K˜Kšœžœžœžœ˜+Kšœžœžœ˜!K˜Kš œ žœžœžœžœ˜$K˜Kšœžœžœ˜)Kšœ žœžœ˜Kšœžœžœ˜Kšœ9˜9K˜šŸœž œ!˜6Kšœ˜Kšœ˜K˜—šŸ œžœžœ žœ žœžœ žœ˜XJšœ˜Jšœ˜Jšœžœžœ˜6šžœ ž˜Jšœ2˜2—J˜—K˜šŸ œžœžœžœžœ žœžœ žœžœžœžœžœ˜mJšœ/˜/Jšžœžœžœ˜)J˜—K˜Kš œ œ œ œ œ œ œ œ ˜5K˜Kšœžœžœ˜K˜šŸ œžœ˜Kšœžœ˜Kšœ(˜(Kšœ˜—K˜šŸ œžœ˜Kšœž˜Kšœ ˜ Kšœ˜—K˜Kšœ2™2K™:K˜Kšœ)™)˜Kšœ žœžœ˜"šœ žœžœ˜Kšœžœ ˜K˜—K˜KšœN™NKšœžœ˜K˜š Ÿ œžœžœ žœžœ˜CKšœ^™^Kšœžœ˜Kš žœžœžœžœžœ˜#Kšœ ˜ K˜—K˜šŸœžœžœ˜.Kšœ ˜ K˜—K˜—Kšœ?™?˜Kšœžœžœ˜0šœžœžœ˜Kšœžœ˜"Kšœžœž˜K˜——K™Kšœ2™2K˜šœ,žœ!˜PKšœ˜Kšœ˜Kšœ˜K˜Kšœ"˜"Kšœ"˜"Kšœ ˜ Kšœ ˜ Kšœ˜K˜—K™Kšœ2™2K˜Kšœžœ žœ˜&Kšœžœ˜"Kšœ"žœ˜&Kšœ žœ žœ˜˜Kšœ2˜2Kšœ.˜.Kšœ4˜4—K˜šŸ œžœžœ˜šœ˜Kšžœžœ˜%Kšžœžœ&žœ˜AKšžœ˜—K˜—K˜Kšœ5˜5K˜šŸ œžœžœžœ ˜/Kšžœ˜ Kšœ˜—K˜K˜šŸœžœžœ˜*Kšœ+™+K™EKšžœžœžœ˜Kšžœžœžœžœžœžœžœ ˜.Kšœ˜Kšœ žœ 4˜HKšœ'˜'šœ0˜0KšœB™B—Kšœ]˜]Kšœ˜—K˜šÏb œ˜(KšœQ™QKšœC™CKšœ1™1šžœ˜Kšœ$žœ˜.šœ žœ˜Kš žœžœžœžœžœžœ˜F—K˜—Kšœ ˜ K˜šŸ œžœžœžœ˜@Kšœ+™+Kšžœžœžœ˜šžœžœ˜$KšœQ™QKšœ"™"Kšžœ žœ˜'šžœ˜Kšœ&˜&Kšœ2˜2KšœG˜Gšœ.˜.KšœD˜D—Kšœ žœ˜K˜—Kšœ™Kšœ/˜/Kšœ˜K˜K˜—Kšœ˜Kšœ˜K˜—šŸ œžœžœžœ˜.Kšœ,™,Kšžœžœžœ˜šžœ žœ˜K˜Kšœ žœ˜K˜—Kšœ˜—K˜Kšœ ™ Kšžœžœžœ˜šžœ žœž˜Kšœ˜Kšžœžœ˜—šœ˜Kšœ  ™(—šžœ žœž˜Kšœ) ˜Bšœžœ˜Kšžœžœ˜+Kšžœ0 ,˜`K˜—šœ  ˜Kšœ ,˜AK˜—Kšžœ6˜:K˜——Kšžœ žœ˜Kšœ˜—K˜šŸ œžœ4˜FšŸ œžœžœ ˜"KšœK˜KKšœ˜—Kšœžœ˜ Kšœ˜šžœžœ˜#Jšœ˜Jšž˜J˜—šœžœ˜ Kšœ^˜^Kšœ^˜^Kšœf˜fKšœe˜eKšœ˜—šžœ˜KšžœC˜GKšžœ ˜$—Kšœ˜—K˜šÐbnœ'˜8Kš 5™5Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜šŸœž œžœ˜2Kš 5™5Kšžœžœžœ˜Kšœ ˜ Kšœ'˜'Kšœ0˜0Kšœ˜K˜K˜—Kš œ œ œ œ œ œ œ œ ˜5˜Kš¡;™;Kšœ™šŸœžœžœžœ˜3Kšœ%™%K™BKšžœžœžœ˜Kšœ6˜6Kšž œ ˜Kšœ˜K˜——šŸœžœžœžœ˜<šœ™Kšœ2™2Kšœ"™"—Kšœ]™]šžœžœ˜!Kšœ˜Kš žœžœžœžœžœž˜EKšœ˜—K˜š Ÿœžœžœ žœžœžœ˜TKšœ™Kšœ9™9Kšžœžœžœ˜K˜6šžœžœ˜$K˜6Kšžœ ˜K˜6Kšžœ˜—Kšœ˜Kšœ˜K˜—Kšœ™Kšžœ)žœ˜Cšœ+žœ˜>Kš œžœžœžœžœž˜TKšœ˜—K˜Kšžœ žœ˜Kšœ˜—K˜Kš œ œ œ œ œ œ œ œ ˜5K˜šŸ œžœ˜#Kšœžœžœ˜9šœ5˜5KšœY™Y—Kšžœžœžœ˜šžœ˜ Kšœ;˜;Kšœ;˜;Kšœ˜—Kšœžœ˜šž˜Kšœ*˜*šžœ ž˜˜ Kšœ žœžœ˜GKšœ"˜"Kšœ˜—˜ Kšœ žœžœ˜HKšœ"˜"Kšœ˜—šœ˜Kšœ%˜%Kšœ<˜Kš œžœžœžœžœž˜SKšœ˜——Kšœ˜—K˜šŸ œ -˜XKšžœžœžœ˜K˜šŸœžœžœ˜5Kš Q™Qš žœžœžœžœž˜šžœ žœž˜šœ˜šžœ žœž˜Kšœžœ(žœžœ˜EKšžœžœ˜—Kšœ?™?Kšœ.™.Kšœžœ,žœ˜EKšœ˜—Kšžœžœ˜——K˜—K˜šžœž˜šœ ˜ Kšžœžœ0˜TKšœ5˜5K˜—Kšœ$žœ˜(Kšžœžœ˜—Kšœ ˜ Kšœ˜—K˜š¡ œ˜*Kš /œ˜0Kšœ=™=šžœžœ˜ Kš žœžœžœžœžœžœ˜J—K˜Kšœ ˜ Kšœžœ )˜EK˜šŸ œžœžœ žœ˜˜>—Kšž˜Kšœ˜—šžœ˜šžœžœ˜"Kšœ˜Kšœ˜Kšœ˜Kšžœžœžœ˜)K˜—šžœžœ ˜5Kšœ˜Kš œžœžœžœžœ˜.Kšœ5˜5K˜—šžœ  ˜'Kšœžœžœ˜K˜šžœžœ˜&Kšœ-˜-Kšœ˜—šžœ˜šžœ˜ Kšœ˜šœžœ1˜:K˜K˜ Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ K˜Kšœ˜Kšœ ˜ Kšœ˜—šœžœ žœžœ˜+Kšœ%ž˜-K˜—Kšœ˜—Kšœ˜—Kšœ˜—Kšœ˜—K˜—˜$Kšœ™KšœB™BKšœ žœžœ'˜8Kšœ žœžœ&˜7K˜—Kšžœžœ˜—Kšœ˜Kšžœ˜—Kšžœ žœ˜Kšœ˜—K˜Kšœ8™8K˜š Ÿ œžœ žœžœžœžœ˜@šœžœJ˜]Kšœžœ#˜+Kšœ˜—K˜K˜—š Ÿœžœ žœžœžœžœ˜KKšžœžœžœžœ˜(šžœžœ˜"Kšœ˜Kšœ˜Kšœ/˜/šœžœžœ˜%Kšœ˜Kšœ˜Kšœ"˜"Kšžœ˜%Kšœ˜—K˜—Kšœ˜K˜—šŸœ˜2Kš .™.šžœ=žœžœž˜Nšžœžœž˜šœ˜Kšžœžœžœ˜NKšœ.˜.K˜(šžœžœžœ˜.Kš ™Kš .™.Kš ;™;Kšœ\˜\K˜—K˜—Kšžœžœ˜—Kšžœ˜—Kšœ˜K˜—š¢Ÿ œ˜(š žœ-žœ žœžœž˜Kšžœžœž˜šœ 2˜BJšœ@˜@K˜—Kšžœžœ˜—Kšžœ˜—Kšœ˜—K˜š Ÿœžœžœ žœžœ˜EKš *™*Kš œ žœžœžœ žœ˜$Kšœ žœžœ˜ Kšœ žœžœ˜ Kšžœžœžœ˜"Kšžœ žœžœ$˜8Kšžœ žœžœ˜2šžœ žœ˜Kšžœ˜Kšžœ8žœžœ˜S—Kšžœ žœ˜Kšœ˜—K˜š Ÿ œžœžœ žœ žœ˜DKšœ˜Kšœ žœžœ˜(Kšžœžœ˜3Kšœ>™>Kšžœžœžœžœ˜2šœ)˜)Kšœ˜˜Kšœ˜Kšœ ˜ Kšœ žœ˜Kšœ$˜$Kšœžœ˜Kšœ˜Kšœ-˜-Kšœ˜Kšœ ˜ K˜—šœž˜ KšœJ™JKšœD™D—K˜—KšœD˜Dšœ+˜+Kšœ=™=—Kšœ ˜0Kšœ!˜!KšœD˜DKšœ ˜0Kšœ0˜0šœ˜KšœL™LKšœ&™&KšœO™OKšœK™KKšœ4™4—Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜šŸœžœžœ%˜HKšœ+™+Kšœžœžœ˜š¡œ *˜Ršžœžœ žœžœ˜7Kšžœžœ˜"—Kšœ˜—šžœžœžœ˜?Kšœ-˜-Kšžœžœ ˜%K˜—Kšœ˜—K˜š¢ œ˜,šžœ žœž˜šœ˜Kšœ˜Kšœ žœ˜K˜—Kšžœžœ˜—Kšœ˜—K˜š¢œ˜+Kš œžœ.žœž œžœžœ™Xšžœ žœž˜KšœB˜BKšžœžœ˜—Kšžœžœž˜6Kšœ˜—K˜š¢œ˜0K™Mšžœžœ˜ šžœ9žœ˜AKšœžœ˜——Kšœ˜K˜—šŸœ˜#KšœŠ™ŠKšœY™Yšœ žœžœžœž˜(Kšœžœ˜Kšœžœžœ˜#Kšžœžœ˜—Kšžœžœžœžœ˜K˜Kšœ4˜4K˜%K˜—K˜KšŸ;˜;K˜KšŸ œ˜)K˜šŸœžœžœžœ žœ žœ žœ˜GKšžœžœžœ˜K˜šŸœžœ žœ žœ˜BKšœžœ]˜gKšœžœs˜|šžœ˜Kšœžœžœ"˜1Kšœžœžœ˜Kšœ ˜ Kšœ˜—Kšœ˜—K˜šŸ œžœ˜$š žœžœžœžœžœž˜5šžœ(žœ˜1Kšœ$ž˜*Kšœ˜—Kšžœ˜ —šœžœ!˜5Kšœ˜Kšœžœ ˜Kšœ˜—Kšœ)žœ˜.Kšœ˜K˜—šŸœžœ žœ žœ˜;Kš œžœžœžœžœžœ˜šœžœ˜K˜Kšœ2˜2Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜KšœL˜LKšœH˜HKšœC˜CKšœP˜PKšœ&˜&Kšœ ˜1Kšœ žœ˜K˜—K˜Kšœ˜—K™Kšœ™K™HKšœ˜Kšœ˜K˜—šŸœžœžœ˜$Kšœ žœ˜ K˜—K˜šŸœžœžœžœ˜+Kšžœžœžœ˜šžœžœžœ˜Kšœ ž œž œ ™Kšžœ žœžœ˜Kšžœžœ˜4šžœ˜Kšœžœžœ˜šžœžœžœ ˜$Kšžœžœ˜.Kšžœ ˜Kšž˜—K˜—K˜—Kšœ˜K˜—Kšœ˜K˜—KšŸ;˜;K˜š¡œ$˜5Kšœžœ™%KšœEžœ˜KKšœžœ@žœ˜eKšœJžœ˜PKšœ˜—K˜Kšœ6˜6K˜/KšœV˜VKšœR˜RK˜EK˜FK˜CK˜BKšœJ˜JKšœE˜EKšœ?˜?Kšœjžœ˜pKšœlžœ˜rKšœvžœ˜|šœ3 *˜]K˜—Kšžœ˜K™K™—…—VÖ‰