-- 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
DIRECTORY
  Editor USING [RefreshSoThatFirstCharStartsLine, ResetInsertionBuffer,
    SwapMessageWithDeletionBuffer],
  exD: FROM "ExceptionDefs" USING [cantForward, DisplayException,
    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 [FirstSelectedEntry, NextSelectedEntry,
    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};
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.FirstSelectedEntry[toc, key], tsD.NextSelectedEntry[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 --