-- 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 --z20461(529)\f1