-- file: IntDM.mesa
-- edited by Brotz, March 27, 1981 4:14 PM
-- edited by Levin, January 16, 1980 10:14 AM

DIRECTORY
Ascii,
displayCommon: FROM "DisplayCommon",
dsD: FROM "DisplayDefs",
Editor,
inD: FROM "InteractorDefs",
InlineDefs,
intCommon: FROM "IntCommon",
ovD: FROM "OverviewDefs",
vmD: FROM "VirtualMgrDefs";

IntDM: PROGRAM
IMPORTS disC: displayCommon, dsD, Editor, InlineDefs, intC: intCommon, vmD
EXPORTS inD
SHARES inD, vmD = PUBLIC

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.

-- Global variables --

lineBuffer: STRING ← [160]; -- used by FillLineBuffer to avoid slow frame allocation.
-- 158 is length of widest string (all ’|’) that fits without folding.


DisplayDMTail: PROCEDURE [mnp: MessageTextNbrPtr, line: LinePtr,
startIndex: ovD.CharIndex] =
-- Fills out as much of the displayed message as possible starting in line with startIndex.
BEGIN
nextIndex, firstFreeIndex: ovD.CharIndex;
IF ~mnp.haveMessage THEN RETURN;
firstFreeIndex ← vmD.GetMessageSize[mnp.message];
UNTIL (line = mnp.firstLineOffScreen) OR (startIndex >= firstFreeIndex) DO
nextIndex ← PutLineInBitMap[mnp.message, startIndex, line];
line.firstCharIndex ← startIndex;
line.state ← normalText;
line ← line.nextLine;
startIndex ← nextIndex;
ENDLOOP;
IF line = mnp.firstLineOffScreen THEN
BEGIN
line.firstCharIndex ← startIndex;
line.state ← IF startIndex >= firstFreeIndex THEN endOfMessage ELSE normalText;
RETURN;
END;

[] ← dsD.PutStringInBitMap[leftMargin, line.y, mnp.endString, italicFace];
line.state ← endOfMessage;
line.firstCharIndex ← firstFreeIndex;
FOR line ← line.nextLine, line.nextLine UNTIL line = NIL DO
line.state ← trailingBlankLine;
line.firstCharIndex ← firstFreeIndex;
ENDLOOP;
END; -- of DisplayDMTail --


FillLineBuffer: PROCEDURE [vs: vmD.VirtualMessagePtr,
startIndex: ovD.CharIndex] RETURNS [ovD.CharIndex] =
-- Fills lineBuffer with a line of characters, starting by placing at the left margin
-- the character at the startIndex position, and terminating the line at a line separator.
-- Returns index of first character not put into line.
BEGIN
firstFreeIndex, curIndex, breakIndex: ovD.CharIndex;
rightX: ScreenXCoord ← inD.leftMargin;
i: CARDINAL ← 0;
char: CHARACTER ← 0C;
get: POINTER TO vmD.CharCache ← @vs.get;
prevCharBreakProp, charBreakProp, lineBreakProp: dsD.CharProperty ← alphaNumeric;

firstFreeIndex ← vmD.GetMessageSize[vs];
FOR curIndex ← startIndex, curIndex + 1
UNTIL (curIndex >= firstFreeIndex) OR (char = Ascii.CR) DO
lineBuffer[i] ← char ← InlineDefs.BITAND
[IF curIndex IN [get.first .. get.free) THEN get.string[curIndex + get.floor - get.first]
ELSE vmD.GetMessageChar[vs, curIndex],
ovD.CharMask];
i ← i + 1;
charBreakProp ← dsD.GetCharBreakProp[char];
IF lineBreakProp = white THEN
{IF prevCharBreakProp = white AND charBreakProp # white THEN
breakIndex ← curIndex}
ELSE BEGIN
IF lineBreakProp = alphaNumeric OR prevCharBreakProp # alphaNumeric THEN
BEGIN
breakIndex ← curIndex;
IF prevCharBreakProp # white OR charBreakProp # white THEN
lineBreakProp ← prevCharBreakProp;
END;
END;
prevCharBreakProp ← charBreakProp;
IF (rightX ← dsD.GetCharRightX[char, rightX]) > inD.rightMargin THEN
{curIndex ← breakIndex; EXIT}
ENDLOOP;
lineBuffer.length ← curIndex - startIndex;
RETURN[curIndex];
END; -- of FillLineBuffer --


PutLineInBitMap: PROCEDURE [vs: vmD.VirtualMessagePtr, startIndex: ovD.CharIndex,
line: LinePtr] RETURNS [ovD.CharIndex] =
-- Fills the bitmap with a line of characters, starting by placing at inD.leftMargin, line.y
-- the character at the startIndex position, and terminating the line at a line separator.
-- Returns index of first character not put into line.
BEGIN
curIndex: ovD.CharIndex ← FillLineBuffer[vs, startIndex];
line.rightX ← dsD.PutStringInBitMap[inD.leftMargin, line.y, lineBuffer, plainFace];
RETURN[curIndex];
END; -- of PutLineInBitMap --


ScrollUpDM: PROCEDURE [y: ScreenYCoord, mnp: MessageTextNbrPtr] =
-- Scrolls message line containing y or End of Message line to top of neighborhood.
BEGIN
lineY: ScreenYCoord;
line, newTopLine, newLastLine, oldLastLine: LinePtr;
FOR newTopLine ← mnp.lines, newTopLine.nextLine UNTIL
(newTopLine.state = endOfMessage)
OR (y < newTopLine.y + dsD.lineHeight)
OR (newTopLine.nextLine = mnp.firstLineOffScreen)
DO ENDLOOP;
IF newTopLine = mnp.lines THEN
IF newTopLine.state = endOfMessage THEN RETURN
ELSE newTopLine ← newTopLine.nextLine;
IF intC.source.mnp = mnp THEN Editor.DeUnderlineSelection[@intC.source, source];
dsD.SlideFullWidthRectangleVertically[newTopLine.y, mnp.bottomY, mnp.topY];
FOR newLastLine ← mnp.lines, newLastLine.nextLine
UNTIL newLastLine.nextLine = newTopLine
DO ENDLOOP;
oldLastLine ← mnp.firstLineOffScreen;
mnp.firstLineOffScreen.nextLine ← mnp.lines;
mnp.lines ← newTopLine;
newLastLine.nextLine ← NIL;
mnp.firstLineOffScreen ← newLastLine;
lineY ← mnp.topY;
FOR line ← newTopLine, line.nextLine UNTIL line = NIL DO
line.y ← lineY;
lineY ← lineY + dsD.lineHeight;
ENDLOOP;
IF oldLastLine.state # trailingBlankLine THEN
DisplayDMTail[mnp, oldLastLine, oldLastLine.firstCharIndex]
ELSE
FOR line ← oldLastLine.nextLine, line.nextLine UNTIL line = NIL DO
line.state ← trailingBlankLine;
line.firstCharIndex ← oldLastLine.firstCharIndex;
ENDLOOP;
IF intC.source.mnp = mnp THEN Editor.UnderlineSelection[@intC.source, source];
END; -- of ScrollUpDM --


ScrollDownDM: PROCEDURE [y: ScreenYCoord, mnp: MessageTextNbrPtr] =
-- Scrolls top message line of neighborhood down to line containing y. Will not scroll first
-- line of message below top line however.
BEGIN
shiftNum, realShiftNum: CARDINAL;
startCharIndex, charIndex: ovD.CharIndex;
char: CHARACTER;
lineY: ScreenYCoord;
line, newTopLine, newLastLine, oldLastLine: LinePtr;

shiftNum ← MAX[(y - mnp.topY) / dsD.lineHeight, 1];
IF (charIndex ← mnp.lines.firstCharIndex) = 0 THEN RETURN;

IF intC.source.mnp = mnp THEN Editor.DeUnderlineSelection[@intC.source, source];

FOR realShiftNum ← 0, realShiftNum + 1
UNTIL (charIndex = 0) OR (realShiftNum = shiftNum)
DO
charIndex ← charIndex - 1;
char ← 0C;
UNTIL (char = Ascii.CR) OR (charIndex = 0) DO
charIndex ← charIndex - 1;
char ← vmD.GetMessageChar[mnp.message, charIndex];
ENDLOOP;
IF charIndex # 0 THEN charIndex ← charIndex + 1;
ENDLOOP;
startCharIndex ← charIndex;
THROUGH [1 .. realShiftNum] DO
charIndex ← FillLineBuffer[mnp.message, charIndex];
ENDLOOP;
UNTIL realShiftNum = shiftNum OR charIndex = mnp.lines.firstCharIndex DO
charIndex ← FillLineBuffer[mnp.message, charIndex];
realShiftNum ← realShiftNum + 1;
ENDLOOP;
UNTIL charIndex = mnp.lines.firstCharIndex DO
charIndex ← FillLineBuffer[mnp.message, charIndex];
startCharIndex ← FillLineBuffer[mnp.message, startCharIndex];
ENDLOOP;

dsD.SlideFullWidthRectangleVertically
[mnp.topY, mnp.bottomY - realShiftNum * dsD.lineHeight,
mnp.topY + realShiftNum * dsD.lineHeight];
newLastLine ← mnp.lines;
THROUGH [0 .. (mnp.bottomY - mnp.topY) / dsD.lineHeight - realShiftNum) DO
newLastLine ← newLastLine.nextLine;
ENDLOOP;
newTopLine ← newLastLine.nextLine;
oldLastLine ← mnp.firstLineOffScreen;
newLastLine.nextLine ← NIL;
mnp.firstLineOffScreen ← newLastLine;
oldLastLine.nextLine ← mnp.lines;
mnp.lines ← newTopLine;
lineY ← mnp.topY;
FOR line ← newTopLine, line.nextLine UNTIL line = NIL DO
line.y ← lineY;
lineY ← lineY + dsD.lineHeight;
ENDLOOP;
line ← mnp.lines;
THROUGH [1 .. realShiftNum] DO
line.firstCharIndex ← startCharIndex;
line.state ← normalText;
startCharIndex ← PutLineInBitMap[mnp.message, startCharIndex, line];
line ← line.nextLine;
ENDLOOP;
IF intC.source.mnp = mnp THEN Editor.UnderlineSelection[@intC.source, source];
END; -- of ScrollDownDM --


ThumbDM: PROCEDURE [tlnp: ThumbLineNbrPtr, x: ScreenXCoord] =
-- Displays the portion of the DM that begins at the same relative position in the DM as x is
-- in the thumb line.
BEGIN
xRange: CARDINAL ← tlnp.rightX - tlnp.leftX;
messageLength, indexOfCharOnFirstLine, newFirstCharIndex: ovD.CharIndex;
savedCursor: dsD.CursorShape;
mnp: MessageTextNbrPtr = intC.dmTextNbr;
message: vmD.VirtualMessagePtr = mnp.message;

IF ~mnp.haveMessage THEN RETURN;
messageLength ← vmD.GetMessageSize[message];
IF messageLength = 0 THEN RETURN;
[savedCursor, , ] ← dsD.GetCursor[];
dsD.ChangeCursor[hourGlass];
indexOfCharOnFirstLine ←
InlineDefs.LongDiv[InlineDefs.LongMult[messageLength, x - tlnp.leftX], xRange];
IF indexOfCharOnFirstLine = messageLength THEN
indexOfCharOnFirstLine ← indexOfCharOnFirstLine - 1;
newFirstCharIndex ← indexOfCharOnFirstLine;
UNTIL newFirstCharIndex = 0 DO
IF vmD.GetMessageChar[message, newFirstCharIndex] = Ascii.CR THEN GOTO FoundCR;
newFirstCharIndex ← newFirstCharIndex - 1;
REPEAT
FoundCR => newFirstCharIndex ← newFirstCharIndex + 1;
ENDLOOP;
DO
nextIndex: ovD.CharIndex ← FillLineBuffer[message, newFirstCharIndex];
IF nextIndex > indexOfCharOnFirstLine THEN EXIT;
newFirstCharIndex ← nextIndex;
ENDLOOP;
IF intC.source.mnp = mnp THEN Editor.DeUnderlineSelection[@intC.source, source];
dsD.ClearRectangle[leftMargin, rightMargin, mnp.topY, mnp.bottomY];
DisplayDMTail[mnp, mnp.lines, newFirstCharIndex];
IF intC.source.mnp = mnp THEN Editor.UnderlineSelection[@intC.source, source];
dsD.ChangeCursor[savedCursor];
END; -- of ThumbDM --


END. -- of IntDM --