DIRECTORY Basics USING [bitsPerWord, LongMult, RawWords], BasicTime USING [GMT, Now, Period, Update], CedarProcess USING [Priority, SetPriority], Cursors USING [SetCursor], Cursory USING [SetMousePosition], Imager USING [black, ClipRectangleI, Context, DoSave, Font, MaskRectangleI, SetColor, SetFont, SetXY, ShowRope, white], ImagerFont USING [Extents, RopeBoundingBox], ImagerTerminal USING [BWContext], InputFocus USING [CaptureButtons, PopInputFocus, PushInputFocus, ReleaseButtons], Interminal USING [GetMousePosition, MousePosition], PopUpSelection2, PopUpSelection2Private, PrincOps USING [BBptr, BBTable, BBTableSpace, BitAddress, BitBltFlags, SrcDesc], PrincOpsUtils USING [AlignedBBTable, BITBLT], Process USING [Detach, MsecToTicks, Pause], Real USING [Fix, FixI, Round], Rope USING [InlineIsEmpty, ROPE], RuntimeError USING [UNCAUGHT], Terminal USING [ColorMode, Current, FrameBuffer, GetBWFrameBuffer, GetColorFrameBufferA, GetColorFrameBufferB, GetColorMode, ModifyColorFrame, Virtual], TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords, TIPTable], UserProfile USING [Boolean, CallWhenProfileChanges, Number, ProfileChangedProc, Token], VFonts USING [EstablishFont], ViewerClasses USING [NotifyProc], ViewerLocks USING [CallUnderColumnLock, CallUnderViewerTreeLock], ViewerPrivate USING [CreateContext]; PopUpSelection2Impl: CEDAR MONITOR IMPORTS Basics, BasicTime, CedarProcess, Cursors, Cursory, Imager, ImagerFont, ImagerTerminal, InputFocus, Interminal, PrincOpsUtils, Process, Real, Rope, RuntimeError, Terminal, TIPUser, UserProfile, VFonts, ViewerLocks, ViewerPrivate EXPORTS PopUpSelection2 = BEGIN OPEN PopUpSelection2; Menu: TYPE = REF MenuPrivate; MenuPrivate: PUBLIC TYPE = PopUpSelection2Private.MenuPrivate; sHeight, sWidth: INTEGER; -- size of screen, in pixels; [screen containing menu] mX, mY: INTEGER; -- menu position, in screen coordinates context: Imager.Context; -- menu context mOnColor: BOOL; -- menu [NOT cursor] on color screen lastRemoved: BasicTime.GMT _ BasicTime.Now[]; --for crazy heuristics lastMouse: Interminal.MousePosition; -- at start; remember between calls is creature comfort NotifyConsumer: Consumer _ NIL; notifyConsumerData: REF ANY _ NIL; reCheck: CONDITION _ [timeout: Process.MsecToTicks[1000]]; allDone: BOOL _ TRUE; --monitored moduleFree: BOOL _ FALSE; --monitored; initializations frees module escaped: BOOL _ FALSE; bordThick: NAT = 2; bordSep: NAT = 2; borderW: NAT = bordThick+bordSep; border2W: NAT = borderW+borderW; headSep: NAT = 2; gm: Menu; gRow, gCol, gSelection: NAT _ 0; gDefault: NAT; gMouse: REF _ NIL; gHelpOn: INTEGER _ -1; gHelpMode: BOOL _ FALSE; gXOrg, gYOrg: INTEGER _ 0; -- initial mouse position, in screen coords, relative to [mX, mY] vt: Terminal.Virtual _ Terminal.Current[]; --always use this terminal tipTable: TIPUser.TIPTable = TIPUser.InstantiateNewTIPTable["PopUpSelection2.tip"]; font: Imager.Font _ NIL; SetDoneAndNotify: PROC --must only be called from the notifier process, because it passes result to NotifyConsumer-- = { Consume: Consumer; sel: INT; data: REF ANY; [Consume, sel, data] _ SetDone[]; IF Consume # NIL THEN Consume[sel, data]; }; SetDone: ENTRY PROC RETURNS [Consume: Consumer, sel: INT, data: REF ANY] = { allDone _ TRUE; BROADCAST reCheck; IF escaped THEN Consume _ NIL ELSE Consume _ NotifyConsumer; sel _ IF timedOut THEN -1 ELSE gSelection; data _ notifyConsumerData; }; SetTimeOut: ENTRY PROC [key: REF] = { IF timeOutKey=key AND ~moduleFree THEN allDone _ timedOut _ TRUE; BROADCAST reCheck }; Wait: ENTRY PROC [] = { allDone _ FALSE; WHILE ~allDone DO WAIT reCheck ENDLOOP; }; Enter: ENTRY PROC [] = { WHILE ~moduleFree DO WAIT reCheck ENDLOOP; moduleFree _ FALSE }; Leave: ENTRY PROC [] = { moduleFree _ TRUE; BROADCAST reCheck }; SavedRec: TYPE = RECORD [ col: BOOL, x, y, w, h: INTEGER, fb1: Terminal.FrameBuffer_NIL, buffer1: REF_NIL, fb2: Terminal.FrameBuffer_NIL, buffer2: REF_NIL ]; bpWm1: NAT = Basics.bitsPerWord-1; bBFlags: PrincOps.BitBltFlags ~ PrincOps.BitBltFlags[ direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: null, dstFunc: null, reserved: 0 ]; PixToWords: PROC [n: INTEGER, bpp: NAT] RETURNS [INTEGER] = INLINE { RETURN [(n*bpp+bpWm1)/Basics.bitsPerWord] }; Save: PROC [x, y, w, h: INTEGER, col: BOOL_FALSE] RETURNS [s: REF SavedRec] = TRUSTED { blit: PrincOps.BBptr; AllocateBitmap: PROC [wW, hL: CARDINAL] RETURNS [REF] = TRUSTED INLINE { c: CARD _ Basics.LongMult[wW, hL]; IF c>=LAST[CARDINAL] THEN { s.h _ hL _ (LAST[CARDINAL]-1)/wW; c _ Basics.LongMult[wW, hL]; }; RETURN [NEW[Basics.RawWords[c]]]; }; Blit: PROC [fb: Terminal.FrameBuffer, buffer: REF] = TRUSTED INLINE { blit.flags _ bBFlags; blit.src _ PrincOps.BitAddress[ word: fb.base + Basics.LongMult[s.y, fb.wordsPerLine] + s.x*fb.bitsPerPixel/Basics.bitsPerWord, reserved: 0, bit: s.x*fb.bitsPerPixel MOD Basics.bitsPerWord ]; blit.srcDesc.srcBpl _ fb.bitsPerPixel*fb.width; blit.dst _ PrincOps.BitAddress[LOOPHOLE[buffer], 0, 0]; blit.dstBpl _ PixToWords[s.w, fb.bitsPerPixel]*Basics.bitsPerWord; blit.width _ s.w*fb.bitsPerPixel; blit.height _ s.h; PrincOpsUtils.BITBLT[blit]; }; DoWithFrame: PROC [] = TRUSTED { Blit[s.fb1, s.buffer1]; IF s.buffer2#NIL THEN Blit[s.fb2, s.buffer2]; }; bitBlitTable: PrincOps.BBTableSpace; blit _ PrincOpsUtils.AlignedBBTable[@bitBlitTable]; s _ NEW[SavedRec_[ col: col, x: x, y: y, w: w, h: h ]]; IF s.col THEN { s.fb1 _ Terminal.GetColorFrameBufferA[vt]; IF s.fb1=NIL THEN s.fb1 _ Terminal.GetBWFrameBuffer[vt];--bad color mode change s.buffer1 _ AllocateBitmap[PixToWords[s.w, s.fb1.bitsPerPixel], s.h]; IF Terminal.GetColorMode[vt].full THEN { s.fb2 _ Terminal.GetColorFrameBufferB[vt]; IF s.fb2#NIL THEN s.buffer2 _ AllocateBitmap[PixToWords[s.w, s.fb2.bitsPerPixel], s.h]; }; Terminal.ModifyColorFrame[vt, DoWithFrame]; } ELSE { s.fb1 _ Terminal.GetBWFrameBuffer[vt]; s.buffer1 _ AllocateBitmap[PixToWords[s.w, s.fb1.bitsPerPixel], s.h]; Blit[s.fb1, s.buffer1]; } }; Restore: PROC [s: REF SavedRec] = TRUSTED { blit: PrincOps.BBptr; Blit: PROC [fb: Terminal.FrameBuffer, buffer: REF] = TRUSTED INLINE { blit.flags _ bBFlags; blit.src _ PrincOps.BitAddress[LOOPHOLE[buffer], 0, 0]; blit.srcDesc.srcBpl _ PixToWords[s.w, fb.bitsPerPixel]*Basics.bitsPerWord; blit.dst _ PrincOps.BitAddress[ word: fb.base + Basics.LongMult[s.y, fb.wordsPerLine] + s.x*fb.bitsPerPixel/Basics.bitsPerWord, reserved: 0, bit: s.x*fb.bitsPerPixel MOD Basics.bitsPerWord ]; blit.dstBpl _ fb.bitsPerPixel*fb.width; blit.width _ s.w*fb.bitsPerPixel; blit.height _ s.h; PrincOpsUtils.BITBLT[blit]; }; DoWithFrame: PROC [] = TRUSTED { Blit[s.fb1, s.buffer1]; IF s.buffer2#NIL THEN Blit[s.fb2, s.buffer2]; }; bitBlitTable: PrincOps.BBTableSpace; blit _ PrincOpsUtils.AlignedBBTable[@bitBlitTable]; IF s.col THEN Terminal.ModifyColorFrame[vt, DoWithFrame] ELSE Blit[s.fb1, s.buffer1]; }; SetUpTimeOutWatcher: PROC [i: NAT] = { 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]]}; } }; timedOut: BOOL; timeOutKey: REF BasicTime.GMT _ NIL; --NIL means no time out expected; SetUpTimeOutWatcher is only writer TimeOutWatcherProcess: PROC [key: REF BasicTime.GMT] = { CedarProcess.SetPriority[excited]; DO Process.Pause[Process.MsecToTicks[500]]; IF timeOutKey#key THEN RETURN; --invalid invocation of TimeOutWatcherProcess IF moduleFree THEN RETURN; --not timed out IF BasicTime.Period[from: key^, to: BasicTime.Now[]]>0 THEN { --time out SetTimeOut[key]; }; ENDLOOP; }; helpSave: REF SavedRec _ NIL; NthRestFirstRope: PROC [list: LIST OF Rope.ROPE, n: INTEGER] RETURNS [r: Rope.ROPE_NIL] = INLINE { FOR l: LIST OF Rope.ROPE _ list, l.rest WHILE l#NIL DO IF n<=0 THEN {r _ l.first; EXIT}; n _ n-1; ENDLOOP; }; Box: PROC [x, y, w, h, border: INTEGER] = { Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, x, y, w, h]; Imager.SetColor[context, Imager.black]; Imager.MaskRectangleI[context, x, y, border, h]; Imager.MaskRectangleI[context, x+w-border, y, border, h]; Imager.MaskRectangleI[context, x, y, w, border]; Imager.MaskRectangleI[context, x, y+h-border, w, border]; }; HelpMessage: PROC [n: INTEGER] = { msg: Rope.ROPE _ NIL; Draw: PROC [] = { bordThick: NAT ~ 2; bordDist: NAT ~ 4; below: NAT ~ 2; y: INTEGER; e: ImagerFont.Extents _ ImagerFont.RopeBoundingBox[font, msg]; h: INTEGER _ Real.FixI[MIN[e.descent+e.ascent+2*bordDist]]; --text height w: INTEGER _ Real.FixI[MIN[e.rightExtent+e.leftExtent+2*bordDist+1, sWidth]]; --text width ty: INTEGER _ mY+gm.height+below; --text box's upper y in screen coordinates tx: INTEGER _ MIN[MAX[mX + (gm.width - w)/2, 0], sWidth-w]; --text box's left x in screen coordinates IF ty+h>=sHeight THEN { ty _ mY-h-below; IF ty<0 THEN RETURN; }; helpSave _ Save[tx, ty, w, h, mOnColor]; y _ sHeight-ty-h; Box[tx, y, w, h, bordThick]; Imager.SetColor[context, Imager.black]; Imager.SetXY[context, [(tx+bordDist)+e.leftExtent, y+bordDist+e.descent]]; Imager.SetFont[context, font]; Imager.ShowRope[context, msg]; }; IF n#gHelpOn THEN { gHelpOn _ n; IF helpSave#NIL THEN {Restore[helpSave]; helpSave _ NIL}; msg _ IF n = 0 THEN gm.doc ELSE IF n <= gm.choices.length AND gm.choices[n-1] # nullChoice THEN gm.choices[n-1].doc ELSE "no-op"; IF ~Rope.InlineIsEmpty[msg] THEN Imager.DoSave[context, Draw]; } }; MoveCursor: ENTRY PROC [x, y: INTEGER, onCol: BOOL] = { ENABLE UNWIND => NULL; row, col, sel: NAT _ 0; IF allDone THEN RETURN; IF onCol=mOnColor THEN { dx: INTEGER = x-mX-gm.arrayLeft; dy: INTEGER = y-mY-gm.arrayTopSep; IF dx IN [0 .. gm.arrayWidth) AND dy IN [0 .. gm.arrayHeight) THEN { row _ dy/gm.rowHeight; col _ dx/gm.colWidth; sel _ row*gm.cols + col + 1; } ELSE { gHelpMode _ TRUE; HelpMessage[0]; }; }; IF gSelection#sel THEN { ll: INTEGER ~ mX; by: INTEGER ~ sHeight-mY-gm.height; IF gSelection#0 THEN PaintEntry[ll, by, gRow, gCol, gSelection-1, FALSE]; gRow _ row; gCol _ col; gSelection _ sel; IF gSelection#0 THEN PaintEntry[ll, by, gRow, gCol, gSelection-1, TRUE]; IF gHelpMode THEN HelpMessage[gSelection]; }; }; PopUpNotify: ViewerClasses.NotifyProc = { ProtectedNotify: PROC [input: LIST OF REF ANY] = { IF allDone THEN SetDoneAndNotify[] 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, sHeight-coords^.mouseY, coords^.color]; atom: ATOM => IF atom=$Done THEN SetDoneAndNotify[] ELSE IF atom=$Escaped THEN { escaped _ TRUE; SetDoneAndNotify[]; }; ENDCASE => NULL; ENDLOOP }; }; ProtectedNotify[input ! RuntimeError.UNCAUGHT => GOTO failed]; EXITS failed => {gSelection _ 0; SetDoneAndNotify[]}; }; Create: PUBLIC PROC [choices: ChoiceS, doc: ROPE, header: Image _ NIL, left, top: Label _ nullLabel, columns: NAT _ 1, timeOut: NAT _ 0] RETURNS [m: Menu] = { m _ NEW [MenuPrivate _ [ choices: choices, header: header, doc: doc, left: left, top: top, rows: (choices.length + columns-1) / columns, cols: columns, timeOut: timeOut ]]; IF top # nullLabel THEN { m.colWidth _ MAX[m.colWidth, top.minSpacing]; m.tlSize _ top.minWidth; }; IF left # nullLabel THEN { m.rowHeight _ MAX[m.rowHeight, left.minSpacing]; m.llSize _ left.minWidth; }; FOR i: NAT IN [0 .. choices.length) DO IF choices[i] # nullChoice THEN { m.rowHeight _ MAX[m.rowHeight, Ceiling[choices[i].image.size.y]]; m.colWidth _ MAX[m.colWidth, Ceiling[choices[i].image.size.x]]; }; ENDLOOP; m.arrayTopSep _ borderW + m.tlSize; IF header # NIL THEN m.arrayTopSep _ m.arrayTopSep + headSep + Ceiling[header.size.y]; m.arrayHeight _ m.rows * m.rowHeight; m.arrayWidth _ m.cols * m.colWidth; m.height _ m.arrayTopSep + m.arrayHeight + borderW; m.width _ m.arrayWidth + m.llSize + border2W; m.arrayLeft _ bordThick + bordSep + m.llSize; m.arrayBot _ bordThick + bordSep; }; Pop: PUBLIC PROC [menu: Menu, default: NAT _ 0, position: REF _ NIL, InNotifier: Consumer _ NIL, notifyData: REF ANY _ NIL] RETURNS [selection: INT _ 0] = { Enter[]; {ENABLE UNWIND => {NotifyConsumer _ NIL; Leave[]}; WithViewerLock: PROC [] = { saved: REF SavedRec _ PrepareMenu[]; Imager.DoSave[context, PaintMenu]; HelpMessage[default]; CheatMousePos[]; SetUpTimeOutWatcher[gm.timeOut]; Wait[]; RemoveMenu[saved]; }; InputFocus.PushInputFocus[]; InputFocus.CaptureButtons[PopUpNotify, tipTable]; Cursors.SetCursor[menu]; gm _ menu; gDefault _ default; gMouse _ position; NotifyConsumer _ InNotifier; notifyConsumerData _ notifyData; IF (gSelection _ default) # 0 THEN { gRow _ (default-1)/menu.cols; gCol _ default-1 - gRow * menu.cols; gXOrg _ menu.arrayLeft + gCol * menu.colWidth + menu.colWidth / 2; gYOrg _ menu.arrayTopSep + gRow * menu.rowHeight + menu.rowHeight/2; } ELSE { gXOrg _ menu.width/2; gYOrg _ borderW + Real.Round[menu.header.size.y/2]; }; gHelpMode _ TRUE; DO ComputeMenuPosition[]; escaped _ FALSE; gHelpOn _ -1; IF mOnColor THEN ViewerLocks.CallUnderColumnLock[WithViewerLock, color] ELSE ViewerLocks.CallUnderViewerTreeLock[WithViewerLock]; IF ~escaped THEN EXIT; gMouse _ $Escaped; IF (gSelection _ default) # 0 THEN { gRow _ (default-1)/menu.cols; gCol _ default-1 - gRow * menu.cols; }; ENDLOOP; NotifyConsumer _ NIL; InputFocus.ReleaseButtons[]; InputFocus.PopInputFocus[]; IF timedOut THEN selection _ -1 ELSE selection _ gSelection; gMouse _ NIL; }; Leave[]; }; ComputeMenuPosition: PROC [] = INLINE { DefaultPos: PROC [] = INLINE{ IF BasicTime.Period[from: lastRemoved, to: BasicTime.Now[]]>0 THEN lastMouse _ Interminal.GetMousePosition[]; }; IF gMouse=NIL THEN DefaultPos[] ELSE WITH gMouse SELECT FROM m: TIPUser.TIPScreenCoords => { frame: Terminal.FrameBuffer _ IF m.color THEN Terminal.GetColorFrameBufferA[vt] ELSE Terminal.GetBWFrameBuffer[vt]; lastMouse _ [mouseX: m.mouseX, mouseY: frame.height-m.mouseY, color: m.color]; }; m: REF Interminal.MousePosition => lastMouse _ m^; ENDCASE => lastMouse _ Interminal.GetMousePosition[]; mOnColor _ lastMouse.color; }; PrepareMenu: PROC [] RETURNS [saved: REF SavedRec] = INLINE { frameBuffer: Terminal.FrameBuffer_NIL; IF mOnColor THEN { frameBuffer _ Terminal.GetColorFrameBufferA[vt]; context _ ViewerPrivate.CreateContext[color]; }; IF ~mOnColor OR frameBuffer=NIL THEN { mOnColor _ FALSE; frameBuffer _ Terminal.GetBWFrameBuffer[vt]; context _ ImagerTerminal.BWContext[vt: vt, pixelUnits: TRUE]; }; sHeight _ frameBuffer.height; sWidth _ frameBuffer.width; mX _ MIN[MAX[0, lastMouse.mouseX - gXOrg], sWidth-gm.width]; mY _ MIN[MAX[0, lastMouse.mouseY - gYOrg], sHeight-gm.height]; saved _ Save[mX, mY, gm.width, gm.height, mOnColor]; context.SetFont[font]; }; RemoveMenu: PROC [saved: REF SavedRec] = INLINE { IF helpSave#NIL THEN { Restore[helpSave]; helpSave _ NIL }; Restore[saved]; lastRemoved _ BasicTime.Now[]; }; PaintMenu: PROC [] = { tidleAdd: NAT ~ 3; baseLine: NAT ~ 4; ll: INTEGER ~ mX; by: INTEGER ~ sHeight-mY-gm.height; h: INTEGER _ gm.height-borderW+baseLine; i, row, col: NAT _ 0; Imager.ClipRectangleI[context, ll, by, gm.width, gm.height]; Box[ll, by, gm.width, gm.height, bordThick]; IF gm.header # NIL THEN gm.header.Draw[gm.header, context, [ll + borderW, by + gm.height - borderW - gm.header.size.y, gm.width - 2*borderW, gm.header.size.y], FALSE]; IF gm.left # NIL THEN gm.left.Draw[context, [ll + gm.arrayLeft, by + gm.arrayBot + gm.arrayHeight], gm.rows, gm.rowHeight, gm.llSize, gm.left.data]; IF gm.top # NIL THEN gm.top.Draw[context, [ll + gm.arrayLeft, by + gm.arrayBot + gm.arrayHeight], gm.cols, gm.colWidth, gm.tlSize, gm.top.data]; FOR i: NAT IN [0 .. gm.choices.length) DO PaintEntry[ll, by, row, col, i, gSelection = i+1]; IF (col _ col + 1) = gm.cols THEN {col _ 0; row _ row + 1}; ENDLOOP; }; PaintEntry: PROC [ll, by: INTEGER, row, col, i: NAT, highlight: BOOL] = { image: Image = IF i < gm.choices.length THEN gm.choices[i].image ELSE NIL; IF image # NIL THEN image.Draw[image, context, [ll + gm.arrayLeft + col * gm.colWidth, by + gm.arrayBot + (gm.rows - row - 1) * gm.rowHeight, gm.colWidth, gm.rowHeight], highlight]; }; CheatMousePos: PROC [] = INLINE { Cursory.SetMousePosition[vt, [mX + gXOrg, mY + gYOrg]--ignores color bit--]; }; InitFontGlobals: UserProfile.ProfileChangedProc = { font _ VFonts.EstablishFont[ family: UserProfile.Token["PopUpSelection.FontFamily", "Helvetica"], size: UserProfile.Number["PopUpSelection.FontSize", 10], bold: UserProfile.Boolean["PopUpSelection.FontBold", FALSE], italic: UserProfile.Boolean["PopUpSelection.FontItalic", FALSE], defaultOnFailure: TRUE ]; }; Ceiling: PROC [r: REAL] RETURNS [i: INTEGER] = { d: INT = Real.Fix[r] + 1; i _ Real.Fix[r-d]+d}; Floor: PROC [r: REAL] RETURNS [i: INTEGER] = { d: INT = Real.Fix[r] - 1; i _ Real.Fix[r-d]+d}; ChoiceList: TYPE = LIST OF Choice; MakeChoices: PROC [asList: ChoiceList] RETURNS [choices: ChoiceS] = { len: NAT _ 0; FOR l: ChoiceList _ asList, l.rest WHILE l # NIL DO len _ len + 1 ENDLOOP; choices _ NEW [ChoiceSequence[len]]; FOR i: NAT IN [0 .. len) DO choices[i] _ asList.first; asList _ asList.rest; ENDLOOP; }; UserProfile.CallWhenProfileChanges[InitFontGlobals]; Leave[]; --initializes monitor locks END. ζPopUpSelection2Impl.Mesa Copyright c 1983, 1986 by Xerox Corporation. All rights reserved. Created by Christian Jacobi, November 9, 1983 12:14 pm Last Edited by: Christian Jacobi, August 26, 1986 10:31:27 am PDT Last Edited by: Pier, June 23, 1986 11:39:21 am PDT Mike Spreitzer November 14, 1986 6:26:47 pm PST --global variables are protected with MONITOR Enter and Leave DebugProcess: PROC = { DO -- forever Process.Pause[Process.MsecToTicks[1000]]; IF Terminal.GetKeys[vt][ESC]=down THEN SetDone[]; ENDLOOP; }; --SetDone and Wait for scheduling interaction menu --Enter and Leave for scheduling module entrance -- saving and restoring screen contents --x, y, w, h in pixels --wW: width in words; hL: height in lines --in this case we cant allocate the words --we don't care about uglyness, but please don't wedge! --timeout nonsense --monitored call only --does not modify timeOutKey --never called with key=NIL --TimeOutWatcherProcess is NOT monitored --help and move tx: INTEGER _ MIN[mX, sWidth-w]; --text boxes left x in screen coordinates --Cursors.GetCursorInfo[].hotX.. is already included in x, y by window package... --made sequential by viewer package -- the main stuff -- sets variables lastMouse and mOnColor -- Only use real cursor coordinates if interval since last popup menu call is big enough -- Reuse same position if call is immediately following, and is using a different menu; -- In doubt change position, to make it possible to visualize area below pop up menu. --default: use position position or last position according of time --else reuse last position --must be called with DoSave; changes state --sets the cursor such that it points into the default field, or header TRUSTED {Process.Detach[FORK DebugProcess[]]}; Κ€˜codešœ™Kšœ Οmœ7™BKšœ6™6K™AK™3K™/—K˜šΟk ˜ Kšœžœ#˜0Kšœ žœžœ˜,Kšœ žœ˜+Kšœžœ ˜Kšœžœ˜#Kšœžœk˜xKšœ žœ˜-Kšœžœ ˜!Kšœ žœA˜QKšœ žœ#˜4Kšœ˜Kšœ˜Kšœ žœB˜QKšœžœžœ˜-Kšœžœ˜,Kšœžœ˜Kšœžœžœ˜"Kšœ žœžœ˜Kšœ žœŠ˜™Kšœžœ5˜CKšœ žœF˜XKšœžœ˜Kšœžœ˜!Kšœ žœ0˜AKšœžœ˜$K˜—šΟbœžœž˜"Kšžœδ˜λKšžœ˜—Kšžœžœ˜K˜Kšœžœžœ ˜Kšœ žœžœ&˜>K˜Kšœ=™=K˜KšœžœΟc6˜QKšœžœ (˜9Kšœ ˜*Kšœ žœ '˜8Kšœžœ ˜DKšœ& 7˜]KšΟnœ žœ˜Kšœžœžœžœ˜"K˜Kšœ ž œ(˜:K˜Kšœ žœžœ  ˜"Kšœ žœžœ *˜DKšœ žœžœ˜K˜Kšœ žœ˜Kšœ žœ˜Kšœ žœ˜!Kšœ žœ˜ Kšœ žœ˜K˜K˜ Kšœžœ˜ Kšœ žœ˜Kšœžœžœ˜Kšœ žœ˜Kšœ žœžœ˜Kšœžœ A˜\K˜Kšœ+ ˜EKšœS˜SKšœžœ˜K˜š‘ œžœ™šžœ  ™ Kšœ)™)Kšžœžœžœ ™1Kšžœ™—Kšœ™K™—K™Kšœ2™2K˜š‘œžœ ]œ˜xKš‘œ ˜Kšœžœ˜ Kšœžœžœ˜Kšœ!˜!Kšžœ žœžœ˜)K˜K˜—š‘œžœžœžœ‘œžœžœžœ˜LKšœ žœ˜Kšž œ ˜Kšžœ žœ žœžœ˜˜>Kšœžœ žœ"  ˜IKšœžœ žœ4  ˜[Kšœžœ *˜LKšœžœžœ )™JKšœžœžœžœ' )˜ešžœžœ˜Kšœ˜Kšžœžœžœ˜K˜—Kšœ(˜(Kšœ˜Kšœ˜Kšœ'˜'KšœJ˜JK˜Kšœ˜Kšœ˜—šžœ žœ˜Kšœ ˜ Kšžœ žœžœžœ˜9Kšœžœžœžœžœžœžœžœ ˜Kšžœžœ˜>K˜—K˜—K˜š ‘ œžœžœžœ žœ˜7Kšžœžœžœ˜Kšœžœ˜KšœQ™QKšžœ žœžœ˜šžœžœ˜Kšœžœ˜ Kšœžœ˜"š žœžœžœžœžœ˜DK˜K˜K˜K˜—šžœ˜Kšœ žœ˜Kšœ˜K˜—K˜—šžœžœ˜Kšœžœ˜Kšœžœ˜#Kšžœžœ.žœ˜IK˜ K˜ K˜Kšžœžœ.žœ˜HKšžœ žœ˜*Kšœ˜—Kšœ˜—K˜šΠbn œ˜)Kšœ#™#K˜š ‘œžœ žœžœžœžœ˜2Kšžœ žœ˜#šžœ˜šžœžœžœžœžœžœžœž˜>šžœ žœž˜šœ#˜#KšœB˜B—šœžœ˜Kšžœ žœ˜%šžœžœžœ˜Kšœ žœ˜Kšœ˜Kšœ˜——Kšžœžœ˜—Kšž˜—K˜—Kšœ˜—K˜Kšœ%žœžœ ˜>Kšžœ0˜5Kšœ˜—K˜K™K™K˜š‘œžœžœžœžœ)žœžœžœ˜žšœžœ˜K˜K˜K˜ K˜ K˜ K˜-Kšœ˜K˜K˜—šžœžœ˜Kšœ žœ˜-K˜Kšœ˜—šžœžœ˜Kšœžœ˜0K˜Kšœ˜—šžœžœžœž˜&šžœžœ˜!Kšœžœ0˜AKšœ žœ/˜?K˜—Kšžœ˜—Kšœ#˜#Kšžœ žœžœB˜VKšœ%˜%Kšœ#˜#Kšœ3˜3Kšœ-˜-K˜-K˜!K˜—K˜š‘œžœžœžœžœžœ‘ œ žœžœžœžœžœ žœ ˜œšœ˜Kšœžœžœžœ ˜2š‘œžœ˜Kšœžœ˜$Kšœ"˜"Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜—K˜Kšœ1˜1Kšœ˜K˜ Kšœ˜Kšœ˜Kšœ˜K˜ šžœžœ˜$K˜K˜$K˜BKšœD˜DK˜—šžœ˜Kšœ˜K˜3K˜—Kšœ žœ˜šž˜Kšœ˜Kšœ žœ˜Kšœ ˜ Kšžœ žœ7˜GKšžœ5˜9Kšžœ žœžœ˜Kšœ˜šžœžœ˜$K˜K˜$K˜—Kšžœ˜—Kšœžœ˜K˜K˜Kšžœ žœžœ˜Jšœ4˜4Kšœ˜Kšœ˜—K˜š‘ œžœ žœ žœ˜1šžœ žœžœ˜Kšœ˜Kšœ ž˜Kšœ˜—Kšœ˜Kšœ˜Kšœ˜—K˜š‘ œžœ˜K™+Kšœ žœžœ˜%Kšœžœ˜Kšœžœ˜#Kšœžœ˜(Kšœ žœ˜Kšœ<˜`€