-- file: IntTrackMisc.mesa
-- edited by Brotz, December 19, 1980 10:53 AM
-- edited by Levin, August 28, 1980 5:11 PM

DIRECTORY
Ascii,
dsD: FROM "DisplayDefs",
Editor,
inD: FROM "InteractorDefs",
intCommon: FROM "IntCommon",
StreamDefs,
tsD: FROM "TOCSelectionDefs",
vmD: FROM "VirtualMgrDefs";

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

BEGIN

OPEN inD;

-- Handles tracking in the TOC and DM regions


TOCTracker: 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;
DO
[ , xOffset, yOffset] ← dsD.GetCursor[];
x ← cursorX↑ + xOffset;
y ← cursorY↑ + yOffset;
SELECT TRUE FROM
(~intC.haveMailFile OR y ~IN [tnp.topY .. tnp.bottomY)) => RETURN;
(x < lineBarLeftX) => TOCScrollBarTracker[tnp];
(x < leftMargin) => tsD.TOCTextTracker[tnp];
(x IN [markLeftX .. numberLeftX) AND trackerType = normal) => MarkTracker[tnp];
ENDCASE => dsD.ChangeCursor[charArrow];
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
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 ScrollUpTOC[y, tnp];
down => IF newState = neutral THEN ScrollDownTOC[y, tnp];
continuousUp =>
IF inD.realTimeClock↑ - startTime >= intC.continuousScrollDelay THEN
BEGIN
startTime ← inD.realTimeClock↑;
ScrollUpTOC[tnp.topY, tnp];
END;
continuousDown =>
IF inD.realTimeClock↑ - startTime >= intC.continuousScrollDelay THEN
BEGIN
startTime ← inD.realTimeClock↑;
ScrollDownTOC[tnp.topY, tnp];
END;
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;
toc: vmD.TOCFixedPart;
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
{IF state = cocked THEN InvertMark[]; RETURN};
SELECT state FROM
neutral =>
IF MouseButton[left, down] AND ThisIsAFirstLine[y, tnp] THEN
BEGIN
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[markC, @toc];
SELECT newMarkChar ← keystream.get[keystream] FROM
Ascii.DEL, Editor.cancelCode => newMarkChar ← toc.mark;
’? => {toc.seen ← FALSE; toc.mark ← ’ };
ENDCASE => {toc.seen ← TRUE; toc.mark ← newMarkChar};
[] ← dsD.PutCharInBitMap[newMarkChar, markLeftX, markY, plainFace];
vmD.PutTOCFixedPart[markC, @toc];
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[]; state ← neutral};
END;
ENDCASE;
IdleLoop[];
AcceptKeyboardInput[];
ENDLOOP;
END; -- of MarkTracker --


END. -- of IntTrackMisc --