-- file: IntTOC.mesa
-- last edited by Brotz, December 19, 1980 12:04 PM

DIRECTORY
Ascii,
displayCommon: FROM "DisplayCommon",
dsD: FROM "DisplayDefs",
inD: FROM "InteractorDefs",
Inline,
intCommon: FROM "IntCommon",
opD: FROM "OperationsDefs",
String,
tsD: FROM "TOCSelectionDefs",
vmD: FROM "VirtualMgrDefs";

IntTOC: PROGRAM
IMPORTS disC: displayCommon, dsD, inD, Inline, intC: intCommon, String, tsD,
vmD
EXPORTS inD =

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.


DisplayTOCTail: PUBLIC PROCEDURE [tnp: TOCTextNbrPtr, line: LinePtr,
index: vmD.TOCIndex, lineNum: TOCLineNumber] =
-- Paints the rest of the TOC neighborhood from line to the bottom. The first text displayed
-- is lineNum of index. Displays End of messages if necessary.
BEGIN
firstFree, i: vmD.TOCIndex;
nLines: CARDINAL;
lastLineOnScreen: LinePtr;

IF ~intC.haveMailFile THEN RETURN;
firstFree ← vmD.GetFirstFreeTOCIndex[];
IF tnp.lines = tnp.firstLineOffScreen THEN
BEGIN
IF index < firstFree THEN {tnp.lines.state ← index; tnp.lines.linePair ← LinePair[index, 1]}
ELSE {tnp.lines.state ← end; tnp.lines.linePair ← LinePair[firstFree, 1]};
RETURN;
END;
IF index < firstFree THEN [line, nLines] ← DisplayTOCEntry[tnp, index, lineNum - 1, line];
FOR i IN (index .. firstFree) UNTIL line = tnp.firstLineOffScreen DO
[line, nLines] ← DisplayTOCEntry[tnp, i, 0, line];
ENDLOOP;
IF line = tnp.firstLineOffScreen THEN
BEGIN
FOR lastLineOnScreen ← tnp.lines, lastLineOnScreen.nextLine
UNTIL lastLineOnScreen.nextLine = tnp.firstLineOffScreen DO ENDLOOP;
IF lastLineOnScreen.linePair.index + 1 = firstFree
AND lastLineOnScreen.linePair.lineNumber = nLines THEN
BEGIN
tnp.firstLineOffScreen.state ← end;
tnp.firstLineOffScreen.linePair ← LinePair[firstFree, 1];
END
ELSE BEGIN
tnp.firstLineOffScreen.state ← index;
IF lastLineOnScreen.linePair.lineNumber = nLines THEN
tnp.firstLineOffScreen.linePair ← LinePair[lastLineOnScreen.linePair.index + 1, 1]
ELSE tnp.firstLineOffScreen.linePair ← LinePair[lastLineOnScreen.linePair.index,
lastLineOnScreen.linePair.lineNumber + 1]
END;
END
ELSE BEGIN
line.state ← end;
line.linePair ← LinePair[firstFree, 1];
[] ← dsD.PutStringInBitMap[markLeftX, line.y, "End of Messages."L, italicFace];
FOR line ← line.nextLine, line.nextLine UNTIL line = NIL DO
line.state ← empty;
line.linePair ← LinePair[firstFree, 1];
ENDLOOP;
END;
tsD.ConsiderAll[tnp];
END; -- of DisplayTOCTail --


DisplayTOCEntry: PUBLIC PROCEDURE [tnp: TOCTextNbrPtr, index: vmD.TOCIndex,
suppressed: CARDINAL, firstLine: LinePtr] RETURNS [firstUnusedLine: LinePtr,
nLines: CARDINAL] =
-- Processes the index’th TOCEntry, displaying it under certain circumstances: suppressed
-- gives number of lines of the TOCEntry to skip before displaying the rest of the
-- TOCEntry. firstLine points to the Line descriptor for the first line to be used.
-- DisplayTOCEntry stops displaying when either it has displayed the entire entry or there
-- is no more room in the TOC text region. In either case, it returns the total number of
-- lines (including suppressed lines) that this TOCEntry requires and the first unused line.
BEGIN
firstFreeX: ScreenXCoord;
y: ScreenYCoord;
tocFixedPart: vmD.TOCFixedPart;
tocString: STRING ← [opD.maxTOCStringLength];
stringIndex: CARDINAL;

vmD.GetTOCFixedPart[index, @tocFixedPart];
vmD.GetTOCString[index, tocString];
firstUnusedLine ← firstLine;

-- Process the first line of a TOC Entry.
nLines ← 1;
stringIndex ← 0;
IF (firstUnusedLine = NIL) OR (firstUnusedLine = tnp.firstLineOffScreen)
OR (nLines <= suppressed) THEN
-- First line is Not to be displayed.
BEGIN
-- Skip to subject field.
THROUGH [1 .. 2] DO
UNTIL tocString[stringIndex] = opD.substringSeparator DO
stringIndex ← stringIndex+1;
ENDLOOP;
stringIndex ← stringIndex + 1;
ENDLOOP;
-- Skip first line of subject.
[stringIndex, ] ← PretendToFillScreenWithWords[intC.subjectLeftX, rightMargin,
stringIndex, tocString];
END
ELSE
BEGIN -- First line Is to be displayed
y ← firstUnusedLine.y;
dsD.ClearRectangle[markLeftX, rightMargin, y, y + dsD.lineHeight];
-- display mark --
IF ~tocFixedPart.seen THEN [] ← dsD.PutCharInBitMap[’?, markLeftX, y, plainFace]
ELSE [] ← dsD.PutCharInBitMap[tocFixedPart.mark, markLeftX, y, plainFace];
-- display message number --
PutFourDigitStringIntoBitMap[index, numberLeftX, y];
-- Display date.
stringIndex ←
FillScreenWithChars[intC.dateLeftX, y, intC.dateRightX, stringIndex, tocString];
UNTIL tocString[stringIndex] = opD.substringSeparator DO
stringIndex ← stringIndex + 1;
ENDLOOP;
stringIndex ← stringIndex + 1;
-- Display FROM: field.
stringIndex ←
FillScreenWithChars[intC.fromLeftX, y, intC.fromRightX, stringIndex, tocString];
UNTIL tocString[stringIndex] = opD.substringSeparator DO
stringIndex ← stringIndex + 1;
ENDLOOP;
stringIndex ← stringIndex + 1;
-- Display first line of Subject: field.
[stringIndex, firstFreeX] ←
FillScreenWithWords[intC.subjectLeftX, y, rightMargin, stringIndex, tocString];
IF tocFixedPart.deleted THEN
dsD.BlackenRectangle[markLeftX, firstFreeX,
y + dsD.lineHeight / 2, y + dsD.lineHeight / 2 + 1];
-- Record info for this line in the line descriptor block.
firstUnusedLine.linePair ← LinePair[index, nLines];
firstUnusedLine.state ← index;
firstUnusedLine ← firstUnusedLine.nextLine;
END;

-- Process lines after the first one.
UNTIL (stringIndex >= tocString.length) OR (nLines = maxLinesPerTOCEntry) DO
nLines ← nLines + 1;
IF (firstUnusedLine = NIL) OR (firstUnusedLine = tnp.firstLineOffScreen)
OR (nLines <= suppressed) THEN
-- Do not display this line.
[stringIndex, ] ←
PretendToFillScreenWithWords[intC.subjectExtensionLeftX, inD.rightMargin,
stringIndex, tocString]
ELSE
BEGIN -- Display this line.
y ← firstUnusedLine.y;
dsD.ClearRectangle[intC.subjectExtensionLeftX, inD.rightMargin,
y, y + dsD.lineHeight];
-- Display extra line of Subject: field.
[stringIndex, firstFreeX] ←
FillScreenWithWords[intC.subjectExtensionLeftX, y, inD.rightMargin,
stringIndex, tocString];
IF tocFixedPart.deleted THEN
dsD.BlackenRectangle[intC.subjectExtensionLeftX, firstFreeX,
y + dsD.lineHeight / 2, y + dsD.lineHeight / 2 + 1];
-- Record info for this line in the line descriptor block.
firstUnusedLine.linePair ← LinePair[index, nLines];
firstUnusedLine.state ← index;
firstUnusedLine ← firstUnusedLine.nextLine;
END;
ENDLOOP;
END; -- of DisplayTOCEntry --


PutFourDigitStringIntoBitMap: PROCEDURE [n: CARDINAL, startX: ScreenXCoord,
y: ScreenYCoord] =
-- Places the four digit string for n into the bitmap. Blanks are allowed to take up a full
-- digit width on the screen.
BEGIN
s: STRING ← [4];
String.AppendDecimal[s, n];
[] ← dsD.PutStringInBitMap[startX + (4 - s.length) * digitWidth, y, s, plainFace];
END; -- of PutFourDigitStringIntoBitMap --


FillScreenWithChars: PROCEDURE [x: ScreenXCoord, y: ScreenYCoord, limX: ScreenXCoord,
startCharIndex: CARDINAL, s: STRING] RETURNS [firstUnusedCharIndex: CARDINAL] =
-- Fills the screen section [x..limX) with characters from s, breaking just before a character
-- begins.
BEGIN
curX: ScreenXCoord ← x;
char: CHARACTER;
charString: STRING ← [120];
i: CARDINAL ← 0;

FOR firstUnusedCharIndex IN [startCharIndex .. s.length) DO
IF (charString[i] ← char ← s[firstUnusedCharIndex]) = opD.substringSeparator THEN EXIT;
IF (curX ← dsD.GetCharRightX[char, curX]) > limX THEN EXIT;
i ← i + 1;
REPEAT
FINISHED => firstUnusedCharIndex ← s.length;
ENDLOOP;
charString.length ← firstUnusedCharIndex - startCharIndex;
[] ← dsD.PutStringInBitMap[x, y, charString, plainFace];
END; -- of FillScreenWithChars --


FillScreenWithWords: PUBLIC PROCEDURE [x: ScreenXCoord, y: ScreenYCoord,
limX: ScreenXCoord, startCharIndex: CARDINAL, s: STRING]
RETURNS [firstUnusedCharIndex: CARDINAL, firstEmptyX: ScreenXCoord] =
-- Fills the screen section [x..limX) with characters from s, breaking just before a word
-- begins. All white space characters remain on the same line as the word they follow.
BEGIN
curCharIndex: CARDINAL;
curX: ScreenXCoord ← x;
charString: STRING ← [120];

[firstUnusedCharIndex, firstEmptyX]
← PretendToFillScreenWithWords[x, limX, startCharIndex, s];
FOR curCharIndex IN [startCharIndex .. firstUnusedCharIndex) DO
charString[curCharIndex - startCharIndex] ← s[curCharIndex];
ENDLOOP;
charString.length ← firstUnusedCharIndex - startCharIndex;
[] ← dsD.PutStringInBitMap[x, y, charString, plainFace];
END; -- of FillScreenWithWords --


PretendToFillScreenWithWords: PROCEDURE [x: ScreenXCoord, limX: ScreenXCoord,
startCharIndex: CARDINAL, s: STRING] RETURNS [firstUnusedCharIndex: CARDINAL,
firstEmptyX: ScreenXCoord] =
-- Fills the screen section [x..limX) with characters from s, breaking just before a word
-- begins, at a substringSeparator character, or at the end of screen section in a word that
-- fills the entire section. All white space characters following a word are considered as
-- part of that word. The firstUnusedCharIndex returned may be the index of a
-- substringSeparator character.
BEGIN
state: {inWhite, inWord} ← inWord;
firstErasableX: ScreenXCoord ← x;
curCharIndex: CARDINAL ← startCharIndex;
curX: ScreenXCoord ← x;
newCurX: ScreenXCoord;
char: CHARACTER;

WhiteChar: PROCEDURE [c: CHARACTER] RETURNS [BOOLEAN] =
BEGIN
RETURN[(c = Ascii.SP) OR (c = Ascii.TAB)];
END; -- of WhiteChar --

firstUnusedCharIndex ← startCharIndex;
UNTIL curCharIndex >= s.length DO
char ← s[curCharIndex];
IF char = opD.substringSeparator THEN EXIT;
IF WhiteChar[char] THEN state ← inWhite
ELSE IF state = inWhite THEN BEGIN
-- transition from white space to nonwhite space --
firstUnusedCharIndex ← curCharIndex;
firstErasableX ← curX;
state ← inWord;
END;
IF (newCurX ← dsD.GetCharRightX[char, curX]) >= limX THEN BEGIN
-- character does not fit --
IF firstUnusedCharIndex # startCharIndex THEN
GOTO backupCurrentWord
ELSE -- break in middle of long word -- EXIT;
END;
curCharIndex ← curCharIndex + 1;
curX ← newCurX;
REPEAT
backupCurrentWord => BEGIN
-- break before current word --
firstEmptyX ← firstErasableX;
RETURN;
END;
ENDLOOP;
-- string s terminated, substringSeparator found, or break long word --
firstUnusedCharIndex ← curCharIndex;
firstEmptyX ← curX;
END; -- of PretendToFillScreenWithWords --


MapYToTOCIndex: PUBLIC PROCEDURE [y: ScreenYCoord, tnp: TOCTextNbrPtr]
RETURNS [vmD.TOCIndex] =
-- Returns the vmD.TOCIndex of the TOCEntry that occupies the line containing y.
BEGIN
line: LinePtr;
line ← MapYToTOCLine[y, tnp];
IF line.state = index THEN RETURN[line.linePair.index]
ELSE RETURN[vmD.GetFirstFreeTOCIndex[] - 1];
END; -- of MapYToTOCIndex --


ThisIsAFirstLine: PUBLIC PROCEDURE [y: ScreenYCoord, tnp: TOCTextNbrPtr]
RETURNS [BOOLEAN] =
-- Returns TRUE if y is in a first line of a TOCEntry, FALSE otherwise.
BEGIN
line: LinePtr ← MapYToTOCLine[y, tnp];
RETURN[line.state = index AND line.linePair.lineNumber = 1];
END; -- of ThisIsAFirstLine --


MapYToTOCLine: PROCEDURE [y: ScreenYCoord, tnp: TOCTextNbrPtr] RETURNS [LinePtr] =
-- Returns the LinePtr for the line containing y.
BEGIN
nLines: CARDINAL;
line: LinePtr;
nLines ← LOOPHOLE[(y - tnp.topY), CARDINAL] / dsD.lineHeight;
line ← tnp.lines;
THROUGH [1 .. nLines] DO line ← line.nextLine; ENDLOOP;
RETURN[line];
END; -- of MapYToTOCLine --


MapTOCIndexToTopY: PUBLIC PROCEDURE [index: vmD.TOCIndex, tnp: TOCTextNbrPtr]
RETURNS [ScreenYCoord, BOOLEAN] =
-- Returns the first scanline for index. Returns FALSE if index is not currently displayed.
BEGIN
line: LinePtr;
line ← MapTOCIndexToTOCLine[index, tnp];
IF line = NIL THEN RETURN [tnp.topY, FALSE]
ELSE RETURN [line.y, TRUE];
END; -- of MapTOCIndexToTopY --


MapTOCIndexToTOCLine: PUBLIC PROCEDURE [index: vmD.TOCIndex, tnp: TOCTextNbrPtr,
line: LinePtr ← NIL] RETURNS [LinePtr] =
-- Returns the tocLine for index. Returns NIL if index is not currently displayed. Starts
-- search at line if not NIL.
BEGIN
IF line = NIL THEN line ← tnp.lines;
UNTIL line = tnp.firstLineOffScreen DO
IF line.linePair.index = index THEN RETURN[line];
line ← line.nextLine;
ENDLOOP;
RETURN[NIL];
END; -- of MapTOCIndexToTOCLine --


ScrollUpTOC: PUBLIC PROCEDURE [y: ScreenYCoord, tnp: TOCTextNbrPtr] =
-- Computes number of lines to scroll up, moves area of screen up, refreshes bottom of TOC
-- with new text, and fixes up Line structures.
BEGIN
shiftNum: CARDINAL;
line, lastLine, newLastLine, newTopLine: LinePtr;
lineY: ScreenYCoord;
firstFree: vmD.TOCIndex;

IF ~intC.haveMailFile THEN RETURN;
shiftNum ← MAX[(y - tnp.topY) / dsD.lineHeight, 1];

-- calculate possible smaller shiftNum --
line ← MapYToTOCLine[y, tnp];
IF line = tnp.lines THEN
IF line.state = end THEN RETURN ELSE line ← line.nextLine;
IF line.state = empty THEN BEGIN
shiftNum ← 0;
FOR line ← tnp.lines, line.nextLine UNTIL line.state = end DO
shiftNum ← shiftNum + 1;
ENDLOOP;
END;
IF shiftNum = 0 THEN RETURN;

dsD.SlideFullWidthRectangleVertically
[top: tnp.topY + shiftNum * dsD.lineHeight, bottom: tnp.bottomY, newTop: tnp.topY];
newLastLine ← tnp.lines;
THROUGH [1 .. shiftNum) DO
newLastLine ← newLastLine.nextLine;
ENDLOOP;
newTopLine ← newLastLine.nextLine;
lastLine ← tnp.firstLineOffScreen;
newLastLine.nextLine ← NIL;
lastLine.nextLine ← tnp.lines;
tnp.lines ← newTopLine;
tnp.firstLineOffScreen ← newLastLine;
lineY ← tnp.topY;
FOR line ← newTopLine, line.nextLine UNTIL line = NIL DO
line.y ← lineY;
lineY ← lineY + dsD.lineHeight;
ENDLOOP;
IF lastLine.state = empty THEN BEGIN
firstFree ← vmD.GetFirstFreeTOCIndex[];
FOR line ← lastLine.nextLine, line.nextLine UNTIL line = NIL DO
line.state ← empty;
line.linePair ← LinePair[firstFree, 1];
ENDLOOP;
END
ELSE DisplayTOCTail[tnp, lastLine, lastLine.linePair.index, lastLine.linePair.lineNumber];
UpdateTOCThumbLine[];
END; -- of ScrollUpTOC --


ScrollDownTOC: PUBLIC PROCEDURE [y: ScreenYCoord, tnp: TOCTextNbrPtr] =
-- Computes number of lines to scroll down, moves area of screen down, refreshes top of
-- TOC with new text, and fixes up Line structures.
BEGIN
shiftNum, lineCount, nLines: CARDINAL;
line, lastLine, newLastLine, newTopLine: LinePtr;
lineY: ScreenYCoord;
i, lastInvisibleIndex, lastIndexToRefresh, newTopIndex: vmD.TOCIndex;
newTopLineNum: TOCLineNumber;
IF ~intC.haveMailFile THEN RETURN;
shiftNum ← MAX[(y - tnp.topY) / dsD.lineHeight, 1];

-- calculate possible smaller shiftNum --
line ← MapYToTOCLine[y, tnp];
IF tnp.lines.state = end THEN BEGIN
lineCount ← 0;
lastInvisibleIndex ← vmD.GetFirstFreeTOCIndex[] - 1;
END
ELSE BEGIN
lineCount ← tnp.lines.linePair.lineNumber - 1;
lastInvisibleIndex ← tnp.lines.linePair.index - 1;
END;
lastIndexToRefresh ← IF lineCount = 0 THEN lastInvisibleIndex ELSE lastInvisibleIndex + 1;
FOR i ← lastInvisibleIndex, i-1 UNTIL (i=0) OR (lineCount >= shiftNum) DO
[, nLines] ← DisplayTOCEntry[tnp, i, 1, NIL];
lineCount ← lineCount + nLines;
ENDLOOP;
newTopIndex ← i + 1;
IF lineCount <= shiftNum THEN {shiftNum ← lineCount; newTopLineNum ← 1}
ELSE newTopLineNum ← lineCount - shiftNum + 1;
IF shiftNum = 0 THEN RETURN;
dsD.SlideFullWidthRectangleVertically
[top: tnp.topY, bottom: tnp.bottomY - shiftNum * dsD.lineHeight,
newTop: tnp.topY + shiftNum * dsD.lineHeight];
newLastLine ← tnp.lines;
THROUGH [0 .. LOOPHOLE[LOOPHOLE[tnp.bottomY - tnp.topY, CARDINAL] / dsD.lineHeight - shiftNum, CARDINAL]) DO
newLastLine ← newLastLine.nextLine;
ENDLOOP;
newTopLine ← newLastLine.nextLine;
lastLine ← tnp.firstLineOffScreen;
newLastLine.nextLine ← NIL;
lastLine.nextLine ← tnp.lines;
tnp.lines ← newTopLine;
tnp.firstLineOffScreen ← newLastLine;
lineY ← tnp.topY;
FOR line ← newTopLine, line.nextLine UNTIL line = NIL DO
line.y ← lineY;
lineY ← lineY + dsD.lineHeight;
ENDLOOP;
[line,] ← DisplayTOCEntry[tnp, newTopIndex, newTopLineNum - 1, newTopLine];
FOR i IN [newTopIndex + 1 .. lastIndexToRefresh] DO
[line,] ← DisplayTOCEntry[tnp, i, 0, line];
ENDLOOP;
tsD.ConsiderAll[tnp];
UpdateTOCThumbLine[];
END; -- of ScrollDownTOC --


NextTOCEntry: PUBLIC PROCEDURE [tnp: TOCTextNbrPtr, entry: vmD.TOCIndex]
RETURNS [exists: BOOLEAN, nextEntry: vmD.TOCIndex] =
-- Determines the next TOCEntry after entry according to the current TOC filter.
BEGIN
-- kludge for now with no filter.
nextEntry ← entry + 1;
exists ← nextEntry < vmD.GetFirstFreeTOCIndex[];
END; -- of NextTOCEntry --


ThumbTOC: PUBLIC PROCEDURE [tlnp: ThumbLineNbrPtr, x: ScreenXCoord] =
-- Determines which TOCEntry will be the first entry on screen after thumbing and
-- displays the appropriate TOCEntries.
BEGIN
xRange: CARDINAL ← tlnp.rightX - tlnp.leftX;
savedCursor: dsD.CursorShape;
firstFree, newTopIndex: vmD.TOCIndex;

IF ~intC.haveMailFile THEN RETURN;
firstFree ← vmD.GetFirstFreeTOCIndex[] -1;
IF firstFree = 0 THEN RETURN;
[savedCursor, , ] ← dsD.GetCursor[];
dsD.ChangeCursor[hourGlass];
newTopIndex ← Inline.LongDiv[Inline.LongMult[firstFree, x - tlnp.leftX], xRange]+1;
dsD.ClearRectangle
[inD.leftMargin, inD.rightMargin, intC.tocTextNbr.topY, intC.tocTextNbr.bottomY];
DisplayTOCTail[intC.tocTextNbr, intC.tocTextNbr.lines, newTopIndex, 1];
UpdateTOCThumbLine[];
dsD.ChangeCursor[savedCursor];
END; -- of ThumbTOC --


MakeTOCIndexVisible: PUBLIC PROCEDURE [index: vmD.TOCIndex] =
-- Makes the first line of index visible within the TOC window, including as much of the
-- entry as possible.
BEGIN
toc: TOCTextNbrPtr = intC.tocTextNbr;
firstIndex: vmD.TOCIndex ← toc.lines.linePair.index;
lastIndex: vmD.TOCIndex ← toc.firstLineOffScreen.linePair.index;
empty: BOOLEAN;
[empty, , ] ← DisplayedRange[index, index, toc];
SELECT TRUE FROM
(~empty OR toc.topY = toc.bottomY) => RETURN;
(index <= firstIndex AND index + 3 >= firstIndex) =>
UNTIL toc.lines.linePair = LinePair[index, 1] DO ScrollDownTOC[toc.topY, toc]; ENDLOOP;
(index IN [lastIndex .. lastIndex + 3]) =>
UNTIL ~empty AND (toc.lines.linePair.index = index
OR toc.firstLineOffScreen.linePair.index # index) DO
ScrollUpTOC[toc.topY, toc];
[empty, , ] ← DisplayedRange[index, index, toc];
ENDLOOP;
ENDCASE =>
BEGIN
dsD.ClearRectangle[leftMargin, rightMargin, toc.topY, toc.bottomY];
DisplayTOCTail[toc, toc.lines, index, 1];
END;
END; -- of MakeTOCIndexVisible --


Consider: PUBLIC PROCEDURE [lowIndex, highIndex: vmD.TOCIndex, tnp: TOCTextNbrPtr] =
-- Paints considered marker on the first lines of all visible TOCEntries in the range
-- [lowIndex .. highIndex].
BEGIN
Considerer[lowIndex, highIndex, tnp, dsD.replace];
END; -- of Consider --


Deconsider: PUBLIC PROCEDURE [lowIndex, highIndex: vmD.TOCIndex, tnp: TOCTextNbrPtr] =
-- Removes selected marker from the first lines of all visible TOCEntries in the range
-- [lowIndex .. highIndex].
BEGIN
Considerer[lowIndex, highIndex, tnp, dsD.erase];
END; -- of Deconsider --


Considerer: PROCEDURE [lowIndex, highIndex: vmD.TOCIndex, tnp: TOCTextNbrPtr,
function: dsD.BitBltFunction] =
-- Modifies selected marker from the first lines of all visible TOCEntries in the range
-- [lowIndex .. highIndex].
BEGIN
empty: BOOLEAN;
y: ScreenYCoord;
h, offset: CARDINAL;
lowC, highC, consideredIndex: vmD.TOCIndex;
[empty, lowC, highC] ← DisplayedRange[lowIndex, highIndex, tnp];
IF empty THEN RETURN;
[ , h] ← dsD.GetPictureParameters[triangle];
offset ← LOOPHOLE[(dsD.lineHeight - h), CARDINAL] / 2;
FOR consideredIndex IN [lowC .. highC] DO
[y, ] ← MapTOCIndexToTopY[consideredIndex, tnp];
dsD.PaintPicture[consideredLeftX, y + offset, triangle, function];
ENDLOOP;
END; -- of Considerer --


DisplayedRange: PROCEDURE [lowIndex, highIndex: vmD.TOCIndex, tnp: TOCTextNbrPtr]
RETURNS [empty: BOOLEAN, lowDisplayedInRange, highDisplayedInRange:
vmD.TOCIndex] =
-- Returns the range of those TOCEntries in [lowIndex..highIndex] whose first lines are
-- actually displayed on the screen.
BEGIN
line: LinePtr;
FOR line ← tnp.lines, line.nextLine DO
IF line = tnp.firstLineOffScreen OR line.state # index THEN RETURN[TRUE, 0, 0];
IF line.linePair.index IN [lowIndex .. highIndex] AND line.linePair.lineNumber = 1 THEN
EXIT;
ENDLOOP;
highDisplayedInRange ← lowDisplayedInRange ← line.linePair.index;
empty ← FALSE;
FOR line ← line, line.nextLine
UNTIL line = tnp.firstLineOffScreen OR line.state # index
OR line.linePair.index > highIndex DO
highDisplayedInRange ← line.linePair.index;
ENDLOOP;
END; -- of DisplayedRange --


END. -- of IntTOC --