-- file: IntTOC.mesa -- last edited by Brotz, June 1, 1982 4:28 PM -- last edited by Taft, May 9, 1983 10:56 AM DIRECTORY Ascii USING [CR, SP, TAB], dsD: FROM "DisplayDefs" USING [BitBltFunction, BlackenRectangle, ChangeCursor, ClearRectangle, CursorShape, erase, GetCharRightX, GetCursor, GetPictureParameters, lineHeight, PaintPicture, PutCharInBitMap, PutStringInBitMap, replace, ScreenXCoord, ScreenYCoord, SlideFullWidthRectangleVertically], Editor USING [nextCode], exD: FROM "ExceptionDefs" USING [SysBug], inD: FROM "InteractorDefs" USING [consideredLeftX, digitWidth, leftMargin, LinePair, LinePtr, markLeftX, maxLinesPerTOCEntry, NbrPtr, numberLeftX, rightMargin, ScreenXCoord, ScreenYCoord, ThumbLineNbrPtr, TOCChangeAction, TOCLineNumber, TOCTextNbrPtr, UpdateTOCThumbLine], Inline USING [LongDiv, LongMult], intCommon USING [dateLeftX, dateRightX, fromLeftX, fromRightX, source, subjectExtensionLeftX, subjectLeftX, tocTextNbr], opD: FROM "OperationsDefs" USING [maxTOCStringLength, substringSeparator], String USING [AppendDecimal], tsD: FROM "TOCSelectionDefs" USING [AddRange, ConsiderAll, IsSelected, LastSelectedEntry, NextSelectedEntry, PrevSelectedEntry, RemoveRange], vmD: FROM "VirtualMgrDefs" USING [FirstFreeTOCIndex, GetTOCFixedPart, GetTOCString, TOCFixedPart, TOCHandle, TOCIndex, UnlockTOC, WaitForLock]; IntTOC: PROGRAM IMPORTS dsD, exD, 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, key: CARDINAL, 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; toc: vmD.TOCHandle _ tnp.toc; IF ~tnp.haveToc THEN RETURN; firstFree _ vmD.FirstFreeTOCIndex[toc, key]; 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, key, index, lineNum - 1, line]; FOR i IN (index .. firstFree) UNTIL line = tnp.firstLineOffScreen DO [line, nLines] _ DisplayTOCEntry[tnp, key, 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, key]; END; -- of DisplayTOCTail -- DisplayTOCEntry: PUBLIC PROCEDURE [tnp: TOCTextNbrPtr, key: CARDINAL, 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, subjectLeftX, subjectExtensionLeftX: ScreenXCoord; y: ScreenYCoord; tocFixedPart: vmD.TOCFixedPart; tocString: STRING _ [opD.maxTOCStringLength]; stringIndex: CARDINAL _ 0; toc: vmD.TOCHandle _ tnp.toc; SkipToSubjectField: PROCEDURE = BEGIN THROUGH [1 .. 2] DO UNTIL tocString[stringIndex] = opD.substringSeparator DO stringIndex _ stringIndex + 1; ENDLOOP; stringIndex _ stringIndex + 1; ENDLOOP; END; -- of SkipToSubjectField -- IF tnp.displayFormatted THEN BEGIN vmD.GetTOCFixedPart[toc, key, index, @tocFixedPart]; subjectLeftX _ intC.subjectLeftX; subjectExtensionLeftX _ intC.subjectExtensionLeftX; END ELSE {subjectLeftX _ markLeftX; subjectExtensionLeftX _ markLeftX + 15}; vmD.GetTOCString[toc, key, index, tocString]; firstUnusedLine _ firstLine; -- Process the first line of a TOC Entry. nLines _ 1; IF (firstUnusedLine = NIL) OR (firstUnusedLine = tnp.firstLineOffScreen) OR (nLines <= suppressed) THEN -- First line is Not to be displayed. BEGIN SkipToSubjectField[]; -- Skip first line of subject. [stringIndex, ] _ PretendToFillScreenWithWords [subjectLeftX, rightMargin, stringIndex, tocString]; END ELSE BEGIN -- First line Is to be displayed y _ firstUnusedLine.y; dsD.ClearRectangle[markLeftX, rightMargin, y, y + dsD.lineHeight]; IF tnp.displayFormatted THEN BEGIN -- 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: field. 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; END ELSE SkipToSubjectField[]; -- Display first line of Subject: field. [stringIndex, firstFreeX] _ FillScreenWithWords[subjectLeftX, y, rightMargin, stringIndex, tocString]; IF tnp.displayFormatted AND 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 (tnp.displayFormatted AND nLines = maxLinesPerTOCEntry) DO nLines _ nLines + 1; IF (firstUnusedLine = NIL) OR (firstUnusedLine = tnp.firstLineOffScreen) OR (nLines <= suppressed) THEN -- Do not display this line. [stringIndex, ] _ PretendToFillScreenWithWords [subjectExtensionLeftX, rightMargin, stringIndex, tocString] ELSE BEGIN -- Display this line. y _ firstUnusedLine.y; dsD.ClearRectangle[subjectExtensionLeftX, rightMargin, y, y + dsD.lineHeight]; -- Display extra line of Subject: field. [stringIndex, firstFreeX] _ FillScreenWithWords[subjectExtensionLeftX, y, rightMargin, stringIndex, tocString]; IF tnp.displayFormatted AND tocFixedPart.deleted THEN dsD.BlackenRectangle [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 char = Ascii.CR OR char = Editor.nextCode THEN {s[curCharIndex] _ Ascii.CR; curCharIndex _ curCharIndex + 1; EXIT}; IF WhiteChar[char] THEN state _ inWhite ELSE IF state = inWhite THEN -- transition from white space to nonwhite space -- {firstUnusedCharIndex _ curCharIndex; firstErasableX _ curX; state _ inWord}; 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 => {firstEmptyX _ firstErasableX; RETURN}; ENDLOOP; -- string s terminated, substringSeparator found, or break long word -- RETURN[curCharIndex, 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 _ MapYToTOCLine[y, tnp]; RETURN[IF line.state = index THEN line.linePair.index ELSE line.linePair.index - 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 PROC [y: ScreenYCoord, tnp: TOCTextNbrPtr, key: CARDINAL] = -- 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 ~tnp.haveToc 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.FirstFreeTOCIndex[tnp.toc, key]; FOR line _ lastLine.nextLine, line.nextLine UNTIL line = NIL DO line.state _ empty; line.linePair _ LinePair[firstFree, 1]; ENDLOOP; END ELSE DisplayTOCTail[tnp, key, lastLine, lastLine.linePair.index, lastLine.linePair.lineNumber]; UpdateTOCThumbLine[tnp, key]; END; -- of ScrollUpTOC -- ScrollDownTOC: PUBLIC PROC [y: ScreenYCoord, tnp: TOCTextNbrPtr, key: CARDINAL] = -- 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 ~tnp.haveToc 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.FirstFreeTOCIndex[tnp.toc, key] - 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, key, 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, key, newTopIndex, newTopLineNum - 1, newTopLine]; FOR i IN [newTopIndex + 1 .. lastIndexToRefresh] DO [line,] _ DisplayTOCEntry[tnp, key, i, 0, line]; ENDLOOP; tsD.ConsiderAll[tnp, key]; UpdateTOCThumbLine[tnp, key]; END; -- of ScrollDownTOC -- NextTOCEntry: PUBLIC PROC [tnp: TOCTextNbrPtr, key: CARDINAL, 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.FirstFreeTOCIndex[tnp.toc, key]; END; -- of NextTOCEntry -- ThumbTOC: PUBLIC PROCEDURE [tlnp: ThumbLineNbrPtr, np: NbrPtr, 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; tnp: TOCTextNbrPtr _ WITH p: np SELECT FROM tocText => @p, ENDCASE => ERROR; toc: vmD.TOCHandle _ tnp.toc; key: CARDINAL _ 0; keyExists: BOOLEAN = (intC.source.key # 0); IF ~tnp.haveToc THEN RETURN; IF keyExists THEN key _ intC.source.key ELSE key _ vmD.WaitForLock[toc]; firstFree _ vmD.FirstFreeTOCIndex[toc, key] - 1; IF firstFree = 0 THEN {IF ~keyExists THEN vmD.UnlockTOC[toc, key]; RETURN}; [savedCursor, , ] _ dsD.GetCursor[]; dsD.ChangeCursor[hourGlass]; newTopIndex _ Inline.LongDiv[Inline.LongMult[firstFree, x - tlnp.leftX], xRange] + 1; dsD.ClearRectangle[leftMargin, rightMargin, tnp.topY, tnp.bottomY]; DisplayTOCTail[tnp, key, tnp.lines, newTopIndex, 1]; UpdateTOCThumbLine[tnp, key]; IF ~keyExists THEN vmD.UnlockTOC[toc, key]; dsD.ChangeCursor[savedCursor]; END; -- of ThumbTOC -- MakeTOCIndexVisible: PUBLIC PROCEDURE [index: vmD.TOCIndex, key: CARDINAL] = -- 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, key]; 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, key]; [empty, , ] _ DisplayedRange[index, index, toc]; ENDLOOP; ENDCASE => BEGIN dsD.ClearRectangle[leftMargin, rightMargin, toc.topY, toc.bottomY]; DisplayTOCTail[toc, key, toc.lines, index, 1]; END; END; -- of MakeTOCIndexVisible -- Consider: PUBLIC PROCEDURE [lowIndex, highIndex: vmD.TOCIndex, tnp: TOCTextNbrPtr, key: CARDINAL] = -- Paints considered marker on the first lines of all visible TOCEntries in the range -- [lowIndex .. highIndex]. BEGIN Considerer[lowIndex, highIndex, tnp, key, dsD.replace]; END; -- of Consider -- Deconsider: PUBLIC PROCEDURE [lowIndex, highIndex: vmD.TOCIndex, tnp: TOCTextNbrPtr, key: CARDINAL] = -- Removes selected marker from the first lines of all visible TOCEntries in the range -- [lowIndex .. highIndex]. BEGIN Considerer[lowIndex, highIndex, tnp, key, dsD.erase]; END; -- of Deconsider -- Considerer: PROCEDURE [lowIndex, highIndex: vmD.TOCIndex, tnp: TOCTextNbrPtr, key: CARDINAL, 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; IF key = 0 OR tnp.toc.key # key THEN exD.SysBug[]; [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 -- RefreshTOCChange: PUBLIC PROCEDURE [toc: vmD.TOCHandle, key: CARDINAL, index: vmD.TOCIndex, action: TOCChangeAction] = -- Updates the TOC selection according to action, and repaints the TOC neighborhood if toc -- is the one currently displayed there. A single TOC entry may be inserted at index or -- the index'th entry may be deleted or replaced by a new single entry. The change must -- have already been made in the virtual TOC. BEGIN OPEN tsD; i: vmD.TOCIndex _ index; line: LinePtr; tnp: TOCTextNbrPtr = intC.tocTextNbr; -- First, update TOC selection. SELECT action FROM delete => BEGIN i _ index; IF IsSelected[toc, key, index] THEN RemoveRange[toc, key, index, index + 1]; UNTIL (i _ NextSelectedEntry[toc, key, i]) = 0 DO RemoveRange[toc, key, i, i + 1]; AddRange[toc, key, i - 1, i]; ENDLOOP; END; insert => BEGIN FOR i _ LastSelectedEntry[toc, key], PrevSelectedEntry[toc, key, i] UNTIL i = 0 OR i < index DO RemoveRange[toc, key, i, i + 1]; AddRange[toc, key, i + 1, i + 2]; ENDLOOP; END; ENDCASE; -- Next, update line blocks and screen. SELECT TRUE FROM tnp.toc # toc => NULL; index < tnp.lines.linePair.index => -- action occurred before the displayed lines. SELECT action FROM insert, delete => BEGIN delta: INTEGER _ IF action = insert THEN 1 ELSE -1; FOR line _ tnp.lines, line.nextLine UNTIL line = NIL DO line.linePair.index _ line.linePair.index + delta; ENDLOOP; END; ENDCASE; index <= tnp.firstLineOffScreen.linePair.index => BEGIN -- action occurred on the screen (or in trivial case of only in first line off screen. line _ MapTOCIndexToTOCLine[index, tnp]; IF line = NIL THEN line _ tnp.firstLineOffScreen; IF action = replace AND index < tnp.firstLineOffScreen.linePair.index AND line.linePair.lineNumber = 1 THEN BEGIN -- We are replacing an entry that is completely displayed on the screen. oldLines: CARDINAL _ 1; FOR ln: LinePtr _ line.nextLine, ln.nextLine UNTIL ln.linePair.index # index DO oldLines _ oldLines + 1; ENDLOOP; IF oldLines = DisplayTOCEntry[tnp, key, index, 0, NIL].nLines THEN BEGIN -- Simple case: no ripple, no changes in following toc entries. dsD.ClearRectangle[leftMargin, rightMargin, line.y, line.y + dsD.lineHeight*oldLines]; [ , ] _ DisplayTOCEntry[tnp, key, index, 0, line]; IF tsD.IsSelected[toc, key, index] THEN Consider[index, index, tnp, key]; RETURN; END; END; dsD.ClearRectangle[leftMargin, rightMargin, line.y, tnp.bottomY]; DisplayTOCTail[tnp, key, line, index, 1]; END; ENDCASE; -- action occurred below screen; no fixup necessary. END; -- of RefreshTOCChange -- END. -- of IntTOC --z20461(529)\f1