-- file: IntTrackMisc.mesa
-- edited by Brotz, April 30, 1982 11:05 AM
-- edited by Levin, August 28, 1980 5:11 PM

DIRECTORY
Ascii USING [DEL],
dsD: FROM "DisplayDefs" USING [ChangeCursor, ClearRectangle, GetCursor,
InvertRectangle, lineHeight, PutCharInBitMap, ScreenXCoord, ScreenYCoord],
Editor USING [cancelCode],
inD: FROM "InteractorDefs" USING [AcceptKeyboardInput, CaretIsBlinking, cursorX,
cursorY, IdleLoop, leftMargin, lineBarLeftX, MapTOCIndexToTopY, MapYToTOCIndex,
markLeftX, MouseButton, numberLeftX, realTimeClock, ScreenXCoord, ScreenYCoord,
ScrollDownTOC, ScrollUpTOC, SetCaretBlinking, StartBlinkingCaret, StopBlinkingCaret,
ThisIsAFirstLine, TOCTextNbrPtr, TrackerType],
intCommon USING [continuousScrollDelay, continuousScrollTimeOut, keystream, source,
target],
StreamDefs USING [StreamHandle],
tsD: FROM "TOCSelectionDefs" USING [TOCTextTracker],
vmD: FROM "VirtualMgrDefs" USING [GetTOCFixedPart, PutTOCFixedPart, TOCFixedPart,
TOCHandle, TOCIndex, UnlockTOC, WaitForLock];

IntTrackMisc: PROGRAM
IMPORTS dsD, inD, intC: intCommon, tsD, vmD
EXPORTS inD =

BEGIN
OPEN inD;

-- Handles tracking in the TOC and DM regions


TOCTracker: PUBLIC PROCEDURE [tnp: TOCTextNbrPtr, trackerType: TrackerType] =
-- Tracks cursor within the TOC text neighborhood. Chooses among three subneighborhood
-- cursor trackers depending on the cursor’s position.
BEGIN
x: ScreenXCoord;
y: ScreenYCoord;
xOffset, yOffset: INTEGER;
cursorOk: BOOLEAN ← TRUE;
dsD.ChangeCursor[charArrow];
DO
[ , xOffset, yOffset] ← dsD.GetCursor[];
x ← cursorX↑ + xOffset;
y ← cursorY↑ + yOffset;
SELECT TRUE FROM
y ~IN [tnp.topY .. tnp.bottomY) => RETURN;
~tnp.haveToc => NULL;
(x < lineBarLeftX) => {TOCScrollBarTracker[tnp]; cursorOk ← FALSE};
(x < leftMargin) => {tsD.TOCTextTracker[tnp]; cursorOk ← FALSE};
(x IN [markLeftX .. numberLeftX) AND trackerType = normal
AND tnp.displayFormatted) => {MarkTracker[tnp]; cursorOk ← FALSE};
ENDCASE => {IF ~cursorOk THEN dsD.ChangeCursor[charArrow]; cursorOk ← TRUE};
IdleLoop[];
AcceptKeyboardInput[];
ENDLOOP;
END; -- of TOCTracker --


TOCScrollBarTracker: PROCEDURE [tnp: TOCTextNbrPtr] =
-- Sets cursor shape for scroll bar subneighborhood. Watches for button up and down and
-- calls TOC scroll routines as appropriate.
BEGIN

LockAndScroll: PROCEDURE [y: ScreenYCoord, direction: {up, down}] =
BEGIN
toc: vmD.TOCHandle = tnp.toc;
keyExists: BOOLEAN = (intC.source.key # 0);
key: CARDINAL ← IF keyExists THEN intC.source.key ELSE vmD.WaitForLock[toc];
IF direction = up THEN ScrollUpTOC[y, tnp, key] ELSE ScrollDownTOC[y, tnp, key];
IF ~keyExists THEN vmD.UnlockTOC[toc, key];
END; -- of LockAndScroll --

state, newState: {neutral, up, down, continuousUp, continuousDown} ← neutral;
x: ScreenXCoord;
y: ScreenYCoord;
startTime: CARDINAL;
xOffset, yOffset: INTEGER;
dsD.ChangeCursor[scroll];
[ , xOffset, yOffset] ← dsD.GetCursor[];
DO
x ← cursorX↑ + xOffset;
y ← cursorY↑ + yOffset;
IF x >= lineBarLeftX OR y ~IN [tnp.topY .. tnp.bottomY) THEN RETURN;
newState ← SELECT TRUE FROM
MouseButton[left, down] => IF state = continuousUp THEN state ELSE up,
MouseButton[right, down] => IF state = continuousDown THEN state ELSE down,
ENDCASE => neutral;
SELECT state FROM
up => IF newState = neutral THEN LockAndScroll[y, up];
down => IF newState = neutral THEN LockAndScroll[y, down];
continuousUp =>
IF inD.realTimeClock↑ - startTime >= intC.continuousScrollDelay THEN
{startTime ← inD.realTimeClock↑; LockAndScroll[tnp.topY, up]};
continuousDown =>
IF inD.realTimeClock↑ - startTime >= intC.continuousScrollDelay THEN
{startTime ← inD.realTimeClock↑; LockAndScroll[tnp.topY, down]};
ENDCASE;
SELECT TRUE FROM
(state = newState AND (state = up OR state = down)) =>
IF intC.continuousScrollTimeOut # 0
AND inD.realTimeClock↑ - startTime >= intC.continuousScrollTimeOut THEN
BEGIN
state ← IF state = up THEN continuousUp ELSE continuousDown;
startTime ← inD.realTimeClock↑ - intC.continuousScrollDelay;
END;
(newState # state) => BEGIN
IF (state ← newState) # neutral THEN startTime ← inD.realTimeClock↑;
dsD.ChangeCursor
[SELECT state FROM up => scrollUp, down => scrollDown, ENDCASE => scroll];
END;
ENDCASE;
IdleLoop[];
AcceptKeyboardInput[];
ENDLOOP;
END; -- of TOCScrollBarTracker --


MarkTracker: PROCEDURE [tnp: TOCTextNbrPtr] =
-- Tracks cursor within the mark subneighborhood. Watches for button up and down,
-- indicates cocked state on screen, changes marks on screen and in TOCEntry.
BEGIN
state: {neutral, cocked} ← neutral;
x: ScreenXCoord;
y, markY: ScreenYCoord;
xOffset, yOffset: INTEGER;
markC, thisEntry: vmD.TOCIndex;
newMarkChar: CHARACTER;
fp: vmD.TOCFixedPart;
toc: vmD.TOCHandle = tnp.toc;
key: CARDINAL ← 0;
keystream: StreamDefs.StreamHandle = intC.keystream;

InvertMark: PROCEDURE =
BEGIN
dsD.InvertRectangle[markLeftX, numberLeftX, markY, markY + dsD.lineHeight];
END; -- of InvertMark --

dsD.ChangeCursor[charArrow];
[ , xOffset, yOffset] ← dsD.GetCursor[];
DO
x ← cursorX↑ + xOffset;
y ← cursorY↑ + yOffset;
IF x ~IN [markLeftX .. numberLeftX) OR y ~IN [tnp.topY .. tnp.bottomY) THEN
BEGIN
IF state = cocked THEN {InvertMark[]; vmD.UnlockTOC[toc, key]};
RETURN;
END;
SELECT state FROM
neutral =>
IF MouseButton[left, down] AND ThisIsAFirstLine[y, tnp] THEN
BEGIN
key ← vmD.WaitForLock[toc];
thisEntry ← MapYToTOCIndex[y, tnp];
state ← cocked;
markC ← thisEntry;
[markY, ] ← MapTOCIndexToTopY[markC, tnp];
InvertMark[];
END;
cocked =>
IF MouseButton[left, up] THEN
BEGIN
keystream.reset[keystream];
dsD.ClearRectangle[markLeftX, numberLeftX, markY, markY + dsD.lineHeight];
StartBlinkingCaret[markLeftX, markY + dsD.lineHeight / 2];
dsD.ChangeCursor[invisibleCursor];
-- Read in mark and display it --
WHILE keystream.endof[keystream] DO
IdleLoop[];
ENDLOOP;
dsD.ChangeCursor[charArrow];
StopBlinkingCaret[];
state ← neutral;
vmD.GetTOCFixedPart[toc, key, markC, @fp];
SELECT newMarkChar ← keystream.get[keystream] FROM
Ascii.DEL, Editor.cancelCode => newMarkChar ← fp.mark;
’? => {fp.seen ← FALSE; fp.mark ← ’ };
ENDCASE => {fp.seen ← TRUE; fp.mark ← newMarkChar};
[] ← dsD.PutCharInBitMap[newMarkChar, markLeftX, markY, plainFace];
vmD.PutTOCFixedPart[toc, key, markC, @fp];
vmD.UnlockTOC[toc, key];
key ← 0;
IF CaretIsBlinking[] THEN SetCaretBlinking[intC.target.point, intC.target.mnp];
END
ELSE -- left mouse button down, so track --
BEGIN
thisEntry ← MapYToTOCIndex[y, tnp];
IF ThisIsAFirstLine[y, tnp] THEN
BEGIN
IF thisEntry # markC THEN
BEGIN
InvertMark[];
markC ← thisEntry;
[markY, ] ← MapTOCIndexToTopY[markC, tnp];
InvertMark[];
END;
END
ELSE {InvertMark[]; vmD.UnlockTOC[toc, key]; key ← 0; state ← neutral};
END;
ENDCASE;
IdleLoop[];
AcceptKeyboardInput[];
ENDLOOP;
END; -- of MarkTracker --


END. -- of IntTrackMisc --