-- file: IntEditCom.Mesa
-- edited by Horning, April 26, 1978 11:07 AM
-- edited by Brotz, April 8, 1981 11:40 AM
-- edited by Levin, July 9, 1980 1:23 PM

DIRECTORY
Editor,
exD: FROM "ExceptionDefs",
inD: FROM "InteractorDefs",
intCommon: FROM "IntCommon",
lmD: FROM "LaurelMenuDefs",
lsD: FROM "LaurelStateDefs",
ovD: FROM "OverviewDefs",
prD: FROM "ProtectionDefs",
String,
tsD: FROM "TocSelectionDefs",
vmD: FROM "VirtualMgrDefs";

IntEditCom: PROGRAM
IMPORTS Editor, exD, inD, intC: intCommon, lsD, prD, String, tsD, vmD
EXPORTS inD, lmD = PUBLIC

BEGIN

OPEN inD, Editor;

-- Editor Department of the Interactor Division
-- Implements the editor for the composed message. Commands are: Insert, Append, Replace,
-- Delete, Undo, Redo. Other operations (which are not commands per se) are selection,
-- scrolling, and command aborting.


-- Commands Section of the Editor Department
-- Establishes the command structure of the editor. Handles keyboard and mouse input;
-- interprets them. Establishes selections.

-- Selection convention: a range is represented by a nonempty half open interval; a point is
-- represented by an empty half open interval; selection beyond the message end is
-- represented by the point [messageLength .. messageLength).



NewFormCommand: PROCEDURE [hp: HousePtr, confirmed: BOOLEAN] =
-- Clears old composed message and initializes a new one to the standard composed message
-- template. Refreshes display of the composed message.
BEGIN
InitializeCM[hp, confirmed, newForm];
END; -- of NewFormCommand --


ForwardCommand: PROCEDURE [hp: HousePtr, confirmed: BOOLEAN] =
-- Clears old composed message and initializes a new one to the standard template for
-- forwarded messages, followed by the entire displayed message (surrounded by dashed
-- lines). Refreshes display of the composed message.
BEGIN
fixedPart: vmD.TOCFixedPart;
i: vmD.TOCIndex;
IF tsD.TOCSelectionEmpty[] THEN {exD.DisplayException[exD.cantForward]; RETURN};
-- Check that there is at least one message to print.
FOR i ← tsD.FirstSelectedEntry[], tsD.NextSelectedEntry[i] UNTIL i = 0 DO
vmD.GetTOCFixedPart[i, @fixedPart];
IF ~fixedPart.deleted THEN EXIT;
REPEAT
FINISHED => {exD.DisplayException[exD.noUndeletedEntries]; RETURN};
ENDLOOP;
InitializeCM[hp, confirmed, forward];
END; -- of ForwardCommand --


InitializeCM: PROCEDURE [hp: HousePtr, confirmed: BOOLEAN,
kind: {newForm, forward}] =
BEGIN
mnp: MessageTextNbrPtr = intC.cmTextNbr;
oldMessageLength: ovD.CharIndex ← vmD.GetMessageSize[mnp.message];
composeMessage: vmD.ComposeMessagePtr;
filler: STRING;

newFormString: STRING =
"Subject: Ťopic¿
To: Řecipients¿
cc: ČopiesTo¿,

M̌essage¿

"L;

forwardString: STRING =
"Subject: Ťopic¿
To: Řecipients¿
cc: ČopiesTo¿

ČoveringMessage¿
"L;

separator: STRING =
"

---------------------------

"L;


trailer: STRING =
"
------------------------------------------------------------

"L;


IF ~confirmed AND intC.composedMessageEdited
AND vmD.GetMessageSize[mnp.message] # 0
AND ~AskUserToConfirm[exD.willReplaceMessage]
THEN RETURN;

SwapMessageWithDeletionBuffer[mnp];
ResetInsertionBuffer[mnp];
composeMessage ← vmD.MapVirtualToComposeMessage[mnp.message];
IF intC.cForCopies THEN
BEGIN
newFormString[35] ← ’:;
newFormString[36] ← ’ ;
forwardString[35] ← ’:;
forwardString[36] ← ’ ;
END;

IF kind = newForm THEN
BEGIN
vmD.InitComposeMessage[composeMessage, newFormString];
vmD.StartMessageInsertion[composeMessage, 50];
filler ← intC.user.name;
END
ELSE IF kind = forward THEN
BEGIN
fp: vmD.TOCFixedPart;
vm: vmD.DisplayMessagePtr ← vmD.AllocateDisplayMessageObject[];
index: vmD.TOCIndex;
vmSize, cmSize: ovD.CharIndex;
limit: ovD.CharIndex = 62000;

vmD.InitComposeMessage[composeMessage, forwardString];
FOR index ← tsD.FirstSelectedEntry[], tsD.NextSelectedEntry[index] UNTIL index = 0 DO
vmD.GetTOCFixedPart[index, @fp];
IF fp.deleted THEN LOOP;
IF vmD.VirtualizeMessage[index, vm] # ovD.ok OR
(vmSize ← vmD.GetMessageSize[vm]) + separator.length + trailer.length
> (limit - (cmSize ← vmD.GetMessageSize[composeMessage])) THEN
{exD.DisplayException[exD.totalMessageLengthTooBig]; EXIT};
vmD.StartMessageInsertion[composeMessage, cmSize];
[] ← vmD.InsertSubstringInMessage[composeMessage, separator, 0, separator.length];
vmD.StopMessageInsertion[composeMessage];
InsertRangeInMess[cmSize + separator.length, composeMessage, [0, vmSize, vm]];
ENDLOOP;
vmD.FreeVirtualMessageObject[vm];
vmD.StartMessageInsertion[composeMessage, vmD.GetMessageSize[composeMessage]];
filler ← trailer;
END;
[] ← vmD.InsertSubstringInMessage[composeMessage, filler, 0, filler.length];
vmD.StopMessageInsertion[composeMessage];

intC.commandType ← get;
intC.source ← TextSelection
[mnp: mnp, start: 0, end: 0, point: 0, mode: char, pendingDelete: FALSE];
intC.target ← TextSelection
[mnp: mnp, start: 9, end: 16, point: 9, mode: word,
pendingDelete: intC.editorType = modeless];
intC.newTargetSelection ← TRUE;
intC.actionPoint ← 0;
intC.pendingDeleteSetByControl ← FALSE;
IF mnp.protectedFieldPtr # NIL THEN prD.UnprotectAllFields[@mnp.protectedFieldPtr];
SetHighWaterMark[0];
RefreshSoThatFirstCharStartsLine[firstChar: 0, firstLine: mnp.lines, mnp: mnp];
mnp.haveMessage ← TRUE;
intC.runCommandMode ← FALSE;
intC.composedMessageEdited ← FALSE;
intC.deliverCommandHouse.text.length ← 0;
intC.deliverCommandHouse.typeface ← italicFace;
intC.deliverCommandHouse.callable ← FALSE;
TextHouseRefresher[intC.deliverCommandHouse];
END; -- of InitializeCM --


SwapInMenu: PROCEDURE [menuSegment: lsD.StateSegment] RETURNS [hp: HousePtr] =
-- Swaps in the menu contained in menuSegment and locks it in memory. Sets all nextHouse
-- pointers in the house list swapped in to be true pointers to the next House in the list.
-- Returns a pointer to the head of the list.
BEGIN
h, nextHouse: HousePtr;
h ← hp ← lsD.SwapInStateSegment[menuSegment];
DO
h.text ← LOOPHOLE[h + SIZE[House], STRING];
nextHouse ← h + SIZE[House] + String.WordsForString[h.text.maxlength];
IF h.nextHouse = LOOPHOLE[0] THEN {h.nextHouse ← NIL; RETURN}
ELSE h ← h.nextHouse ← nextHouse;
ENDLOOP;
END; -- of SwapInMenu --


ReleaseMenu: PROCEDURE [menuSegment: lsD.StateSegment] =
-- Resets all pointers in the in the menuSegment to their canonical swapped out form:
-- all nextHouse fields are set to 1 except for the last which is set to 0. Releases the
-- menuSegment.
BEGIN
h, nextHouse: HousePtr;
h ← lsD.StateSegmentAddress[menuSegment];
DO
nextHouse ← h.nextHouse;
IF nextHouse = NIL THEN {h.nextHouse ← LOOPHOLE[0]; EXIT}
ELSE {h.nextHouse ← LOOPHOLE[1]; h ← nextHouse};
ENDLOOP;
lsD.WriteStateSegment[menuSegment];
lsD.ReleaseStateSegment[menuSegment];
END; -- of ReleaseMenu --


MapHouseNumberToHousePtr: PROCEDURE [knownHp: HousePtr, knownHpNumber,
number: lmD.HouseNumber] RETURNS [hp: HousePtr] =
-- Returns a pointer to the number’th house (first cnp.houses is number 0) in the house list
-- pointed to by knownHp. KnownHp’s HouseNumber is knownHpNumber. This procedure
-- should be used to set HousePtr’s for houses that are relocated in segments.
BEGIN
hp ← knownHp;
THROUGH [1 .. number - knownHpNumber] DO
hp ← hp.nextHouse;
ENDLOOP;
END; -- of MapHouseNumberToHousePtr --


InsertRangeInMess: PROCEDURE [targetIndex: ovD.CharIndex,
targetMessage: vmD.ComposeMessagePtr, from: vmD.MessageRange] =
-- ### Temporary KLUDGE to keep from losing virtual manager error messages.
BEGIN
errorC: ovD.ErrorCode ← vmD.InsertRangeInMessage[targetIndex, targetMessage, from];
IF errorC # ovD.ok THEN exD.SysBug[exD.insertionOverflow]
END;


InsertMessChar: PROCEDURE [cM: vmD.ComposeMessagePtr, char: CHARACTER] =
-- ### Temporary KLUDGE to keep from losing virtual manager error messages.
BEGIN
errorC: ovD.ErrorCode ← vmD.InsertMessageChar[cM, char];
IF errorC # ovD.ok THEN exD.SysBug[exD.insertionOverflow]
END;


END. -- of IntEditCom --