-- file: IntTrackMain.mesa -- last edited by Horning, April 15, 1981 3:17 PM -- last edited by Brotz, April 16, 1981 11:56 AM -- edited by Levin, 13-Nov-80 16:30:25 DIRECTORY Ascii, displayCommon: FROM "DisplayCommon", dsD: FROM "DisplayDefs", Editor, exD: FROM "ExceptionDefs", inD: FROM "InteractorDefs", Inline, intCommon: FROM "IntCommon", KeyDefs, lmD: FROM "LaurelMenuDefs", ovD: FROM "OverviewDefs", Storage, StreamDefs; IntTrackMain: PROGRAM IMPORTS disC: displayCommon, dsD, Editor, exD, inD, Inline, intC: intCommon, lmD, Storage EXPORTS Editor, inD = PUBLIC BEGIN OPEN inD; -- Purpose: handles user interactions including the display, keyboard and -- mouse. This division gathers together commands and their arguments and is -- responsible for the display of all error messages. -- Global variables acceptKeyboardInputStream: StreamDefs.StreamHandle _ intC.keystream; kbdInputAcceptor: KeyboardInputAcceptor _ Editor.Decode; modelessEditor: BOOLEAN = (intC.editorType = modeless); -- Exported variables keyboardInputAcceptorPtr: POINTER TO KeyboardInputAcceptor _ @kbdInputAcceptor; -- Exported Procedures AcceptKeyboardInput: PROCEDURE = -- Called by cursor tracking routines when keyboard input is acceptable. -- Dispatches to the proper command interpreter. The standard Laurel editor -- operates in a mode such that the acceptKeyboardInputStream is identical to -- intC.keystream. Thus, when input is present on the -- acceptKeyboardInputStream it is also present on the intC.keystream. Other -- command interpreters may introduce a filter between these two keystreams. -- This procedure guarantees that input is present on the -- acceptKeyboardInputStream; if operating in a nonstandard mode, the current -- KeyboardInputAcceptor must funnel input from this keystream to whichever -- keystream is used by subsequent procedures. BEGIN haveChar: BOOLEAN _ ~acceptKeyboardInputStream.endof[acceptKeyboardInputStream]; char: CHARACTER _ IF haveChar THEN acceptKeyboardInputStream.get[acceptKeyboardInputStream] ELSE Ascii.NUL; cancelChar: BOOLEAN _ (char = Editor.cancelCode OR char = Ascii.DEL); haveNonCancel: BOOLEAN = (haveChar AND ~cancelChar); shiftUp: BOOLEAN = ShiftKey[up]; controlUp: BOOLEAN = ControlKey[up]; target: TextSelectionPtr = @intC.target; source: TextSelectionPtr = @intC.source; MonitorTaps[]; IF ControlTap[] THEN BEGIN OPEN Editor; selPtr: TextSelectionPtr _ IF intC.currentSelection = source THEN source ELSE target; underline: UnderlineType _ intC.currentSelection; IF selPtr.mnp.editable AND selPtr.start # selPtr.end THEN BEGIN IF ~selPtr.pendingDelete THEN BEGIN DeUnderlineSelection[selPtr, underline]; selPtr.pendingDelete _ TRUE; UnderlineSelection[selPtr, underline]; END; IF intC.currentSelection = target THEN intC.pendingDeleteSetByControl _ TRUE; END; END; -- hack to make shifted selection look like a key stroke IF source.start # source.end AND ((modelessEditor AND ((shiftUp AND controlUp) OR haveNonCancel)) OR (~modelessEditor AND ~intC.secondarySelectionEnabled AND (shiftUp OR haveNonCancel))) THEN BEGIN IF haveChar THEN acceptKeyboardInputStream.putback[acceptKeyboardInputStream, char]; haveChar _ TRUE; cancelChar _ FALSE; char _ Editor.shiftedSelectionFlag; intC.currentSelection _ target; END ELSE IF target.pendingDelete AND intC.pendingDeleteSetByControl AND ~haveChar AND controlUp AND shiftUp THEN {haveChar _ TRUE; char _ Ascii.DEL}; IF haveChar THEN IF cancelChar AND (~shiftUp OR ~controlUp) THEN BEGIN Editor.ClearSourceSelection[]; IF target.pendingDelete THEN BEGIN Editor.DeUnderlineSelection[target, target]; target.pendingDelete _ FALSE; Editor.UnderlineSelection[target, target]; intC.currentSelection _ target; intC.pendingDeleteSetByControl _ FALSE; END; END ELSE kbdInputAcceptor[intC.cmTextNbr, char]; END; -- of AcceptKeyboardInput -- ScreenTracker: PROCEDURE [trackerType: TrackerType] = -- Determines which cursor tracking routine should handle cursor tracking based -- on the cursor's current position. This procedure is the highest on any -- calling chain. A return to ScreenTracker means that a lower level cursor -- tracker has determined that the cursor has moved out of its jurisdiction and -- a new lower level cursor tracker should be invoked. BEGIN y: ScreenYCoord; yOffset: INTEGER; rp: RegionPtr; DO ENABLE lmD.MenuChange => RESUME; [ , , yOffset] _ dsD.GetCursor[]; y _ cursorY^ + yOffset; FOR rp _ intC.regions, rp.nextRegion UNTIL rp = NIL DO IF y IN [rp.topY .. rp.bottomY) THEN GOTO foundRegion; REPEAT foundRegion => RegionTracker[rp, trackerType]; ENDLOOP; IdleLoop[]; AcceptKeyboardInput[]; ENDLOOP; END; -- of ScreenTracker -- RegionTracker: PROCEDURE [rp: RegionPtr, trackerType: TrackerType] = -- Tracks cursor within a region. Changes cursor shape on entry, calls -- appropriate neighborhood cursor tracker or returns to ScreenTracker if the -- cursor leaves this region. BEGIN x: ScreenXCoord; y: ScreenYCoord; xOffset, yOffset: INTEGER; np: NbrPtr; dsD.ChangeCursor[rp.cursorShape]; [ , xOffset, yOffset] _ dsD.GetCursor[]; DO x _ cursorX^ + xOffset; y _ cursorY^ + yOffset; IF ~(y IN [rp.topY .. rp.bottomY)) THEN RETURN; FOR np _ rp.nbrs, np.nextNbr UNTIL np = NIL DO IF (x IN [np.leftX..np.rightX)) AND (y IN [np.topY..np.bottomY)) THEN GOTO foundNbr; REPEAT foundNbr => BEGIN WITH vnp: np SELECT FROM command => vnp.nbrTracker[@vnp, trackerType]; tocText => vnp.nbrTracker[@vnp, trackerType]; messageText => vnp.nbrTracker[@vnp, trackerType]; boundaryLine => vnp.nbrTracker[@vnp]; boundaryPad => vnp.nbrTracker[@vnp, trackerType]; thumbLine => vnp.nbrTracker[@vnp, trackerType]; ENDCASE => exD.SysBug[]; dsD.ChangeCursor[rp.cursorShape]; END; ENDLOOP; IdleLoop[]; AcceptKeyboardInput[]; ENDLOOP; END; -- of RegionTracker -- CommandTracker: PROC [cnp: CommandNbrPtr, trackerType: TrackerType] = -- This is the nbrTracker for command neighborhoods. Sets cursor shape for -- command neighborhood. Watches for button up and down, calls routines to -- invert and restore command houses on screen. Calls routines in Command -- Dept. as appropriate. BEGIN x: ScreenXCoord; y: ScreenYCoord; xOffset, yOffset: INTEGER; hp: HousePtr; dsD.ChangeCursor[cnp.cursorShape]; [ , xOffset, yOffset] _ dsD.GetCursor[]; DO x _ cursorX^ + xOffset; y _ cursorY^ + yOffset; IF y ~IN [cnp.topY .. cnp.bottomY) THEN RETURN; FOR hp _ cnp.houses, hp.nextHouse UNTIL hp = NIL DO IF x IN [hp.leftX .. hp.rightX) AND y IN [hp.topY .. hp.bottomY) THEN GOTO foundHouse; REPEAT foundHouse => {HouseTracker[hp, trackerType]; dsD.ChangeCursor[cnp.cursorShape]}; ENDLOOP; IdleLoop[]; AcceptKeyboardInput[]; ENDLOOP; END; -- of CommandTracker -- HouseTracker: PROCEDURE [hp: HousePtr, trackerType: TrackerType] = -- Tracks cursor when within a command house. Watches for button down and -- up, inverts house display pattern, and calls command procedure when fired. BEGIN x: ScreenXCoord; y: ScreenYCoord; xOffset, yOffset: INTEGER; state: {neutral, cockedLeft, confirmCocked, nextBracketCocked, nextBracketContinuous, cockedRight} _ neutral; nextBracketTime: CARDINAL; menuChangeAbort: BOOLEAN _ FALSE; keystream: StreamDefs.StreamHandle = intC.keystream; CleanUp: PROCEDURE = BEGIN IF state = cockedLeft OR state = cockedRight THEN dsD.InvertRectangle[hp.leftX, hp.rightX, hp.topY, hp.bottomY]; state _ neutral; END; -- of CleanUp -- [ , xOffset, yOffset] _ dsD.GetCursor[]; DO x _ cursorX^ + xOffset; y _ cursorY^ + yOffset; IF menuChangeAbort OR y ~IN [hp.topY .. hp.bottomY) OR x ~IN [hp.leftX .. hp.rightX) THEN {CleanUp[]; RETURN}; SELECT TRUE FROM (state = neutral AND hp.callable AND trackerType = normal AND MouseButton[left, down]) => BEGIN state _ cockedLeft; dsD.InvertRectangle[hp.leftX, hp.rightX, hp.topY, hp.bottomY]; END; (state = neutral AND hp.callable AND trackerType = normal AND MouseButton[right, down] AND hp.needsConfirmation) => BEGIN state _ cockedRight; dsD.InvertRectangle[hp.leftX, hp.rightX, hp.topY, hp.bottomY]; END; (state = neutral AND hp = intC.currentCommand AND trackerType = brackets) => IF MouseButton[left, down] THEN {nextBracketTime _ realTimeClock^; state _ nextBracketCocked} ELSE IF MouseButton[middle, down] THEN state _ confirmCocked; ((state = cockedLeft AND MouseButton[left, up]) OR (state = cockedRight AND MouseButton[right, up])) => BEGIN dsD.InvertRectangle[hp.leftX, hp.rightX, hp.topY, hp.bottomY]; IndicateCommandBusy[hp]; IF CaretIsBlinking[] THEN StopBlinkingCaret[]; intC.currentCommand _ hp; IF intC.source.start # intC.source.end THEN Editor.ClearSourceSelection[]; hp.command[hp, state = cockedRight]; [] _ Storage.Prune[]; IF hp.trackerIndicateDone THEN BEGIN IndicateCommandFinished[hp]; IF CaretIsBlinking[] THEN SetCaretBlinking[intC.target.point, intC.target.mnp]; END; intC.currentCommand _ NIL; RETURN; -- Command may have changed world significantly. END; (state = confirmCocked AND MouseButton[middle, up]) => {keystream.putback[keystream, Ascii.ESC]; RETURN}; (state = nextBracketCocked) => IF intC.nextBracketTimeout # 0 AND realTimeClock^ - nextBracketTime >= intC.nextBracketTimeout THEN BEGIN state _ nextBracketContinuous; nextBracketTime _ realTimeClock^ - intC.nextBracketDelay; END ELSE IF MouseButton[left, up] THEN {keystream.putback[keystream, Editor.nextBracketStringCode]; RETURN}; (state = nextBracketContinuous) => IF MouseButton[left, up] THEN RETURN ELSE IF realTimeClock^ - nextBracketTime >= intC.nextBracketDelay THEN BEGIN keystream.putback[keystream, Editor.nextBracketStringCode]; AcceptKeyboardInput [ ! lmD.MenuChange => {CleanUp[]; menuChangeAbort _ TRUE; RESUME}]; nextBracketTime _ realTimeClock^ END; ENDCASE; IdleLoop[]; AcceptKeyboardInput [ ! lmD.MenuChange => {CleanUp[]; menuChangeAbort _ TRUE; RESUME}]; ENDLOOP; END; -- of HouseTracker -- BoundaryLineNbrTracker: PROCEDURE [bnp: BoundaryLineNbrPtr] = -- Tracks cursor within a boundary line neighborhood. BEGIN x: ScreenXCoord; y: ScreenYCoord; xOffset, yOffset: INTEGER; dsD.ChangeCursor[bnp.cursorShape]; [ , xOffset, yOffset] _ dsD.GetCursor[]; DO x _ cursorX^ + xOffset; y _ cursorY^ + yOffset; IF y ~IN [bnp.topY .. bnp.bottomY) OR x ~IN [bnp.leftX .. bnp.rightX) THEN RETURN; IdleLoop[]; AcceptKeyboardInput[]; ENDLOOP; END; -- of BoundaryLineNbrTracker -- BoundaryPadNbrTracker: PROCEDURE [bnp: BoundaryPadNbrPtr, trackerType: TrackerType]= -- Tracks cursor within a boundary pad neighborhood. BEGIN state: {up, down} _ up; x: ScreenXCoord; y: ScreenYCoord; xOffset, yOffset: INTEGER; dsD.ChangeCursor[charArrow]; [ , xOffset, yOffset] _ dsD.GetCursor[]; DO x _ cursorX^ + xOffset; y _ cursorY^ + yOffset; SELECT state FROM up => BEGIN IF y ~IN [bnp.topY .. bnp.bottomY) OR x ~IN [bnp.leftX .. bnp.rightX) THEN RETURN; IF MouseButton[middle, down] THEN BEGIN cursorX^ _ mouseX^ _ bnp.leftX; cursorY^ _ mouseY^ _ bnp.topY; dsD.SetCursor[boundaryPad]; [ , xOffset, yOffset] _ dsD.GetCursor[]; dsD.PaintPicture[bnp.leftX, bnp.topY, boundaryPad, dsD.invert]; state _ down; END; END; down => BEGIN IF x + 150 < bnp.leftX THEN BEGIN dsD.ChangeCursor[charArrow]; dsD.PaintPicture[bnp.leftX, bnp.topY, boundaryPad, dsD.invert]; RETURN; END; IF MouseButton[middle, up] THEN BEGIN dsD.ChangeCursor[invisibleCursor]; StopBlinkingCaret[]; bnp.command[bnp, y]; IF trackerType = normal AND CaretIsBlinking[] THEN SetCaretBlinking[intC.target.point, intC.target.mnp]; IF trackerType = brackets THEN SetBracketsCaretBlinking[]; --##cursorX^ _ mouseX^ _ bnp.leftX + 8; --##cursorY^ _ mouseY^ _ bnp.topY + 2; dsD.ChangeCursor[charArrow]; RETURN; END; END; ENDCASE; IdleLoop[]; AcceptKeyboardInput[]; ENDLOOP; END; -- of BoundaryPadNbrTracker -- ThumbLineNbrTracker: PROCEDURE [tlnp: ThumbLineNbrPtr, trackerType: TrackerType] = -- Tracks cursor within a Thumb Line neighborhood. BEGIN state: {up, down} _ up; x: ScreenXCoord; y: ScreenYCoord; xOffset, yOffset: INTEGER; dsD.ChangeCursor[charArrow]; [ , xOffset, yOffset] _ dsD.GetCursor[]; DO x _ cursorX^ + xOffset; y _ cursorY^ + yOffset; IF y ~IN [tlnp.topY .. tlnp.bottomY) OR x + 5 ~IN [tlnp.leftX .. tlnp.rightX + 10) THEN {dsD.ChangeCursor[charArrow]; RETURN}; SELECT state FROM up => IF MouseButton[middle, down] THEN BEGIN dsD.ChangeCursor[thumbMarker]; [ , xOffset, yOffset] _ dsD.GetCursor[]; state _ down; END; down => IF MouseButton[middle, up] THEN BEGIN IF CaretIsBlinking[] AND trackerType = normal THEN StopBlinkingCaret[]; tlnp.command[tlnp, IF x < tlnp.leftX THEN tlnp.leftX ELSE IF x > tlnp.rightX THEN tlnp.rightX ELSE x]; IF CaretIsBlinking[] AND trackerType = normal THEN SetCaretBlinking[intC.target.point, intC.target.mnp]; dsD.ChangeCursor[charArrow]; RETURN; END; ENDCASE; IdleLoop[]; AcceptKeyboardInput[]; ENDLOOP; END; -- of ThumbLineNbrTracker -- CaretIsBlinking: PROCEDURE RETURNS [BOOLEAN] = -- Indicates whether the editor is in a state such that the caret should be -- blinking. BEGIN RETURN[intC.cmTextNbr.haveMessage AND (modelessEditor OR ~intC.commandMode)]; END; -- of CaretIsBlinking -- IndicateCommandBusy: PROCEDURE [hp: HousePtr] = -- Shows that a command is in progress, by: displaying the command house in -- gray and changing the cursor shape. BEGIN dsD.ChangeCursor[hourGlass]; exD.ClearExceptionsRegion[]; dsD.ReplaceRectangle[hp.leftX, hp.rightX, hp.topY, hp.bottomY, lightGray]; IF hp.usePicture THEN dsD.PaintPicture[hp.leftX, hp.topY, hp.picture, dsD.paint] ELSE [] _ dsD.PutStringInBitMap[hp.leftX, hp.topY, hp.text, boldFace]; END; -- of IndicateCommandBusy -- IndicateCommandFinished: PROCEDURE [hp: HousePtr] = -- Shows that a command is finished, by: displaying the command house in -- white. BEGIN dsD.ClearRectangle[hp.leftX, hp.rightX, hp.topY, hp.bottomY]; IF hp.usePicture THEN dsD.PaintPicture[hp.leftX, hp.topY, hp.picture, dsD.paint] ELSE [] _ dsD.PutStringInBitMap[hp.leftX, hp.topY, hp.text, hp.typeface]; END; -- of IndicateCommandFinished -- MakeCommandsCallable: PROCEDURE [callable: BOOLEAN] = -- Sets the callable property of all commands currently on screen to be the value -- of the input parameter. BEGIN IterateOverHouseList: PROCEDURE [house: HousePtr] = BEGIN UNTIL house = NIL DO IF house.typeface = boldFace THEN house.callable _ callable; house _ house.nextHouse; ENDLOOP; END; -- of IterateOverHouseList -- IterateOverHouseList[intC.mailCommandNbr.houses]; IterateOverHouseList[intC.tocCommandNbr.houses]; IterateOverHouseList[intC.cmCommandNbr.houses]; END; -- of MakeCommandsCallable -- TapState: TYPE = {down, notPossibleTapDown, possibleTapDown, up}; controlTapState: TapState _ up; controlTap: BOOLEAN _ FALSE; controlDown: CARDINAL; MonitorTaps: PROCEDURE = -- To be called continuously. Maintains Control and key states and records -- Control taps. BEGIN controlUp: BOOLEAN _ ControlKey[up]; IF ~modelessEditor THEN RETURN; SELECT controlTapState FROM down => IF controlUp THEN controlTapState _ up ELSE IF ShiftKey[down] OR MouseButton[any, down] THEN {controlTapState _ possibleTapDown; controlDown _ realTimeClock^}; notPossibleTapDown => IF controlUp THEN controlTapState _ up; possibleTapDown => IF controlUp THEN BEGIN controlTapState _ up; IF realTimeClock^ - controlDown < intC.tapTimeOut THEN controlTap _ TRUE; END; up => IF ~controlUp THEN IF ShiftKey[down] OR MouseButton[any, down] THEN {controlTapState _ possibleTapDown; controlDown _ realTimeClock^} ELSE controlTapState _ down; ENDCASE; END; -- of MonitorTaps -- ResetTaps: PROCEDURE = -- To be called when any mouse buttons go down in selection areas or on a -- SHIFT-CANCEL. BEGIN controlTap _ FALSE; controlTapState _ IF ControlKey[up] THEN up ELSE notPossibleTapDown; END; -- of ResetTaps -- ControlTap: PROCEDURE RETURNS [yes: BOOLEAN] = -- Returns TRUE iff a Control tap has occurred. Upon return of TRUE, Control -- tap is reset. BEGIN IF (yes _ controlTap) THEN controlTap _ FALSE; END; -- of ControlTap -- ShiftKey: PROCEDURE [state: KeyDefs.updown] RETURNS [yes: BOOLEAN] = BEGIN OPEN KeyDefs; SELECT state FROM up => RETURN[Keys.LeftShift = up AND Keys.RightShift = up]; down => RETURN[Keys.LeftShift = down OR Keys.RightShift = down]; ENDCASE; END; -- of ShiftKey -- ComKey: PROCEDURE [state: KeyDefs.updown] RETURNS [yes: BOOLEAN] = BEGIN OPEN KeyDefs; up: BOOLEAN = (Keys.Spare3 = up AND Keys.FL4 = up AND Keys.FR5 = up); RETURN[IF state = up THEN up ELSE ~up]; END; -- of ComKey -- ControlKey: PROCEDURE [state: KeyDefs.updown] RETURNS [yes: BOOLEAN] = BEGIN RETURN[KeyDefs.Keys.Ctrl = state]; END; -- of ControlKey -- END. -- of IntTrackMain -- z19932(529)\f1