-- file: IntDisplayCom.Mesa
-- last edited by Horning, March 2, 1978 10:58 PM
-- last edited by Kierr, March 8, 1978 4:51 PM
-- last edited by Brotz, November 13, 1981 12:24 PM
-- last edited by Schroeder, Wednesday Nov. 5, 1980 3:14 pm PST.
-- last edited by Levin, January 8, 1981 10:19 AM.
-- last edited by Taft, May 21, 1983 1:50 PM

DIRECTORY
Core USING [Close, Delete, Open],
dsD: FROM "DisplayDefs" USING [ClearRectangle, lineHeight, PutCharInBitMap,
ScreenYCoord],
Editor USING [RefreshSoThatFirstCharStartsLine],
exD: FROM "ExceptionDefs" USING [cantMoveToFileInUse, diskErrorTargetDamage,
DisplayException, Exception, moveWouldOverfill, newFile, nil, noCurrentFile,
noMessagesMoved, noMoveIllegalName, noSelectedEntries, noUndeletedMessages],
inD: FROM "InteractorDefs" USING [CommandProcedure, Confirm, ConfirmBrackets,
Consider, DisplayTOCEntry, leftMargin, LinePtr, MakeTOCIndexVisible,
MapTOCIndexToTOCLine, MapTOCIndexToTopY, markLeftX, MessageTextNbrPtr,
NextTOCEntry, numberLeftX, rightMargin, ScreenYCoord, ScrollDownTOC, ScrollUpTOC,
TOCLineNumber, TOCTextNbrPtr, UpdateTOCThumbLine],
intCommon USING [displayAfterDelete, dmTextNbr, moveToBracketsHouse, tocTextNbr],
opD: FROM "OperationsDefs" USING [AppendMailToFileOperation, NoMessagesMoved],
String USING [AppendString],
tsD: FROM "TOCSelectionDefs" USING [DeconsiderAll, FirstSelectedEntry, IsSelected,
LastSelectedEntry, NextSelectedEntry, SetTOCSelection, TOCSelectionEmpty],
vmD: FROM "VirtualMgrDefs" USING [DisplayMessage, DisplayMessagePtr,
FirstFreeTOCIndex, FlushDisplayMessage, GetTOCFixedPart, LoadDisplayMessage,
PutTOCFixedPart, TOCFixedPart, TOCHandle, TOCIndex, UnlockTOC, WaitForLock],
VMDefs USING [CantOpen, FileHandle];

IntDisplayCom: PROGRAM
IMPORTS Core, dsD, Editor, exD, inD, intC: intCommon, opD, String, tsD, vmD, VMDefs
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.


DeleteCommand: PUBLIC CommandProcedure =
-- Sets deleted field for each selected TOCEntry. Updates the display to reflect the changes.
BEGIN
tnp: TOCTextNbrPtr = intC.tocTextNbr;
toc: vmD.TOCHandle ← tnp.toc;
key: CARDINAL ← vmD.WaitForLock[toc];
ModifyTOCOperation[tnp, key, delete];
DoDisplayAfterDelete[tnp, key];
vmD.UnlockTOC[toc, key];
END; -- of DeleteCommand --


DoDisplayAfterDelete: PRIVATE PROC [tnp: TOCTextNbrPtr, key: CARDINAL] =
BEGIN
toc: vmD.TOCHandle = tnp.toc;
index: vmD.TOCIndex ← vmD.DisplayMessage[intC.dmTextNbr.message].index;
IF intC.displayAfterDelete AND tnp.haveToc AND ~tsD.TOCSelectionEmpty[toc, key]
AND index = tsD.FirstSelectedEntry[toc, key] AND index = tsD.LastSelectedEntry[toc, key]
AND index + 1 < vmD.FirstFreeTOCIndex[toc, key] THEN
BEGIN
fp: vmD.TOCFixedPart;
vmD.GetTOCFixedPart[toc, key, index + 1, @fp];
IF ~fp.seen THEN
BEGIN
exception: exD.Exception ← DisplayMessageOperation[tnp, key];
SELECT exception FROM
exD.nil, exD.noUndeletedMessages => NULL;
ENDCASE => exD.DisplayException[exception];
END;
END;
END; -- of DoDisplayAfterDelete --


ModifyTOCOperation: PRIVATE PROCEDURE
[tnp: TOCTextNbrPtr, key: CARDINAL, action: {delete, undelete, move}] =
-- Sets fields for each selected TOCEntry. Updates the display to reflect the changes.
BEGIN
toc: vmD.TOCHandle = tnp.toc;
line: LinePtr ← tnp.lines;
topLineNumber: TOCLineNumber ← line.linePair.lineNumber;
index: vmD.TOCIndex;
fp: vmD.TOCFixedPart;

IF ~tnp.haveToc THEN {exD.DisplayException[exD.noCurrentFile]; RETURN};
IF tsD.TOCSelectionEmpty[toc, key] THEN
{exD.DisplayException[exD.noSelectedEntries]; RETURN};

FOR index ← tsD.FirstSelectedEntry[toc, key], tsD.NextSelectedEntry[toc, key, index]
UNTIL index = 0 DO
vmD.GetTOCFixedPart[toc, key, index, @fp];
IF action = move AND ~ fp.deleted THEN fp.mark ← ’m;
fp.deleted ← (action = delete OR action = move);
vmD.PutTOCFixedPart[toc, key, index, @fp];
ENDLOOP;
index ← line.linePair.index;
IF tsD.IsSelected[toc, key, index] THEN
-- TOC entry at top of screen may be only partially visible
[line, ] ← DisplayTOCEntry
[tnp: tnp, key: key, index: index, suppressed: topLineNumber - 1, firstLine: line];
UNTIL (index ← tsD.NextSelectedEntry[toc, key, index]) = 0
OR (line ← MapTOCIndexToTOCLine[index, tnp]) = NIL DO
[ , ] ← DisplayTOCEntry[tnp, key, index, 0, line];
ENDLOOP;
END; -- of ModifyTOCOperation --


UndeleteCommand: PUBLIC CommandProcedure =
-- Clears deleted field for each selected TOCEntry. Updates the display to reflect the
-- changes.
BEGIN
tnp: TOCTextNbrPtr = intC.tocTextNbr;
toc: vmD.TOCHandle ← tnp.toc;
key: CARDINAL ← vmD.WaitForLock[toc];
ModifyTOCOperation[tnp, key, undelete];
vmD.UnlockTOC[toc, key];
END; -- of UndeleteCommand --


DisplayMessageCommand: PUBLIC CommandProcedure =
-- Clears DM text neighborhood. Sets TOC selection to the current displayed message if it
-- was already selected, otherwise the first selected TOCEntry. If the current display
-- message index equals is this selected message, then displays and selects the next
-- undeleted message, otherwise displays the selected message. Virtualizes the selected
-- message and refreshes the DM text Nbr.
BEGIN
tnp: TOCTextNbrPtr = intC.tocTextNbr;
toc: vmD.TOCHandle ← tnp.toc;
key: CARDINAL ← vmD.WaitForLock[toc];
exception: exD.Exception ← DisplayMessageOperation[tnp, key];
vmD.UnlockTOC[toc, key];
IF exception # exD.nil THEN exD.DisplayException[exception];
END; -- of DisplayMessageCommand --


DisplayMessageOperation: PROCEDURE [tnp: TOCTextNbrPtr, key: CARDINAL]
RETURNS [exception: exD.Exception] =
-- Clears DM text neighborhood. Sets TOC selection to the current displayed message if it
-- was already selected, otherwise the first selected TOCEntry. If the current display
-- message index equals is this selected message, then displays and selects the next
-- undeleted message, otherwise displays the selected message. Virtualizes the selected
-- message and refreshes the DM text Nbr.
BEGIN
fp: vmD.TOCFixedPart;
selectedIndex: vmD.TOCIndex;
dm: MessageTextNbrPtr = intC.dmTextNbr;
toc: vmD.TOCHandle = tnp.toc;
message: vmD.DisplayMessagePtr = vmD.DisplayMessage[dm.message];
exception ← exD.nil;

IF ~tnp.haveToc THEN RETURN[exD.noCurrentFile];
IF tsD.TOCSelectionEmpty[toc, key] THEN RETURN[exD.noSelectedEntries];
selectedIndex ← message.index;
BEGIN -- for EXITS --
SELECT TRUE FROM
(~dm.haveMessage OR message.toc # toc OR ~tsD.IsSelected[toc, key, selectedIndex]) =>
BEGIN
selectedIndex ← tsD.FirstSelectedEntry[toc, key];
MakeTOCIndexVisible[selectedIndex, key];
END;
(tsD.FirstSelectedEntry[toc, key] # tsD.LastSelectedEntry[toc, key]) =>
BEGIN -- currently viewing one of a series of selected entries.
originalSelectedIndex: vmD.TOCIndex ← selectedIndex;
DO
selectedIndex ← IF selectedIndex = tsD.LastSelectedEntry[toc, key]
THEN tsD.FirstSelectedEntry[toc, key]
ELSE tsD.NextSelectedEntry[toc, key, selectedIndex];
IF selectedIndex = originalSelectedIndex THEN
BEGIN
selectedIndex ← tsD.FirstSelectedEntry[toc, key];
GO TO SelectNextUndeletedEntry;
END;
vmD.GetTOCFixedPart[toc, key, selectedIndex, @fp];
IF ~fp.deleted THEN EXIT;
ENDLOOP;
MakeTOCIndexVisible[selectedIndex, key];
END
ENDCASE => GO TO SelectNextUndeletedEntry;
EXITS
SelectNextUndeletedEntry =>
BEGIN
DO
nextMessageExists, visible, postVisible: BOOLEAN;
initialY, postSelectionY: ScreenYCoord;
[initialY, visible] ← MapTOCIndexToTopY[selectedIndex, tnp];
[nextMessageExists, selectedIndex] ← NextTOCEntry[tnp, key, selectedIndex];
IF ~nextMessageExists THEN RETURN[exD.noUndeletedMessages];
MakeTOCIndexVisible[selectedIndex, key];
[postSelectionY, postVisible] ← MapTOCIndexToTopY[selectedIndex, tnp];
IF visible AND postVisible AND tnp.firstLineOffScreen.state # empty THEN
IF initialY < postSelectionY THEN
ScrollUpTOC[tnp.topY + postSelectionY - initialY, tnp, key]
ELSE IF initialY > postSelectionY THEN
ScrollDownTOC[tnp.topY + initialY - postSelectionY, tnp, key];
vmD.GetTOCFixedPart[toc, key, selectedIndex, @fp];
IF ~fp.deleted THEN EXIT;
ENDLOOP;
tsD.DeconsiderAll[tnp, key];
tsD.SetTOCSelection[toc, key, selectedIndex];
Consider[selectedIndex, selectedIndex, tnp, key];
END;
END; -- of EXITS --

IF dm.haveMessage THEN
BEGIN
messageToc: vmD.TOCHandle = message.toc;
IF messageToc = toc THEN vmD.FlushDisplayMessage[message, key]
ELSE BEGIN
messageKey: CARDINAL ← vmD.WaitForLock[messageToc];
vmD.FlushDisplayMessage[message, messageKey];
vmD.UnlockTOC[messageToc, messageKey];
END;
END;
UpdateTOCThumbLine[tnp, key];
vmD.LoadDisplayMessage[toc, key, selectedIndex, message];
message.formatStart ← message.formatEnd ← 0;
dm.haveMessage ← TRUE;
dsD.ClearRectangle[leftMargin, rightMargin, dm.topY, dm.bottomY];
Editor.RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: dm.lines, mnp: dm];
vmD.GetTOCFixedPart[toc, key, selectedIndex, @fp];
IF ~fp.seen THEN
BEGIN
line: LinePtr;
fp.seen ← TRUE;
vmD.PutTOCFixedPart[toc, key, selectedIndex, @fp];
line ← MapTOCIndexToTOCLine[selectedIndex, tnp];
IF line # NIL THEN
BEGIN
dsD.ClearRectangle[markLeftX, numberLeftX, line.y, line.y + dsD.lineHeight];
[] ← dsD.PutCharInBitMap[fp.mark, markLeftX, line.y, plainFace];
END;
END;
END; -- of DisplayMessageOperation --


MoveMessageToFileCommand: PUBLIC CommandProcedure =
-- Calls AppendMailToFileOperation for all selected messages in the current mail file to be
-- appended to the file whose name appears in the Move to brackets. If the operation is
-- successful, then the messages are Deleted.
BEGIN
appendFileHandle: VMDefs.FileHandle;
appendFileName: STRING;
fName: STRING = [75];
i: CARDINAL;
fileCreated: BOOLEAN ← FALSE;
bracketsText: STRING = intC.moveToBracketsHouse.text;
tnp: TOCTextNbrPtr = intC.tocTextNbr;
toc: vmD.TOCHandle ← tnp.toc;
key: CARDINAL ← 0;

IF ~confirmed AND
~ConfirmBrackets[hp: intC.moveToBracketsHouse, fileExtension: ".mail."L] THEN RETURN;
IF ~tnp.haveToc THEN {exD.DisplayException[exD.noCurrentFile]; RETURN};
key ← vmD.WaitForLock[toc];
IF tsD.TOCSelectionEmpty[toc, key] THEN
{exD.DisplayException[exD.noSelectedEntries]; vmD.UnlockTOC[toc, key]; RETURN};
vmD.UnlockTOC[toc, key];

-- first check for extension
String.AppendString[fName, bracketsText];
appendFileName ← fName;
IF fName.length > 0 THEN
FOR i IN [0 .. fName.length) DO
IF fName[i] = ’. THEN EXIT;
REPEAT
FINISHED => String.AppendString[fName, ".mail"L];
ENDLOOP;

-- Now check for new file:
BEGIN -- for EXITS --
appendFileHandle ← Core.Open[appendFileName, read
! VMDefs.CantOpen =>
SELECT reason FROM
notFound => BEGIN
exD.DisplayException[exD.newFile];
IF ~inD.Confirm[2] THEN GO TO Return;
fileCreated ← TRUE;
GO TO Continue;
END;
alreadyExists => {exD.DisplayException[exD.cantMoveToFileInUse]; GO TO Return};
illegalFileName => {exD.DisplayException[exD.noMoveIllegalName]; GO TO Return};
ENDCASE => GO TO Return];
Core.Close[appendFileHandle];
EXITS
Continue => NULL;
Return => RETURN;
END; -- of EXITS block --

key ← vmD.WaitForLock[toc];
BEGIN -- for EXITS --
opD.AppendMailToFileOperation[toc, key, appendFileName
! opD.NoMessagesMoved =>
BEGIN
exD.DisplayException[SELECT reason FROM
diskFull => exD.moveWouldOverfill,
noUndeleted => exD.noMessagesMoved,
ENDCASE => exD.diskErrorTargetDamage];
IF fileCreated THEN
BEGIN
appendFileHandle ← Core.Open[appendFileName, update
! VMDefs.CantOpen => GO TO unlock];
Core.Delete[appendFileHandle];
END;
GO TO unlock;
END];
ModifyTOCOperation[tnp, key, move];
DoDisplayAfterDelete[tnp, key];
GO TO unlock;
EXITS
unlock => vmD.UnlockTOC[toc, key];
END; -- of EXITS block --
END; -- of MoveMessageToFileCommand --


END. -- of IntDisplayCom --