-- file: EditorDisplay.Mesa -- last edited by Brotz, March 17, 1981 1:26 PM DIRECTORY Ascii, displayCommon: FROM "DisplayCommon", dsD: FROM "DisplayDefs", Editor, inD: FROM "InteractorDefs", Inline, intCommon: FROM "IntCommon", ovD: FROM "OverviewDefs", prD: FROM "ProtectionDefs", Storage, vmD: FROM "VirtualMgrDefs"; EditorDisplay: PROGRAM IMPORTS disC: displayCommon, dsD, inD, Inline, intC: intCommon, prD, Storage, vmD EXPORTS Editor, inD SHARES dsD, Editor, inD, vmD = PUBLIC BEGIN OPEN Editor, inD; -- Smart Display Section of the Editor Department -- Maintains lines and characters within lines on the screen. Handles scrolling, -- refreshing, mapping x,y to character indices. -- Global variables of the smart display highWaterMark: ovD.CharIndex; charBbtPtr: dsD.BbtPtr; RefreshFromFirstChange: PROCEDURE [actionIndex: ovD.CharIndex, deletedChars, insertedChars: CARDINAL, mnp: MessageTextNbrPtr] = -- A change in the message text has occured at actionPoint. Its relation to the screen is -- unknown. This routine is to fix up the screen to reflect the new state of the message -- text. BEGIN nextIndex, prevIndex, previousLineStartIndex, prevLineLastIndex: ovD.CharIndex; previousLine, startLine: LinePtr; rightX: ScreenXCoord; normalChar: CHARACTER; message: vmD.ComposeMessagePtr _ LOOPHOLE[mnp.message]; endChanged: BOOLEAN; AffectsPreviousLine: PROCEDURE RETURNS [affected: BOOLEAN] = -- Determines whether a change at actionIndex has affected the previous line. If so, -- affected is set to true. Line may be a visible line or the firstLineOffScreen. -- previousLine NIL if off top or nonexistent. If affected, rightX of previous line is -- computed whether previous line is on screen or not. BEGIN char: CHARACTER; firstUnitEndIndex: ovD.CharIndex; thisLineFirstCharIndex: ovD.CharIndex _ startLine.firstCharIndex; message: vmD.VirtualMessagePtr _ mnp.message; newRightX: ScreenXCoord; affected _ FALSE; IF thisLineFirstCharIndex = 0 OR (char _ vmD.GetMessageChar[message, thisLineFirstCharIndex - 1]) = Ascii.CR THEN RETURN; IF (previousLine _ FindLineBeforePlace[mnp.lines, startLine]) = NIL THEN BEGIN previousLineStartIndex _ SearchBackForFirstCharIndexOfLine[thisLineFirstCharIndex - 1, message]; rightX _ RangeRightXInLine [previousLineStartIndex, thisLineFirstCharIndex, inD.leftMargin, message]; END ELSE BEGIN rightX _ previousLine.rightX; previousLineStartIndex _ previousLine.firstCharIndex; END; IF dsD.GetCharProperty[char, alphaNumeric] THEN BEGIN -- previous line was broken at a long word IF thisLineFirstCharIndex < actionIndex THEN RETURN; IF thisLineFirstCharIndex >= vmD.GetMessageSize[message] THEN newRightX _ rightX ELSE newRightX _ dsD.GetCharRightX [vmD.GetMessageChar[message, thisLineFirstCharIndex], rightX]; END ELSE BEGIN [newRightX, firstUnitEndIndex] _ NextUnit[message, thisLineFirstCharIndex, rightX]; IF firstUnitEndIndex < actionIndex THEN RETURN; END; affected _ (newRightX <= inD.rightMargin) OR (thisLineFirstCharIndex < vmD.GetMessageSize[message] AND dsD.GetCharProperty [vmD.GetMessageChar[message, thisLineFirstCharIndex], white]); END; -- of AffectsPreviousLine -- ReformatWithinLine: PROCEDURE RETURNS [endChanged: BOOLEAN] = BEGIN trailingBlockStartX, rightX, insertionRightX, insertionLimit: ScreenXCoord; trailingBlockXLength, deletedXLength: CARDINAL; i, trailingBlockStartIndex, trailingBlockEndIndex, nextUnitEndIndex: ovD.CharIndex; deltaX: INTEGER; char: CHARACTER; get: POINTER TO vmD.CharCache _ @message.get; actionX: ScreenXCoord = MapCharIndexInLineToLeftX[actionIndex, startLine, message]; BEGIN -- for EXITS -- endChanged _ TRUE; -- guilty until proven otherwise. IF actionIndex + deletedChars >= startLine.nextLine.firstCharIndex THEN BEGIN -- deletion removed rest of line. startLine.state _ normalText; -- if inserting at end of message, may have been endOfMessage. GO TO GiveUp; END; -- some of line remains -- First, find how much space was deleted trailingBlockXLength _ 0; trailingBlockStartIndex _ actionIndex + insertedChars; trailingBlockEndIndex _ startLine.nextLine.firstCharIndex - deletedChars + insertedChars; FOR i IN [trailingBlockStartIndex .. trailingBlockEndIndex) DO char _ IF i IN [get.first .. get.free) THEN get.string[i + get.floor - get.first] ELSE vmD.GetMessageChar[message, i]; IF Inline.BITAND[char, ovD.CharMask] = Ascii.TAB THEN GO TO GiveUp ELSE trailingBlockXLength _ trailingBlockXLength + dsD.GetStaticCharWidth[char]; ENDLOOP; deletedXLength _ startLine.rightX - trailingBlockXLength - actionX; -- Check if insertion will fit on this line. insertionLimit _ inD.rightMargin - startLine.rightX + deletedXLength + actionX; insertionRightX _ actionX; FOR i IN [actionIndex .. trailingBlockStartIndex) DO char _ IF i IN [get.first .. get.free) THEN get.string[i + get.floor - get.first] ELSE vmD.GetMessageChar[message, i]; IF char >= ovD.LineBreakValue THEN vmD.PutMessageChar[message, i, char _ Inline.BITAND[char, ovD.CharMask]]; insertionRightX _ dsD.GetCharRightX[char, insertionRightX]; IF insertionRightX > insertionLimit OR char = Ascii.CR THEN GO TO GiveUp; ENDLOOP; IF insertionLimit < insertionRightX + inD.CRWidth AND trailingBlockEndIndex > 0 AND ~dsD.GetCharProperty [vmD.GetMessageChar[message, trailingBlockEndIndex - 1], white] THEN GO TO GiveUp; -- Entire trailing block will remain on this line. Rearrange screen so far. trailingBlockStartX _ startLine.rightX - trailingBlockXLength; deltaX _ insertionRightX - actionX - deletedXLength; dsD.SlideRectangleHorizontally [trailingBlockStartX, startLine.rightX, startLine.y, startLine.nextLine.y, deltaX]; dsD.ClearRectangle[actionX, trailingBlockStartX + deltaX, startLine.y, startLine.nextLine.y]; [ , ] _ CharacterBlaster[actionIndex, trailingBlockStartIndex, message, actionX, startLine.y]; -- Will text move up from next line? [rightX, nextUnitEndIndex] _ NextUnit [message, trailingBlockEndIndex, startLine.rightX + deltaX]; IF deltaX < 0 AND (char _ vmD.GetMessageChar[message, trailingBlockEndIndex - 1]) # Ascii.CR AND (~dsD.GetCharProperty[char, white] OR rightX < inD.rightMargin OR (trailingBlockEndIndex < vmD.GetMessageSize[message] AND dsD.GetCharProperty [vmD.GetMessageChar[message, trailingBlockEndIndex], white])) THEN [nextIndex, startLine.rightX] _ FormatAndDisplaySuffix [mnp, trailingBlockEndIndex, startLine.firstCharIndex, startLine.rightX + deltaX, startLine] ELSE BEGIN -- text will not move up, the buck stops here. nextIndex _ trailingBlockEndIndex; startLine.rightX _ startLine.rightX + deltaX; endChanged _ FALSE; END; EXITS GiveUp => BEGIN -- line will definitely terminate at a different character than before. dsD.ClearRectangle[actionX, startLine.rightX, startLine.y, startLine.nextLine.y]; [nextIndex, startLine.rightX] _ FormatAndDisplaySuffix [mnp, actionIndex, startLine.firstCharIndex, actionX, startLine]; END; END; END; -- of ReformatWithinLine -- ReformatScreenLines: PROCEDURE [startIndex: ovD.CharIndex] = -- startIndex will be the first char index on startLine. A change of insertedChars and -- deletedChars has occurred at actionIndex. Reformat and redisplay the minimum -- number of screen lines from startLine down so that all lines are properly formatted. -- Any lines whose bitmaps are reusable in their present form should be reused. BEGIN line, startCopyLine, endOfTextLine, firstLineNotCopied, endCopyLine, usableLine, lineToReformat: LinePtr; moveEOM: BOOLEAN _ FALSE; correctedIndex: ovD.CharIndex; insertionEnd: ovD.CharIndex = actionIndex + insertedChars; deltaChars: INTEGER = insertedChars - deletedChars; message: vmD.ComposeMessagePtr = LOOPHOLE[mnp.message]; messageLength: ovD.CharIndex = vmD.GetMessageSize[message]; firstLineOffScreen: LinePtr = mnp.firstLineOffScreen; PaintNewLines: PROCEDURE [startLine, endLine: LinePtr] = BEGIN FOR line: LinePtr _ startLine, line.nextLine UNTIL line = endLine DO ClearLine[line]; [line.rightX, ] _ CharacterBlaster [line.pendingIndex, line.nextLine.pendingIndex, message, leftMargin, line.y]; line.firstCharIndex _ line.pendingIndex; line.state _ normalText; ENDLOOP; END; -- of PaintNewLines -- -- find first line that may be usable in its present form. FOR usableLine _ startLine, usableLine.nextLine UNTIL usableLine = firstLineOffScreen DO IF actionIndex + deletedChars <= usableLine.firstCharIndex THEN EXIT; ENDLOOP; lineToReformat _ startLine; DO -- decide which lines will be copied, which lines will be reformatted. correctedIndex _ usableLine.firstCharIndex + deltaChars; SELECT TRUE FROM (lineToReformat = firstLineOffScreen) => BEGIN lineToReformat.pendingIndex _ startIndex; startCopyLine _ endCopyLine _ endOfTextLine _ firstLineOffScreen; firstLineNotCopied _ usableLine; EXIT; END; (usableLine = firstLineOffScreen OR startIndex < correctedIndex) => BEGIN -- a line must be reformatted as no usable line exists for this startIndex. lineToReformat.pendingIndex _ startIndex; IF startIndex = messageLength THEN BEGIN -- there will be no copying, must paint the End of message indicator. startCopyLine _ endCopyLine _ usableLine _ firstLineNotCopied _ endOfTextLine _ lineToReformat; EXIT; END; lineToReformat _ lineToReformat.nextLine; [startIndex, , ] _ FormatLine[startIndex, message]; END; (startIndex > correctedIndex) => -- current usableLine is not usable after all. usableLine _ usableLine.nextLine; (startIndex = correctedIndex) => BEGIN -- all lines from usableLine on are good. Move as many lines as we can. startCopyLine _ lineToReformat; firstLineNotCopied _ usableLine; UNTIL lineToReformat = firstLineOffScreen OR firstLineNotCopied = firstLineOffScreen OR firstLineNotCopied.state = trailingBlankLine DO lineToReformat.pendingIndex _ firstLineNotCopied.firstCharIndex + deltaChars; lineToReformat _ lineToReformat.nextLine; IF firstLineNotCopied.state = endOfMessage THEN moveEOM _ TRUE; firstLineNotCopied _ firstLineNotCopied.nextLine; ENDLOOP; endCopyLine _ lineToReformat; -- Reformat remainder of lines. startIndex _ firstLineNotCopied.firstCharIndex - deletedChars + insertedChars; UNTIL lineToReformat = firstLineOffScreen OR startIndex = messageLength DO lineToReformat.pendingIndex _ startIndex; [startIndex, , ] _ FormatLine[startIndex, message]; lineToReformat _ lineToReformat.nextLine; ENDLOOP; lineToReformat.pendingIndex _ startIndex; endOfTextLine _ lineToReformat; EXIT; END; ENDCASE; ENDLOOP; -- now rearrange screen according to previous decisions. dsD.MoveFullWidthRectangleVertically [top: usableLine.y, bottom: firstLineNotCopied.y, newTop: startCopyLine.y]; -- now move the rightXs of these same lines IF startCopyLine.y <= usableLine.y THEN --moving up-- MoveRightXs[fromStart: usableLine, fromEnd: firstLineNotCopied, toStart: startCopyLine] ELSE --moving down-- BEGIN DestructiveReverse[usableLine]; MoveRightXs [fromStart: firstLineNotCopied.nextLine, fromEnd: NIL, toStart: endCopyLine.nextLine]; DestructiveReverse[firstLineOffScreen]; END; PaintNewLines[startLine, startCopyLine]; FOR line _ startCopyLine, line.nextLine UNTIL line = endCopyLine DO line.state _ IF (line.firstCharIndex _ line.pendingIndex) = messageLength THEN endOfMessage ELSE normalText; ENDLOOP; PaintNewLines[endCopyLine, endOfTextLine]; -- We've painted all the text that there is. Now clean up with possible "End of message", trailing blank lines, and fix up mnp.firstLineOffScreen. IF endOfTextLine = firstLineOffScreen THEN BEGIN endOfTextLine.firstCharIndex _ endOfTextLine.pendingIndex; endOfTextLine.state _ SELECT TRUE FROM endOfTextLine.pendingIndex # messageLength => normalText, moveEOM => trailingBlankLine, ENDCASE => endOfMessage; END ELSE BEGIN IF ~moveEOM THEN BEGIN PaintEndOfMessageLine[mnp, endOfTextLine]; endOfTextLine _ endOfTextLine.nextLine; END; ClearScreenLinesToTrailingBlankLines[endOfTextLine, messageLength, mnp]; END; END; -- of ReformatScreenLines -- IF deletedChars = 0 AND insertedChars = 0 THEN RETURN; IF mnp.protectedFieldPtr#NIL THEN prD.AdjustProtFields[pfpp: @mnp.protectedFieldPtr, actionIndex: actionIndex, deletedChars: deletedChars, insertedChars: insertedChars]; startLine _ MapCharIndexToLine[actionIndex, mnp]; IF startLine = NIL THEN BEGIN prevIndex _ actionIndex; THROUGH [1 .. 2] DO IF prevIndex = 0 THEN EXIT; prevIndex _ SearchBackForFirstCharIndexOfLine[prevIndex - 1, message]; ENDLOOP; highWaterMark _ MIN[highWaterMark, prevIndex]; -- Refresh without painting underlines. RefreshToPlaceCharOnLine[actionIndex, mnp.lines, mnp, FALSE]; RETURN; END; IF AffectsPreviousLine[] THEN BEGIN prevLineLastIndex _ startLine.firstCharIndex - 1; normalChar _ Inline.BITAND[vmD.GetMessageChar[message, prevLineLastIndex], ovD.CharMask]; vmD.PutMessageChar[message, prevLineLastIndex, normalChar]; IF previousLine # NIL THEN -- previousLine is on screen -- [nextIndex, previousLine.rightX] _ FormatAndDisplaySuffix [mnp, startLine.firstCharIndex, previousLineStartIndex, rightX, previousLine] ELSE -- previousLine is not visible -- [nextIndex, , ] _ FormatLine[previousLineStartIndex, message]; ClearLine[startLine]; startLine.firstCharIndex _ nextIndex; startLine.state _ normalText; [nextIndex, startLine.rightX] _ FormatAndDisplaySuffix[mnp, nextIndex, nextIndex, inD.leftMargin, startLine]; endChanged _ TRUE; END ELSE -- previous line not affected -- endChanged _ ReformatWithinLine[]; IF endChanged THEN BEGIN IF startLine.firstCharIndex # vmD.GetMessageSize[message] THEN startLine _ startLine.nextLine; ReformatScreenLines[nextIndex]; END ELSE AdjustFirstCharIndices[startLine.nextLine, insertedChars - deletedChars]; highWaterMark _ mnp.firstLineOffScreen.firstCharIndex; [] _ MakeCharIndexVisible[actionIndex, mnp]; END; -- of RefreshFromFirstChange -- CharacterBlaster: PROCEDURE [startIndex, endIndex: ovD.CharIndex, message: vmD.VirtualMessagePtr, startX: ScreenXCoord, topY: ScreenYCoord] RETURNS [nextX: ScreenXCoord, nextIndex: ovD.CharIndex] = -- Blasts characters [startIndex, endIndex) on the screen starting at [startX, topY] as fast as -- possible. Will stop blasting out characters if a PCR bit or CR is seen. BEGIN i: ovD.CharIndex; x, y: CARDINAL; char: CHARACTER; offsetPtr: POINTER; charFontPtr: dsD.CharFontPtr; get: POINTER TO vmD.CharCache _ @message.get; endOfLine: BOOLEAN _ FALSE; -- set x, y to bitmap relative coordinates. x _ startX - dsD.xOrigin; y _ topY - dsD.yOrigin; FOR i IN [startIndex .. endIndex) DO char _ IF i IN [get.first .. get.free) THEN get.string[i + get.floor - get.first] ELSE vmD.GetMessageChar[message, i]; IF char >= ovD.LineBreakValue OR char = Ascii.CR THEN {endOfLine _ TRUE; char _ Inline.BITAND[char, ovD.CharMask]}; SELECT char FROM Ascii.SP, Ascii.TAB, Ascii.CR => x _ dsD.GetCharRightX[char, x + dsD.xOrigin] - dsD.xOrigin; ENDCASE => BEGIN offsetPtr _ disC.mcFont + LOOPHOLE[char, CARDINAL]; charFontPtr _ offsetPtr + offsetPtr^; x _ (charBbtPtr.dlx _ x) + (charBbtPtr.dw _ charFontPtr.width); charBbtPtr.dty _ y + charFontPtr.ySkip; charBbtPtr.sbca _ LOOPHOLE[charFontPtr - (charBbtPtr.dh _ charFontPtr.height)]; dsD.BitBlt[charBbtPtr]; END; IF endOfLine THEN {endIndex _ i + 1; EXIT}; ENDLOOP; RETURN[x + dsD.xOrigin, endIndex]; END; -- of CharacterBlaster -- MoveRightXs: PROCEDURE [fromStart, fromEnd, toStart: LinePtr] = -- Moves rightX fields of specified line range BEGIN FOR fromStart _ fromStart, fromStart.nextLine UNTIL fromStart = fromEnd DO toStart.rightX _ fromStart.rightX; toStart _ toStart.nextLine; ENDLOOP; END; -- of MoveRightXs -- RefreshToPlaceCharOnLine: PROCEDURE [startCharIndex: ovD.CharIndex, line: LinePtr, mnp: MessageTextNbrPtr, underline: BOOLEAN _ TRUE] = -- Refresh CM window from line on (inclusive), so that firstCharIndex appears somewhere -- in line after refresh. Selections will be refreshed with their underlines iff underline -- is TRUE. BEGIN -- Advance highwater mark past startCharIndex to insure that head of message is properly -- formatted. Find the index of the first character on the line which contains -- startCharIndex. Refresh the screen. message: vmD.ComposeMessagePtr _ LOOPHOLE[mnp.message]; firstCharIndex: ovD.CharIndex; UNTIL highWaterMark >= startCharIndex DO [highWaterMark, , ] _ FormatLine[highWaterMark, message]; ENDLOOP; firstCharIndex _ SearchBackForFirstCharIndexOfLine[startCharIndex, message]; RefreshSoThatFirstCharStartsLine[firstCharIndex, line, mnp, underline]; END; -- of RefreshToPlaceCharOnLine -- RefreshSoThatFirstCharStartsLine: PROCEDURE [firstChar: ovD.CharIndex, firstLine: LinePtr, mnp: MessageTextNbrPtr, underline: BOOLEAN _ TRUE] = -- Refresh all lines of screen in range [firstLine .. bottomLine], so that firstChar starts -- firstLine. Selections will be refreshed with their underlines iff underline is TRUE. -- Side effects: advances highWaterMark. BEGIN message: vmD.ComposeMessagePtr _ LOOPHOLE[mnp.message]; messageLength: ovD.CharIndex _ vmD.GetMessageSize[message]; line: LinePtr; dsD.ClearRectangle[leftMargin, rightMargin, firstLine.y, mnp.bottomY]; FOR line _ firstLine, line.nextLine DO line.firstCharIndex _ firstChar; IF (line = mnp.firstLineOffScreen) THEN GOTO ScreenHasNowBeenFilled; IF (firstChar = messageLength) THEN GOTO TrailingScreenLinesRemain; line.state _ normalText; IF firstChar >= highWaterMark THEN [ , , ] _ FormatLine[firstChar, message]; [firstChar, , ] _ RompAndStomp[line, firstChar, mnp, underline] REPEAT ScreenHasNowBeenFilled => BEGIN line.state _ IF firstChar = messageLength THEN endOfMessage ELSE normalText; line.firstCharIndex _ firstChar; END; TrailingScreenLinesRemain => BEGIN line.rightX _ leftMargin; PaintEndOfMessageLine[mnp, line]; ClearScreenLinesToTrailingBlankLines [startLine: line.nextLine, charIndex: messageLength, mnp: mnp]; END; ENDLOOP; highWaterMark _ MAX[firstChar, highWaterMark]; END; -- of RefreshSoThatFirstCharStartsLine -- RefreshEndOfMessage: PROCEDURE [mnp: MessageTextNbrPtr] = -- Repaints "End of Message" line if visible with current endOfMessageString. BEGIN FOR line: LinePtr _ mnp.lines, line.nextLine UNTIL line = mnp.firstLineOffScreen DO IF line.state = endOfMessage THEN {PaintEndOfMessageLine[mnp, line]; RETURN}; ENDLOOP; END; -- of RefreshEndOfMessage -- PaintEndOfMessageLine: PROCEDURE [mnp: MessageTextNbrPtr, line: LinePtr] = BEGIN line.state _ endOfMessage; line.firstCharIndex _ vmD.GetMessageSize[mnp.message]; ClearLine[line]; line.rightX _ dsD.PutStringInBitMap[inD.leftMargin, line.y, mnp.endString, italicFace]; END; -- of PaintEndOfMessageLine -- MapXInLineToCharIndex: PROCEDURE [x: ScreenXCoord, line: LinePtr, message: vmD.VirtualMessagePtr] RETURNS[index: ovD.CharIndex, leftX, rightX: ScreenXCoord] = -- Returns the charIndex covering x in line and returns the x bounds of that charIndex. BEGIN -- If x is to right of rightmost character on line, will return info for rightmost character. -- If line is empty, will return charIndex for next character in message from point given -- and will return empty x interval. i: ovD.CharIndex; get: POINTER TO vmD.CharCache _ @message.get; leftX _ rightX _ inD.leftMargin; index _ line.firstCharIndex; FOR i IN [index .. line.nextLine.firstCharIndex) DO leftX _ rightX; index _ i; rightX _ dsD.GetCharRightX [IF index IN [get.first .. get.free) THEN get.string[index + get.floor - get.first] ELSE vmD.GetMessageChar[message, index], rightX]; IF x < rightX THEN RETURN; ENDLOOP; END; -- of MapXInLineToCharIndex -- ScrollUpCM: PROCEDURE [y: ScreenYCoord, mnp: MessageTextNbrPtr] = -- y is the ScreenYCoord of the cursor. Scrolls the screen Up, based on this y position. -- Adjusts all structures to reflect the new screen contents. BEGIN oldTopLine, newBottomScreenLine, lastLineOnScreen: LinePtr; messageLength: ovD.CharIndex _ vmD.GetMessageSize[mnp.message]; firstLineOffScreen: LinePtr = mnp.firstLineOffScreen; oldTopLine _ MapYToNonBlankLine[y, mnp]; IF oldTopLine = mnp.lines THEN IF oldTopLine.state = endOfMessage THEN RETURN ELSE oldTopLine _ oldTopLine.nextLine; newBottomScreenLine _ MoveLines[top: oldTopLine, bottom: firstLineOffScreen, newTop: mnp.lines, mnp: mnp]; lastLineOnScreen _ FindLineBeforePlace[startLine: newBottomScreenLine, place: firstLineOffScreen]; IF lastLineOnScreen = NIL OR lastLineOnScreen.firstCharIndex # messageLength THEN RefreshSoThatFirstCharStartsLine[firstChar: firstLineOffScreen.firstCharIndex, firstLine: newBottomScreenLine, mnp: mnp] ELSE ClearScreenLinesToTrailingBlankLines [startLine: newBottomScreenLine, charIndex: messageLength, mnp: mnp] END; -- of ScrollUpCM -- MakeCharIndexVisible: PROCEDURE [index: ovD.CharIndex, mnp: MessageTextNbrPtr, offScreenSlop: CARDINAL _ 40] RETURNS [line: LinePtr] = -- Ensures that the caret will be visible during type in. BEGIN message: vmD.VirtualMessagePtr _ mnp.message; crCount: CARDINAL _ 0; i, firstIndexOffScreen: ovD.CharIndex; IF mnp.nLines < 2 THEN MoveDMCMBoundary [intC.dmcmBoundaryPadNbr, intC.dmcmBoundaryPadNbr.topY - dsD.lineHeight * (2 - mnp.nLines)]; IF (line _ MapCharIndexToLine[index, mnp]) # NIL THEN RETURN; offScreenSlop _ 240; -- ##change in interface. firstIndexOffScreen _ mnp.firstLineOffScreen.firstCharIndex; IF index IN [firstIndexOffScreen .. firstIndexOffScreen + offScreenSlop] AND mnp.lines.state # endOfMessage THEN FOR i _ firstIndexOffScreen, i + 1 UNTIL i >= index DO IF vmD.GetMessageChar[message, i] = Ascii.CR THEN crCount _ crCount + 1; IF crCount > 4 THEN EXIT; REPEAT FINISHED => BEGIN WHILE line = NIL DO ScrollUpCM[mnp.topY + dsD.lineHeight, mnp]; line _ MapCharIndexToLine[index, mnp]; ENDLOOP; RETURN; END; ENDLOOP; RefreshToPlaceCharOnLine[index, (line _ mnp.lines), mnp]; END; -- of MakeCharIndexVisible -- MapYToNonBlankLine: PROCEDURE [y: ScreenYCoord, mnp: MessageTextNbrPtr] RETURNS [line: LinePtr] = -- Returns the screen line associated with y. If y falls on blank line below the end of -- message, then returns the end of message line. BEGIN FOR line _ mnp.lines, line.nextLine UNTIL (line.state = endOfMessage) OR (line.nextLine.y > y) DO ENDLOOP; END; -- of MapYToNonBlankLine -- ClearScreenLinesToTrailingBlankLines: PROCEDURE [startLine: LinePtr, charIndex: ovD.CharIndex, mnp: MessageTextNbrPtr] = -- Clears the screen bit map and fixes the line blocks for all lines from [startLine .. -- firstLineOffScreen]. BEGIN FOR line: LinePtr _ startLine, line.nextLine UNTIL line = NIL DO IF line.state # trailingBlankLine AND line # mnp.firstLineOffScreen THEN ClearLine[line]; line.firstCharIndex _ charIndex; line.state _ trailingBlankLine; line.rightX _ inD.leftMargin; ENDLOOP; END; -- of ClearScreenLinesToTrailingBlankLines -- ScrollDownCM: PROCEDURE [y: ScreenYCoord, mnp: MessageTextNbrPtr] = -- y is the ScreenYCoord of the cursor. Scrolls the screen Down, based on this y position. -- Adjusts all structures to reflect the new screen contents. BEGIN numberOfScreenLinesAbove, numberOfLinesAvailable, i: CARDINAL; firstCharIndex: ovD.CharIndex _ mnp.lines.firstCharIndex; line, oldBottomLine, newTopLine: LinePtr; firstLineOffScreen: LinePtr = mnp.firstLineOffScreen; numberOfScreenLinesAbove _ MAX[(y - mnp.lines.y) / dsD.lineHeight, 1]; -- The following block of code handles the possibility that there may not be enough text above the screen to be brought on, by adjusting newTopLine. numberOfLinesAvailable _ 0; FOR i IN [1 .. numberOfScreenLinesAbove] DO IF firstCharIndex = 0 THEN EXIT; firstCharIndex _ SearchBackForFirstCharIndexOfLine[firstCharIndex - 1, mnp.message]; numberOfLinesAvailable _ numberOfLinesAvailable + 1; ENDLOOP; newTopLine _ NthLineFrom [mnp.lines, MIN[numberOfScreenLinesAbove, numberOfLinesAvailable]]; oldBottomLine _ mnp.lines; FOR line _ newTopLine, line.nextLine UNTIL line = firstLineOffScreen DO oldBottomLine _ oldBottomLine.nextLine ENDLOOP; firstLineOffScreen.firstCharIndex _ oldBottomLine.firstCharIndex; firstLineOffScreen.state _ oldBottomLine.state; [] _ MoveLines[top: mnp.lines, bottom: oldBottomLine, newTop: newTopLine, mnp: mnp]; FOR line _ mnp.lines, line.nextLine UNTIL line = newTopLine DO line.firstCharIndex _ firstCharIndex; line.state _ normalText; [firstCharIndex, , ] _ RompAndStomp[line, firstCharIndex, mnp]; ENDLOOP; END; -- of ScrollDownCM -- ThumbCM: PROCEDURE [tlnp: ThumbLineNbrPtr, x: ScreenXCoord] = -- Displays that portion of the CM that begins at the same relative position in the CM as -- x is in the thumb line. BEGIN xRange: CARDINAL _ tlnp.rightX - tlnp.leftX; messageLength, newFirstCharIndex: ovD.CharIndex; savedCursor: dsD.CursorShape; mnp: MessageTextNbrPtr = intC.cmTextNbr; IF ~mnp.haveMessage OR (messageLength _ vmD.GetMessageSize[mnp.message]) = 0 THEN RETURN; newFirstCharIndex _ Inline.LongDiv[Inline.LongMult[messageLength, x - tlnp.leftX], xRange]; [savedCursor, , ] _ dsD.GetCursor[]; dsD.ChangeCursor[hourGlass]; RefreshToPlaceCharOnLine[newFirstCharIndex, mnp.lines, mnp]; dsD.ChangeCursor[savedCursor]; END; -- of ThumbCM -- Underline, DeUnderline: PROCEDURE [start, end: ovD.CharIndex, underlineType: UnderlineType, pendingDelete: BOOLEAN, mnp: MessageTextNbrPtr] = -- Draws an underline indication on the screen for the visible section of the message in -- the range [start .. end). BEGIN startLine, endLine, line: LinePtr; startX, endX: ScreenXCoord; firstCharOffScreen: ovD.CharIndex _ mnp.firstLineOffScreen.firstCharIndex; firstCharOnScreen: ovD.CharIndex _ mnp.lines.firstCharIndex; message: vmD.VirtualMessagePtr _ mnp.message; IF (start >= firstCharOffScreen) OR (start >= end) THEN RETURN; IF start < firstCharOnScreen THEN {start _ firstCharOnScreen; startLine _ mnp.lines} ELSE startLine _ MapCharIndexToLine[start, mnp]; IF end <= firstCharOnScreen THEN RETURN; IF end > firstCharOffScreen THEN {end _ firstCharOffScreen; endLine _ mnp.firstLineOffScreen} ELSE endLine _ MapCharIndexToLine[end - 1, mnp]; -- underline within [startline .. endline] startX _ RangeRightXInLine[startLine.firstCharIndex, start, inD.leftMargin, message]; IF startLine = endLine THEN BEGIN endX _ RangeRightXInLine[start, end, startX, message]; DrawUnderlineInLine[startX, endX, startLine, underlineType, pendingDelete]; RETURN; END; -- underline right portion of first startLine. DrawUnderlineInLine[startX, startLine.rightX, startLine, underlineType, pendingDelete]; -- underline entire lines between startLine and endLine. FOR line _ startLine.nextLine, line.nextLine UNTIL line = endLine DO DrawUnderlineInLine[inD.leftMargin, line.rightX, line, underlineType, pendingDelete]; ENDLOOP; -- underline left portion of endLine. endX _ RangeRightXInLine[endLine.firstCharIndex, end, inD.leftMargin, message]; DrawUnderlineInLine[inD.leftMargin, endX, endLine, underlineType, pendingDelete]; END; -- of Underline, DeUnderline -- DrawUnderlineInLine: PROCEDURE [start, end: ScreenXCoord, line: LinePtr, underlineType: UnderlineType, pendingDelete: BOOLEAN] = -- XOR's an emphasis from start to end on line. If underlineType is pendingDelete, then -- inverts entire lineheight, otherwise, underlines on the last scan line for line. BEGIN grayShade: dsD.GrayShade; bottom: ScreenYCoord _ line.y + dsD.lineHeight; top: ScreenYCoord _ bottom - 1; SELECT underlineType FROM target => {grayShade _ black; IF pendingDelete THEN top _ line.y}; source => {grayShade _ dottedLine; IF pendingDelete THEN top _ top - 1}; ENDCASE; -- on underLineType; dsD.InvertRectangle[left: start, right: end, top: top, bottom: bottom, grayShade: grayShade]; IF pendingDelete AND underlineType = source THEN dsD.InvertRectangle[left: start, right: end, top: line.y, bottom: top]; END; -- of DrawUnderlineInLine -- UnderlineSelection, DeUnderlineSelection: PROCEDURE [selection: TextSelectionPtr, underline: UnderlineType] = -- Removes the underline indication on the screen for the visible section of the message -- in the range [start .. end). BEGIN Underline [selection.start, selection.end, underline, selection.pendingDelete, selection.mnp]; END; -- of UnderlineSelection, DeUnderlineSelection -- InitializeSelection: PROCEDURE[selection: TextSelectionPtr] = -- Initializes a selection to its default values. BEGIN selection^ _ TextSelection[NIL, 0, 0, 0, char, FALSE]; selection.mnp _ intC.cmTextNbr; END; -- of InitializeSelection -- ClearSourceSelection: PROCEDURE = -- DeUnderlines and reinitializes intC.source. BEGIN DeUnderlineSelection[@intC.source, source]; InitializeSelection[@intC.source]; END; -- of ClearSourceSelection -- MoveLines: PROCEDURE [top, bottom, newTop: LinePtr, mnp: MessageTextNbrPtr] RETURNS [newBottom: LinePtr] = -- Moves information on screen and updates line structures appropriately. BEGIN -- Move lines from [top .. bottom) to [newTop .. newBottom). Clear screen for each target line before moving. line: LinePtr; newBottom _ newTop; FOR line _ top, line.nextLine UNTIL line = bottom DO newBottom _ newBottom.nextLine; ENDLOOP; dsD.SlideFullWidthRectangleVertically[top.y, bottom.y, newTop.y]; IF top.y >= newTop.y THEN MoveLineDescriptors[top, bottom, newTop] ELSE BEGIN -- lines are moving down, reverse chain destructively, move line descriptors between -- proper endpoints, and reverse chain destructively to restore chain. DestructiveReverse[mnp.lines]; MoveLineDescriptors [top: bottom.nextLine, bottom: top.nextLine, newTop: newBottom.nextLine]; DestructiveReverse[mnp.firstLineOffScreen]; END; END; -- of MoveLines -- MoveLineDescriptors: PROCEDURE [top, bottom, newTop: LinePtr] = -- Updates line structures so that information previously contained in [top .. bottom) is -- moved to [newTop .. ). newTop is guaranteed higher in line chain than -- top upon entry. BEGIN line, newLine, saveNextLine: LinePtr; saveY: ScreenYCoord; newLine _ newTop; FOR line _ top, line.nextLine UNTIL line = bottom DO saveY _ newLine.y; saveNextLine _ newLine.nextLine; newLine^ _ line^; newLine.y _ saveY; newLine _ newLine.nextLine _ saveNextLine; ENDLOOP; END; -- of MoveLineDescriptors -- SearchBackForFirstCharIndexOfLine: PROCEDURE [startIndex: ovD.CharIndex, message: vmD.VirtualMessagePtr] RETURNS [ovD.CharIndex] = -- Returns the largest charIndex less than or equal to index that imediately follows a CR -- or PCR. BEGIN i: ovD.CharIndex; char: CHARACTER; get: POINTER TO vmD.CharCache _ @message.get; FOR i DECREASING IN [0 .. startIndex) DO char _ IF i IN [get.first .. get.free) THEN get.string[i + get.floor - get.first] ELSE vmD.GetMessageChar[message, i]; IF (char = Ascii.CR) OR (char >= ovD.LineBreakValue) THEN RETURN[i + 1]; ENDLOOP; RETURN[0]; END; -- of SearchBackForFirstCharIndexOfLine -- RompAndStomp: PROCEDURE [line: LinePtr, firstChar: ovD.CharIndex, mnp: MessageTextNbrPtr, underline: BOOLEAN _ TRUE] RETURNS [firstUnusedCharIndex: ovD.CharIndex, curRightX: ScreenXCoord, curY: ScreenYCoord] = -- Previously called DisplayOnePreviouslyFormattedLine. Selections will be refreshed with -- their underlines iff underline is TRUE. BEGIN message: vmD.VirtualMessagePtr _ mnp.message; messageLength: ovD.CharIndex _ vmD.GetMessageSize[message]; PaintUnderline: PROCEDURE [selection: POINTER TO TextSelection, underlineType: UnderlineType] = -- Paints underlines for RompAndStomp. BEGIN iStart, iEnd: ovD.CharIndex; startX, endX: ScreenXCoord; IF selection.mnp = mnp THEN BEGIN iStart _ IF selection.start > firstChar THEN selection.start ELSE firstChar; iEnd _ IF selection.end < firstUnusedCharIndex THEN selection.end ELSE firstUnusedCharIndex; IF iStart >= iEnd THEN iStart _ iEnd _ 0; IF (iStart = firstChar) AND (iEnd = firstUnusedCharIndex) THEN DrawUnderlineInLine [leftMargin, curRightX, line, underlineType, selection.pendingDelete] ELSE IF iStart # iEnd THEN BEGIN startX _ RangeRightXInLine[firstChar, iStart, inD.leftMargin, message]; endX _ RangeRightXInLine[iStart, iEnd, startX, message]; DrawUnderlineInLine[startX, endX, line, underlineType, selection.pendingDelete]; END; END; END; -- of PaintUnderline -- curY _ line.y; line.firstCharIndex _ firstChar; line.state _ normalText; [curRightX, firstUnusedCharIndex] _ CharacterBlaster [firstChar, messageLength, message, inD.leftMargin, curY]; line.rightX _ curRightX; IF underline THEN {PaintUnderline[@intC.target, target]; PaintUnderline[@intC.source, source]}; END; -- of RompAndStomp -- FormatCM: PROCEDURE [cm: vmD.ComposeMessagePtr] = -- Formats all of the composed message from the high water mark to the end, -- expanding the message to break up long lines if necessary. highWaterMark is -- set to the end of the message. BEGIN index, messageLength: ovD.CharIndex; index _ highWaterMark; messageLength _ vmD.GetMessageSize[cm]; UNTIL index >= messageLength DO [index, , ] _ FormatLine[index, cm]; ENDLOOP; highWaterMark _ messageLength; END; -- of FormatCM -- FormatAndDisplaySuffix: PROCEDURE [mnp: MessageTextNbrPtr, firstChar, firstCharOfLine: ovD.CharIndex, firstX: ScreenXCoord, line: LinePtr] RETURNS [firstUnusedCharIndex: ovD.CharIndex, curX: ScreenXCoord] = -- Formats (fixes PCR's) of the suffix of one line BEGIN IF firstChar = vmD.GetMessageSize[mnp.message] THEN {firstUnusedCharIndex _ firstChar; curX _ firstX} ELSE BEGIN [firstUnusedCharIndex, curX, ] _ FormatLine[firstCharOfLine, LOOPHOLE[mnp.message]]; IF curX < firstX THEN dsD.ClearRectangle[curX, firstX, line.y, line.nextLine.y] ELSE [ , ] _ CharacterBlaster[firstChar, firstUnusedCharIndex, mnp.message, firstX, line.y]; END; END; -- of FormatAndDisplaySuffix -- FormatLine: PROCEDURE [firstCharOfLine: ovD.CharIndex, message: vmD.ComposeMessagePtr] RETURNS [breakIndex: ovD.CharIndex, breakX: ScreenXCoord, wasOk: BOOLEAN] = -- Formats the suffix of a line by finding how much will fit on the remainder of the line -- and changing the last blank to a PCR. Returns wasOk = TRUE iff the last blank that -- got turned into a PCR actually was a PCR before FormatSuffix was called. BEGIN firstFreeIndex, index: ovD.CharIndex; lastPCR: ovD.CharIndex _ 0; rightX: ScreenXCoord _ inD.leftMargin; i: CARDINAL _ 0; char, prevChar, pcrChar: CHARACTER _ 0C; get: POINTER TO vmD.CharCache _ @message.get; prevCharBreakProp, charBreakProp, lineBreakProp: dsD.CharProperty _ alphaNumeric; InlineGetCharRightX: PROC [char: CHARACTER, leftX: ScreenXCoord] RETURNS [rightX: ScreenXCoord] = INLINE -- Given a char positioned at leftX, returns its rightX. Char is -- always masked to 7 bits. Tab widths may vary depending on -- leftX. BEGIN offsetPtr: POINTER; tabWidth: CARDINAL = 40; IF char = Ascii.TAB THEN RETURN[(((leftX + 5 - dsD.xOrigin) / tabWidth) + 1) * tabWidth + dsD.xOrigin]; offsetPtr _ disC.mcFont + LOOPHOLE[char, CARDINAL]; RETURN[leftX + LOOPHOLE[offsetPtr + offsetPtr^, dsD.CharFontPtr].width]; END; -- of InlineGetCharRightX -- firstFreeIndex _ vmD.GetMessageSize[message]; FOR index _ firstCharOfLine, index + 1 UNTIL index >= firstFreeIndex DO char _ IF index IN [get.first .. get.free) THEN get.string[index + get.floor - get.first] ELSE vmD.GetMessageChar[message, index]; IF char >= ovD.LineBreakValue THEN BEGIN vmD.PutMessageChar[message, index, (char _ Inline.BITAND[char, ovD.CharMask])]; lastPCR _ index; END; IF char = Ascii.CR THEN RETURN[index + 1, rightX + inD.CRWidth, TRUE]; charBreakProp _ disC.charPropertyTable[char]; IF lineBreakProp = white THEN {IF prevCharBreakProp = white AND charBreakProp # white THEN {breakIndex _ index; breakX _ rightX; pcrChar _ prevChar}} ELSE BEGIN IF lineBreakProp = alphaNumeric OR prevCharBreakProp # alphaNumeric THEN BEGIN breakIndex _ index; breakX _ rightX; pcrChar _ prevChar; IF prevCharBreakProp # white OR charBreakProp # white THEN lineBreakProp _ prevCharBreakProp; END; END; IF (rightX _ InlineGetCharRightX[char, rightX]) > inD.rightMargin - inD.CRWidth THEN BEGIN IF rightX > inD.rightMargin OR charBreakProp # white OR (index + 1 < firstFreeIndex AND dsD.GetCharBreakProp[vmD.GetMessageChar[message, index + 1]] = white) THEN BEGIN vmD.PutMessageChar [message, breakIndex - 1, Inline.BITOR[pcrChar, ovD.LineBreakValue]]; RETURN[breakIndex, breakX, ((lastPCR + 1) = breakIndex)]; END ELSE BEGIN vmD.PutMessageChar[message, index, Inline.BITOR[char, ovD.LineBreakValue]]; RETURN[index + 1, rightX, (lastPCR = index)]; END; END; prevCharBreakProp _ charBreakProp; prevChar _ char; ENDLOOP; RETURN[index, rightX, TRUE]; END; -- of FormatLine -- FindLineBeforePlace: PROCEDURE [startLine, place: LinePtr] RETURNS [line: LinePtr] = -- Returns the line preceeding place in the linked line list. Search begins at startLine. Returns NIL if startLine = place or if startLine occurs after place in list. BEGIN FOR line _ startLine, line.nextLine UNTIL (line = NIL) OR (line.nextLine = place) DO ENDLOOP; END; -- of FindLineBeforePlace -- MapCharIndexToLine: PROCEDURE [index: ovD.CharIndex, mnp: MessageTextNbrPtr] RETURNS [LinePtr] = -- Returns the screen line that contains index. Returns NIL if index is not currently -- displayed on any visible screen line. BEGIN -- Assume that lines are maintained so that the firstCharIndex = MessageSize for any line -- after the end of the message. Also assume that a trailing line (firstLineOffScreen) is -- maintained. line, nextLine: LinePtr; char: CHARACTER; prevIndexIsLineBreak: BOOLEAN _ (index = 0 OR (char _ vmD.GetMessageChar[mnp.message, index - 1]) = Ascii.CR OR char >= ovD.LineBreakValue); IF index ~ IN [mnp.lines.firstCharIndex .. mnp.firstLineOffScreen.firstCharIndex] THEN RETURN[NIL]; FOR line _ mnp.lines, nextLine UNTIL line = mnp.firstLineOffScreen DO nextLine _ line.nextLine; IF nextLine.firstCharIndex > index OR (line.state = endOfMessage AND prevIndexIsLineBreak) OR (nextLine.state = endOfMessage AND ~prevIndexIsLineBreak) THEN RETURN[line]; ENDLOOP; RETURN[NIL]; END; -- of MapCharIndexToLine -- RangeRightXInLine: PROCEDURE [start, to: ovD.CharIndex, startX: ScreenXCoord, message: vmD.VirtualMessagePtr] RETURNS [rightX: ScreenXCoord] = -- Returns the sum of the widths in screen raster points of the characters from the -- interval [start .. to), starting at X Coordinate startX in messsage. BEGIN i: ovD.CharIndex; get: POINTER TO vmD.CharCache _ @message.get; rightX _ startX; FOR i IN [start .. to) DO rightX _ dsD.GetCharRightX [IF i IN [get.first .. get.free) THEN get.string[i + get.floor - get.first] ELSE vmD.GetMessageChar[message, i], rightX] ENDLOOP END; -- of RangeRightXInLine -- MapCharIndexInLineToLeftX: PROCEDURE [index: ovD.CharIndex, line: LinePtr, message: vmD.VirtualMessagePtr] RETURNS [x: ScreenXCoord] = -- index is in line. -- Returns the ScreenXCoord of the left edge of the character corresponding to index in -- message. BEGIN RETURN[RangeRightXInLine [start: line.firstCharIndex, to: index, startX: inD.leftMargin, message: message]]; END; -- of MapCharIndexInLineToLeftX -- AdjustFirstCharIndices: PROCEDURE [firstLine: LinePtr, increment: INTEGER] = -- Increments the firstCharIndex of all screen lines from firstLine on. BEGIN line: LinePtr; FOR line _ firstLine, line.nextLine UNTIL line = NIL DO line.firstCharIndex _ line.firstCharIndex + increment; ENDLOOP; END; -- of AdjustFirstCharIndices -- MapCursorToCharIndex: PROCEDURE [x: ScreenXCoord, y: ScreenYCoord, mnp: MessageTextNbrPtr] RETURNS [index: ovD.CharIndex] = -- Map x,y to a char index in message. If x,y is to the right of the rightmost char on its -- line, treat as rightmost character. Similarly, if y is too far down on screen, treat as if -- y was on the last screen line that holds normal message text. BEGIN line: LinePtr; messageLength: ovD.CharIndex = vmD.GetMessageSize[mnp.message]; FOR line _ mnp.lines, line.nextLine UNTIL (line.nextLine.firstCharIndex = messageLength) OR (line.nextLine.y > y) DO ENDLOOP; [index, , ] _ MapXInLineToCharIndex[x, line, mnp.message] END; -- of MapCursorToCharIndex -- NextUnit: PROCEDURE [message: vmD.VirtualMessagePtr, startIndex: ovD.CharIndex, startX: ScreenXCoord] RETURNS[rightX: CARDINAL, endIndex: ovD.CharIndex] = -- Computes rightX of run of characters plus run of following white space [startIndex .. -- endIndex) in message if placed starting at startX. BEGIN state: {black, white} _ black; char: CHARACTER; rightX _ startX; FOR endIndex IN [startIndex .. vmD.GetMessageSize[message]) DO char _ vmD.GetMessageChar[message, endIndex]; IF char = Ascii.CR THEN RETURN[rightX + inD.CRWidth, endIndex + 1]; IF dsD.GetCharProperty[char, white] THEN state _ white ELSE IF state = white THEN RETURN; rightX _ dsD.GetCharRightX[char, rightX]; ENDLOOP; endIndex _ vmD.GetMessageSize[message]; END; -- of NextUnit -- NthLineFrom: PROCEDURE [line: LinePtr, n: CARDINAL] RETURNS [nthLine: LinePtr] = -- Returns the nth line from line in the line chain, or NIL if fewer than n lines remain. BEGIN nthLine _ line; THROUGH [1 .. n] UNTIL nthLine = NIL DO nthLine _ nthLine.nextLine; ENDLOOP; END; -- of NthLineFrom -- ClearLine: PROCEDURE [line: LinePtr] = {dsD.ClearRectangle[inD.leftMargin, line.rightX, line.y, line.nextLine.y]}; SetHighWaterMark: PROCEDURE [h: ovD.CharIndex] = -- Sets highWaterMark. BEGIN highWaterMark _ h; END; -- of SetHighWaterMark -- DestructiveReverse: PROCEDURE [line: LinePtr] = -- Destructively reverse all pointers in line chain starting with line. BEGIN next, prior: LinePtr; prior _ NIL; UNTIL line = NIL DO next _ line.nextLine; line.nextLine _ prior; prior _ line; line _ next; ENDLOOP; END; -- of DestructiveReverse -- Even: PROCEDURE [nWords: CARDINAL] RETURNS [POINTER] = -- allocates n words starting at an even address. BEGIN p: POINTER _ Storage.Node[nWords+1]; RETURN[p + Inline.BITAND[p, 1]]; END; -- of Even -- --Initialization of the BITBLT Table for CharacterBlaster charBbtPtr _ Even[SIZE[dsD.Bbt]]; charBbtPtr.pad _ 0; charBbtPtr.sourcealt _ FALSE; charBbtPtr.destalt _ FALSE; charBbtPtr.func _ dsD.replace + dsD.source; charBbtPtr.unused _ 0; charBbtPtr.slx _ 0; charBbtPtr.sty _ 0; charBbtPtr.dbca _ disC.bitMapPtr; charBbtPtr.dbmr _ dsD.bmWidth; charBbtPtr.sbmr _ 1; END. -- of EditorDisplay -- z19932(635)\f1