-- file: IntEditCom.Mesa
-- edited by Horning, April 26, 1978 11:07 AM
-- edited by Brotz, November 25, 1981 4:49 PM
-- edited by Levin, July 9, 1980 1:23 PM
-- edited by Taft, May 21, 1983 3:23 PM

DIRECTORY
Editor USING [RefreshSoThatFirstCharStartsLine, ResetInsertionBuffer,
SwapMessageWithDeletionBuffer],
exD: FROM "ExceptionDefs" USING [cantForward, DisplayException,
noUndeletedEntries, totalMessageLengthTooBig, willReplaceMessage],
inD: FROM "InteractorDefs" USING [AskUserToConfirm, CharIndex, House, HousePtr,
MessageTextNbrPtr, TextHouseRefresher, TextSelection],
intCommon USING [actionPoint, cForCopies, cmTextNbr, commandType,
composedMessageEdited, deliverCommandHouse, deliverCommandVisible, editorType,
newTargetSelection, pendingDeleteSetByControl, runCommandMode, source, target,
tocTextNbr, user],
lmD: FROM "LaurelMenuDefs" USING [HouseNumber],
lsD: FROM "LaurelStateDefs" USING [ReleaseStateSegment, StateSegment,
StateSegmentAddress, SwapInStateSegment, WriteStateSegment],
prD: FROM "ProtectionDefs" USING [UnprotectAllFields],
String USING [EquivalentString, WordsForString],
tsD: FROM "TocSelectionDefs" USING [FirstSelectedAndUndeletedEntry,
NextSelectedAndUndeletedEntry, TOCSelectionEmpty],
vmD: FROM "VirtualMgrDefs" USING [AllocateDisplayMessageObject, CharIndex,
ComposedMessage, ComposedMessagePtr, DisplayMessagePtr, FlushDisplayMessage,
FreeVirtualMessageObject, GetMessageSize, InitComposedMessage, InsertRangeInMessage,
InsertSubstringInMessage, LoadDisplayMessage, StartMessageInsertion,
StopMessageInsertion, TOCHandle, TOCIndex, UnlockTOC, WaitForLock];

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

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: PUBLIC 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
toc: vmD.TOCHandle = intC.tocTextNbr.toc;
key: CARDINAL ← 0;

InitializeCM[hp, confirmed, newForm, NIL, 0];
END; -- of NewFormCommand --


ForwardCommand: PUBLIC 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
toc: vmD.TOCHandle = intC.tocTextNbr.toc;
key: CARDINAL ← vmD.WaitForLock[toc];
IF tsD.TOCSelectionEmpty[toc, key] THEN
{exD.DisplayException[exD.cantForward]; vmD.UnlockTOC[toc, key]; RETURN};
IF tsD.FirstSelectedAndUndeletedEntry[toc, key] = 0 THEN
{exD.DisplayException[exD.noUndeletedEntries]; vmD.UnlockTOC[toc, key]; RETURN};
InitializeCM[hp, confirmed, forward, toc, key];
vmD.UnlockTOC[toc, key];
END; -- of ForwardCommand --


InitializeCM: PROC [hp: HousePtr, confirmed: BOOLEAN, kind: {newForm, forward},
toc: vmD.TOCHandle, key: CARDINAL] =
BEGIN
mnp: MessageTextNbrPtr = intC.cmTextNbr;
oldMessageLength: CharIndex ← vmD.GetMessageSize[mnp.message];
composedMessage: vmD.ComposedMessagePtr;
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];
composedMessage ← vmD.ComposedMessage[mnp.message];
IF intC.cForCopies THEN
BEGIN
newFormString[35] ← ’:;
newFormString[36] ← ’ ;
forwardString[35] ← ’:;
forwardString[36] ← ’ ;
END;

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

vmD.InitComposedMessage[composedMessage, forwardString];
FOR index ← tsD.FirstSelectedAndUndeletedEntry[toc, key],
tsD.NextSelectedAndUndeletedEntry[toc, key, index] UNTIL index = 0 DO
vmD.LoadDisplayMessage[toc, key, index, vm];
IF (vmSize ← vmD.GetMessageSize[vm]) + separator.length + trailer.length
> (limit - (cmSize ← vmD.GetMessageSize[composedMessage])) THEN
BEGIN
exD.DisplayException[exD.totalMessageLengthTooBig];
vmD.FlushDisplayMessage[vm, key];
EXIT;
END;
vmD.StartMessageInsertion[composedMessage, cmSize];
[] ← vmD.InsertSubstringInMessage[composedMessage, separator, 0, separator.length];
vmD.StopMessageInsertion[composedMessage];
vmD.InsertRangeInMessage
[cmSize + separator.length, composedMessage, [0, vmSize, vm]];
vmD.FlushDisplayMessage[vm, key];
ENDLOOP;
vmD.FreeVirtualMessageObject[vm];
vmD.StartMessageInsertion[composedMessage, vmD.GetMessageSize[composedMessage]];
filler ← trailer;
END;
[] ← vmD.InsertSubstringInMessage[composedMessage, filler, 0, filler.length];
vmD.StopMessageInsertion[composedMessage];

intC.commandType ← get;
intC.source ← TextSelection
[mnp: mnp, start: 0, end: 0, point: 0, key: 0, mode: char, pendingDelete: FALSE];
intC.target ← TextSelection[mnp: mnp, start: 9, end: 16, point: 9, key: 0, mode: word,
pendingDelete: intC.editorType = modeless];
intC.newTargetSelection ← TRUE;
intC.actionPoint ← 0;
intC.pendingDeleteSetByControl ← FALSE;
IF mnp.protectedFieldPtr # NIL THEN prD.UnprotectAllFields[@mnp.protectedFieldPtr];
composedMessage.formatStart ← composedMessage.formatEnd ← 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;
IF intC.deliverCommandVisible THEN TextHouseRefresher[intC.deliverCommandHouse];
END; -- of InitializeCM --


SwapInMenu: PUBLIC 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: PUBLIC 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: PUBLIC 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 --


MapHouseTextToHousePtr: PUBLIC PROCEDURE [knownHp: HousePtr, s: STRING]
RETURNS [hp: HousePtr] =
-- Returns a pointer to the house in the house list started by knownHp whose text field
-- matches "s". To obtain the HousePtr for a brackets house, use
-- MapHouseTextToHousePtr[hp, textOfCommandHouse].nextHouse.
BEGIN
FOR hp ← knownHp, hp.nextHouse UNTIL hp = NIL OR String.EquivalentString[hp.text, s]
DO ENDLOOP;
END; -- of MapHouseTextToHousePtr --


END. -- of IntEditCom --