<> <> <> <> <<>> <> <<>> <> <<>> DIRECTORY Ascii USING [BS, ControlA, ControlW, ControlQ, ControlS, CR, DEL, ESC], BiScrollers USING [BiScroller, ClientCoords, ClientDataOf, QuaBiScroller, QuaViewer], Geom2D USING [Vec], ImagerTransformation USING [Transform], InputFocus USING [SetInputFocus], Interminal USING [GetCursorOffset, SetCursorOffset], MessageWindow USING [Append], Real USING [RoundI], Rope USING [ROPE], Terminal USING [Current, Position, SetBWCursorPosition, SetColorCursorPosition, Virtual], TIPUser USING [TIPTable, TIPScreenCoords], ViewerClasses USING [ModifyProc, NotifyProc, Viewer], ViewerOps USING [UserToScreenCoords], Cursors USING [SetCursor, CursorType], SilUserInput, SilKernel, SilFile ; SilUserInputImpl: CEDAR MONITOR LOCKS uiData USING uiData: SilUIData IMPORTS BiScrollers, MessageWindow, ImagerTransformation, InputFocus, Real, ViewerOps, Interminal, Terminal, Cursors EXPORTS SilUserInput, SilKernel = BEGIN ROPE: TYPE = Rope.ROPE; SilData: TYPE = SilKernel.SilData; UInput: TYPE = SilUserInput.UInput; UInputRec: TYPE = SilUserInput.UInputRec; TIPScreenCoords: TYPE = TIPUser.TIPScreenCoords; <> SilUIData: TYPE = REF SilUIDataRec; SilUIDataRec: PUBLIC TYPE = MONITORED RECORD [ <> queue: UInput _ NIL, gotInput: CONDITION, innerViewer: ViewerClasses.Viewer, --two viewers to use BiScrollers properly outerViewer: ViewerClasses.Viewer, bs: BiScrollers.BiScroller, gotInputFocus: BOOL _ FALSE, caretsShouldChangeNow: BOOL _ FALSE, <> gridSpacing: NAT _ SilUserInput.defaultGridSpacing, gridding: BOOL _ FALSE, --is cursor gridding in this viewer at this moment? hotX: INTEGER _ 0, hotY: INTEGER _ 0, <> <> <> <> xOffset, yOffset: REAL _ 0.0, <> xPostOffset, yPostOffset: INTEGER _ 0, gridMagnification: NAT _ 1, <> <> mode: SilUserInput.InputCharMode _ NotInputingRope, InputWaitingForArg: UInput _ NIL ]; << GLOBAL VARIABLES:>> <> silTipTable: TIPUser.TIPTable; <> terminal: Terminal.Virtual; <> SilHasInputFocus: BOOL; <> silCursorHasBitmap: BOOL _ FALSE; -- set by ControlS restoreCursor: BOOL _ TRUE; -- set by move or copy commands savedCursor: Cursors.CursorType _ textPointer; --restored after ControlS, move, copy InitTipTable: PUBLIC PROC [tipTable: TIPUser.TIPTable] = { <> <<>> silTipTable _ tipTable; terminal _ Terminal.Current[]; }; InitUserInput: PUBLIC PROC [data: SilData, viewer: ViewerClasses.Viewer] = { <> <<>> uiData: SilUIData _ data.uiData _ NEW[SilUIDataRec]; uiData.bs _ BiScrollers.QuaBiScroller[viewer]; uiData.innerViewer _ BiScrollers.QuaViewer[bs: uiData.bs, inner: TRUE]; uiData.outerViewer _ BiScrollers.QuaViewer[bs: uiData.bs, inner: FALSE]; savedCursor _ uiData.innerViewer.class.cursor; }; DestroyUserInput: PUBLIC ENTRY PROC [uiData: SilUIData] = { <> <<>> QuitGridding[uiData]; uiData.innerViewer _ NIL; uiData.outerViewer _ NIL; }; SilNotify: PUBLIC ViewerClasses.NotifyProc = { <<[self: Viewer, input: LIST OF REF ANY] The notify proc which the viewers package will be asked to call. This process will be placing user input records on the user input queue.>> <<>> mx, my: INTEGER _ 0; --for saving mouse coordinates. data: SilData _ NARROW[BiScrollers.QuaBiScroller[self].ClientDataOf[]]; uiData: SilUIData _ data.uiData; FOR list: LIST OF REF ANY _ input, list.rest WHILE list # NIL DO WITH list.first SELECT FROM z: BiScrollers.ClientCoords => { [mx, my] _ GridTheCursor[self, uiData, z]; <> }; C: REF CHAR => RouteInputChars[C^, uiData]; A: ATOM => { SELECT A FROM <> $Exit => QuitGridding[uiData]; <> $TrackMouse => Enque[ [TrackMouse[x: mx, y: my] ], uiData]; <> $MoveMark => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [SetCaret[caret: mark, mode: absolute, x: mx, y: my] ], uiData]; Enque[ [TrackMouse[x: mx, y: my] ], uiData]; }; $MoveStretch => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [OperateOnSelected[op: IF silCursorHasBitmap THEN moveNoStretch ELSE moveStretch, rel: absolute, x: mx, y: my] ], uiData]; Enque[ [TrackMouse[x: mx, y: my] ], uiData]; }; $MoveNoStretch => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [OperateOnSelected[op: moveNoStretch, rel: absolute, x: mx, y: my] ], uiData]; Enque[ [TrackMouse[x: mx, y: my] ], uiData]; }; $MoveOrigin => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [SetCaret[caret: origin, mode: absolute, x: mx, y: my] ], uiData]; Enque[ [TrackMouse[x: mx, y: my] ], uiData]; }; <> $DrawLine => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [DrawBox[markRel: absolute, originRel: relative, x: mx, y: my] ], uiData]; }; $DeleteObject => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [SelectWithPos[x: mx, y: my, mode: delete] ], uiData]; }; $Copy => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [OperateOnSelected[op: copy, rel: absolute, x: mx, y: my] ], uiData]; }; $Undelete => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [Undelete[] ], uiData]; }; <> $SelectObject => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [SelectWithPos[x: mx, y: my] ], uiData]; }; $AddSelected => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [SelectWithPos[x: mx, y: my, mode: add] ], uiData]; }; $SelectArea => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [SelectWithPos[x: mx, y: my, mode: relative] ], uiData]; }; $DeselectObject => { IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ESC, uiData]; Enque[ [SelectWithPos[x: mx, y: my, mode: remove] ], uiData]; }; <> $DrawBox => Enque[ [DrawBox[markRel: absolute, originRel: absolute] ], uiData]; $DrawBackBox => Enque[ [DrawBox[markRel: absolute, originRel: absolute, background: TRUE] ], uiData]; $CopySelected => Enque[ [OperateOnSelected[op: copy, rel: relative] ], uiData]; $DeleteSelected => Enque[ [OperateOnSelected[op: delete] ], uiData]; $CenterOnMark => Enque[ [CenterOnMark[] ], uiData]; $ComplimentMagnify => Enque[ [Compliment[mode: magnification] ], uiData]; $SetDefault => { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [SetDefaultAttribs[]] ]; MessageWindow.Append["Attribute which is to be default: ", TRUE]; }; $SetGrid => { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [SetDetails[detail: gridSize]] ]; MessageWindow.Append["Exponent for new grid size (0-9): ", TRUE]; }; $InputFile => Enque[ [InputFile[mode: absolute] ], uiData]; $InputFileRelative => Enque[ [InputFile[mode: relative] ], uiData]; $Jam => { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [ChangeSelected[]] ]; MessageWindow.Append["Attribute by which to change selection: ", TRUE]; }; $Kill => Enque[ [KillPicture[] ], uiData]; $DefineMacro => { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [ManipulateMacro[mode: define]] ]; MessageWindow.Append["Name for new macro: ", TRUE]; }; $ComplimentOneLevel => Enque[ [Compliment[mode: oneLevel] ], uiData]; $OutputFile => Enque[ [StoreFile[clip: TRUE, large: FALSE] ], uiData]; $OutputFileUnclipped => Enque[ [StoreFile[clip: FALSE, large: FALSE] ], uiData]; $PutFile => Enque[ [StoreFile[clip: FALSE, large: TRUE] ], uiData]; $DeleteMacros => Enque[ [ManipulateMacro[mode: clear] ], uiData]; $ShowTics => Enque[ [Compliment[mode: tics] ], uiData]; $ShowFrames => Enque[ [Compliment[mode: frames] ], uiData]; $View => { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [ShowMacros[]] ]; MessageWindow.Append["Font for macro description or attribute for selection: ", TRUE]; }; $ReduceSelection => { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [SelectForAttrib[mode: reduce]] ]; MessageWindow.Append["Attribute to which to reduce selection: ", TRUE]; }; $MoveRelativeStretch => { Enque[ [OperateOnSelected[op: IF silCursorHasBitmap THEN moveNoStretch ELSE moveStretch, rel: relative, x: mx, y: my] ], uiData]; }; $MoveRelativeNoStretch => { Enque[ [OperateOnSelected[op: moveNoStretch, rel: relative, x: mx, y: my] ], uiData]; }; $Ylock => Enque[ [Compliment[mode: yInc] ], uiData]; $Hardcopy => { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [HardCopy[]] ]; MessageWindow.Append["Hardcopy Device: Type ", TRUE]; MessageWindow.Append["R,H,M,U,C for IP file (H for Stinger); P for Press file)", FALSE]; }; <<$Help => -- HELP FEATURE DISABLED. SIL Help.sil gets desired effect.>> <> $ControlA => IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ControlA, uiData] ELSE Enque[ [SwapFonts[] ], uiData]; $ControlH => IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ControlA, uiData] ELSE { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [ManipulateMacro[mode: expand]] ]; MessageWindow.Append["Name for macro to be expanded: ", TRUE]; }; $ControlQ => IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ControlQ, uiData] ELSE MessageWindow.Append["Use viewer Destroy button to Quit", TRUE]; $ControlW => IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ControlW, uiData] ELSE { uiData.mode _ waitingFor1CharArg; uiData.InputWaitingForArg _ LIST[ [SetDetails[detail: boxWidth]] ]; MessageWindow.Append["New line width (1-9): ", TRUE]; }; $ControlS => IF uiData.mode = InputingRope THEN RouteInputChars[Ascii.ControlS, uiData] ELSE { Enque[ [SetCursor[caret: origin] ], uiData]; silCursorHasBitmap _ TRUE; }; $Del => RouteInputChars[Ascii.DEL, uiData]; $Escape => RouteInputChars[Ascii.ESC, uiData]; $Return => SELECT uiData.mode FROM InputingRope => RouteInputChars[Ascii.CR, uiData] ENDCASE => Enque[ [SetCaret[caret: mark, mode: relative] ], uiData]; $Backspace => RouteInputChars[Ascii.BS, uiData]; ENDCASE => NULL; restoreCursor _ SELECT A FROM $TrackMouse, $ControlS, $MoveMark, $MoveStretch, $MoveNoStretch, $MoveOrigin, $Copy, $CopySelected, $ShowTics, $MoveRelativeStretch, $MoveRelativeNoStretch => FALSE, ENDCASE => TRUE; }; ENDCASE => ERROR; IF silCursorHasBitmap AND restoreCursor THEN TRUSTED { Cursors.SetCursor[blank]; -- clear special cursor bits Cursors.SetCursor[savedCursor]; -- restore normal cursor Interminal.SetCursorOffset[deltaX: 0, deltaY: 0, enableTracking: FALSE]; -- and disable automatic tracking !!!! silCursorHasBitmap _ FALSE; }; ENDLOOP; }; <<>> SilModifyInputFocus: PUBLIC ViewerClasses.ModifyProc = { <<[self: Viewer, change: ModifyAction] >> <> <<>> data: SilData _ NARROW[BiScrollers.QuaBiScroller[self].ClientDataOf[]]; uiData: SilUIData _ data.uiData; SELECT change FROM kill, push => { uiData.gotInputFocus _ FALSE; SilHasInputFocus _ FALSE; }; set, pop => { uiData.gotInputFocus _ TRUE; SilHasInputFocus _ TRUE; }; ENDCASE; }; GetInputFocus: PUBLIC ENTRY PROC[uiData: SilUIData] = { <> IF NOT uiData.gotInputFocus THEN InputFocus.SetInputFocus[uiData.innerViewer]; }; HasInputFocus: PUBLIC PROC[] RETURNS [hasIt: BOOL] = { <> RETURN[SilHasInputFocus]; }; Enque: PUBLIC PROC [UI: UInputRec, uiData: SilUIData] = { <> <> EnqueWithList[LIST[UI], uiData]; }; EnqueWithList: ENTRY PROC [UIL: UInput, uiData: SilUIData] = { <> <> <<>> ENABLE UNWIND => NULL; IF uiData.queue = NIL THEN uiData.queue _ UIL ELSE FOR q: UInput _ uiData.queue, q.rest WHILE q # NIL DO IF q.rest = NIL THEN { q.rest _ UIL; EXIT; }; ENDLOOP; NOTIFY uiData.gotInput; }; Deque: PUBLIC ENTRY PROC [uiData: SilUIData] RETURNS [UInputRec] = { <> <<>> ENABLE UNWIND => NULL; UI: UInputRec _ uiData.queue.first; uiData.queue _ uiData.queue.rest; RETURN [UI]; }; InputAvailable: PUBLIC ENTRY PROC [uiData: SilUIData] RETURNS [yes: BOOL] = { <> ENABLE UNWIND => NULL; RETURN[uiData.queue # NIL]; }; SetCaretChange: PUBLIC ENTRY PROC [uiData: SilUIData] = { <> <<>> ENABLE UNWIND => NULL; <> uiData.caretsShouldChangeNow _ TRUE; NOTIFY uiData.gotInput; }; CaretHasChanged: PUBLIC ENTRY PROC [uiData: SilUIData] = { <> ENABLE UNWIND => NULL; <> uiData.caretsShouldChangeNow _ FALSE; }; ShouldChangeCaret: PUBLIC ENTRY PROC [uiData: SilUIData] RETURNS [changeIt: BOOL] = { <> ENABLE UNWIND => NULL; RETURN[uiData.caretsShouldChangeNow]; }; AwaitUserInput: PUBLIC ENTRY PROC [uiData: SilUIData] = { <> ENABLE UNWIND => NULL; WAIT uiData.gotInput; }; GridTheCursor: PROC [innerViewer: ViewerClasses.Viewer, uiData: SilUIData, cc: BiScrollers.ClientCoords] RETURNS [mx, my: INTEGER _ 0] = TRUSTED { <> <<>> Grid: PROC [raw, grid: INTEGER] RETURNS [gridded: INTEGER] = CHECKED { rem: INTEGER _ raw MOD grid; IF rem >= grid/2 THEN rem _ rem - grid; gridded _ raw - rem}; gridSpacing: NAT _ uiData.gridSpacing * uiData.gridMagnification; viewerPos: Geom2D.Vec; sx, sy: INTEGER; mx _ Real.RoundI[cc.x]; my _ Real.RoundI[cc.y]; <<>> IF NOT uiData.gridding THEN { <<1st mouse input for viewer: coords will be viewer relative; save Cursor Offset so can reset when we release buttons.>> [uiData.hotX, uiData.hotY, ] _ Interminal.GetCursorOffset[]; uiData.gridding _ TRUE; <> uiData.bs.style.SetButtonsCapturedness[uiData.bs, TRUE]; <> Interminal.SetCursorOffset[0, 0, FALSE]; }; mx _ Grid[mx - uiData.xPostOffset, gridSpacing] + uiData.xPostOffset; my _ Grid[my - uiData.yPostOffset, gridSpacing] + uiData.yPostOffset; viewerPos _ uiData.bs.style.GetTransforms[uiData.bs].clientToViewer.Transform[[mx, my]]; [sx, sy] _ ViewerOps.UserToScreenCoords[innerViewer, Real.RoundI[viewerPos.x], Real.RoundI[viewerPos.y]]; IF innerViewer.column#color THEN { sy _ terminal.bwHeight - sy; Terminal.SetBWCursorPosition[terminal, [sx + uiData.hotX, sy + uiData.hotY]]; } ELSE { sy _ terminal.colorHeight - sy; Terminal.SetColorCursorPosition[terminal, [sx + uiData.hotX, sy + uiData.hotY]]; }; }; QuitGridding: PROC [uiData: SilUIData] = TRUSTED { <> uiData.gridding _ FALSE; uiData.bs.style.SetButtonsCapturedness[uiData.bs, FALSE]; Interminal.SetCursorOffset[uiData.hotX, uiData.hotY, TRUE]; --reset remembered offset }; ChangeGridInterval: PUBLIC ENTRY PROC [uiData: SilUIData, gridSpacing: NAT] = { <> ENABLE UNWIND => NULL; uiData.gridSpacing _ gridSpacing; }; MagnifyGrid: PUBLIC ENTRY PROC [uiData: SilUIData, xOffset, yOffset: REAL, magnification: NAT] = { <> <> ENABLE UNWIND => NULL; uiData.xOffset _ xOffset; uiData.yOffset _ yOffset; uiData.gridMagnification _ magnification; uiData.xPostOffset _ Real.RoundI[xOffset * magnification]; uiData.yPostOffset _ Real.RoundI[yOffset * -magnification]; }; NotInputingRope: PUBLIC ENTRY PROC [uiData: SilUIData] = { <> ENABLE UNWIND => NULL; uiData.mode _ NotInputingRope; }; InputingRope: PUBLIC ENTRY PROC [uiData: SilUIData] = { <> ENABLE UNWIND => NULL; uiData.mode _ InputingRope; }; RouteInputChars: PROC [c: CHAR, uiData: SilUIData] = { <<>> SELECT uiData.mode FROM NotInputingRope, InputingRope => Enque[ [UserChar[c] ], uiData]; waitingFor1CharArg => { TRUSTED { WITH temp: uiData.InputWaitingForArg.first SELECT FROM ChangeSelected => temp.char _ c; HardCopy => temp.char _ c; ManipulateMacro => temp.char _ c; SelectForAttrib => temp.char _ c; SetDefaultAttribs => temp.char _ c; SetDetails => temp.char _ c; ShowMacros => SELECT c FROM <> <> '4, '5, '6, '7, '8, '9 => temp.char _ c; ENDCASE => uiData.InputWaitingForArg _ LIST[ [SelectForAttrib[mode: change, char: c]] ]; ENDCASE; }; uiData.mode _ NotInputingRope; <> IF c IN SilFile.PrintingChars THEN EnqueWithList[uiData.InputWaitingForArg, uiData]; uiData.InputWaitingForArg _ NIL; }; waitingForConfirmation => NULL; ENDCASE; }; GetBiScroller: PUBLIC PROC [uiData: SilUIData] RETURNS [bs: BiScrollers.BiScroller] = {bs _ uiData.bs}; END.