-- file: EditorTypein.Mesa
-- last edited by Horning, February 16, 1978 3:24 PM
-- last edited by Brotz, February 20, 1981 2:43 PM
-- last edited by Spitzen, May 16, 1978 2:57 PM
-- last edited by Korb, November 7, 1979 9:57 AM. Modeless editor changes.

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

EditorTypein: PROGRAM
IMPORTS disC: displayCommon, dsD, Editor, exD, Inline, intC: intCommon, vmD
EXPORTS Editor = PUBLIC

BEGIN OPEN Editor, inD;

-- 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.


-- 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).



AcceptTypeIn: PROCEDURE [mnp: MessageTextNbrPtr, char: CHARACTER] =
-- Insert text at target.point.
BEGIN
-- Long set of locals to AcceptTypeIn:

InsertChar: PROCEDURE =
-- Globals: char
-- char is inserted in mnp.message and mnp.scratchBuffer and displayed on screen. Screen
-- is reformatted as necessary.
BEGIN
insertionCount: CARDINAL ← 1;
vmD.StartMessageInsertion[composeMessage, target.point];
DO
InsertMessChar[composeMessage, char];
AppendMessChar[mnp.insertionBuffer, char];
IF ~keystream.endof[keystream] THEN char ← keystream.get[keystream] ELSE EXIT;
SELECT char FROM
Ascii.ESC, Ascii.DEL, Ascii.BS, Ascii.ControlA, Ascii.ControlW =>
{keystream.putback[keystream, char]; EXIT};
cancelCode, insertDeletionCode, nextCode =>
IF modalEditor THEN insertionCount ← insertionCount + 1
ELSE {keystream.putback[keystream, char]; EXIT};
ENDCASE => insertionCount ← insertionCount + 1;
ENDLOOP;

vmD.StopMessageInsertion[composeMessage];
RefreshFromFirstChange[target.point, 0, insertionCount, mnp];
target.point ← target.point + insertionCount;
SELECT commandType↑ FROM
delete => commandType↑ ← replace;
insert, replace => NULL;
ENDCASE => commandType↑ ← insert;
END; -- of InsertChar --

BackSpaceWord: PROCEDURE =
-- Backspaces over one word.
BEGIN

LoopOverChars: PROCEDURE [isAlphaNumeric: BOOLEAN] =
BEGIN
mChar: CHARACTER;
UNTIL target.point = backSpaceTerminus DO
mChar ← vmD.GetMessageChar[composeMessage, target.point - 1];
IF (dsD.GetCharBreakProp[Inline.BITAND[mChar, ovD.CharMask]] = alphaNumeric)
= isAlphaNumeric THEN RETURN;
BackSpaceChar[];
nChars ← nChars + 1;
ENDLOOP;
END; -- of LoopOverChars --

nChars: CARDINAL ← 0;
-- First, back up over white space characters.
LoopOverChars[TRUE];
-- Then, back up over non-white space characters.
LoopOverChars[FALSE];
RefreshFromFirstChange[target.point, nChars, 0, mnp];
END; -- of BackSpaceWord --

BackSpaceChar: PROCEDURE =
-- Perform all actions required by backspace except for screen refresh.
BEGIN
char: CHARACTER;
insertionSize: CARDINAL = vmD.GetMessageSize[mnp.insertionBuffer];
IF insertionSize = 0 THEN
BEGIN
commandType↑ ← delete;
char ← vmD.GetMessageChar[mnp.message, target.point - 1];
vmD.StartMessageInsertion[mnp.deletionBuffer, 0];
InsertMessChar[mnp.deletionBuffer, char];
vmD.StopMessageInsertion[mnp.deletionBuffer];
END
ELSE BEGIN
vmD.UnAppendMessageChar[mnp.insertionBuffer];
IF insertionSize = 1 THEN
commandType↑ ← IF vmD.GetMessageSize[mnp.deletionBuffer] = 0
THEN noCommand ELSE delete;
END;
vmD.DeleteRangeInMessage[from: vmD.MessageRange
[target.point - 1, target.point, composeMessage]];
target.point ← target.point - 1;
actionPoint↑ ← MIN[actionPoint↑, target.point];
END; -- of BackSpaceChar --

BackSpaceCharAndRefresh: PROCEDURE =
BEGIN
IF target.point = backSpaceTerminus THEN RETURN;
BackSpaceChar[];
RefreshFromFirstChange[target.point, 1, 0, mnp];
END; -- of BackSpaceChar --

ForwardSpaceWord: PROCEDURE =
-- Forwardspaces over one word.
BEGIN

LoopOverChars: PROCEDURE [isAlphaNumeric: BOOLEAN] =
BEGIN
mChar: CHARACTER;
UNTIL target.point = vmD.GetMessageSize[composeMessage] DO
mChar ← vmD.GetMessageChar[composeMessage, target.point];
IF (dsD.GetCharBreakProp[Inline.BITAND[mChar, ovD.CharMask]] = alphaNumeric)
= isAlphaNumeric THEN RETURN;
ForwardSpaceChar[];
nChars ← nChars + 1;
ENDLOOP;
END; -- of LoopOverChars --

nChars: CARDINAL ← 0;
-- First, forward space over white space characters.
LoopOverChars[TRUE];
-- Then, forward space over non-white space characters.
LoopOverChars[FALSE];
RefreshFromFirstChange[target.point, nChars, 0, mnp];
END; -- of ForwardSpaceWord --

ForwardSpaceChar: PROCEDURE =
-- Perform all actions required by forwardspace except for screen refresh.
BEGIN
insertionSize: CARDINAL = vmD.GetMessageSize[mnp.insertionBuffer];
IF insertionSize = 0 THEN commandType↑ ← delete;
AppendMessChar[mnp.deletionBuffer, vmD.GetMessageChar[mnp.message, target.point]];
vmD.DeleteRangeInMessage[from: vmD.MessageRange
[target.point, target.point + 1, composeMessage]];
END; -- of ForwardSpaceChar --

ForwardSpaceCharAndRefresh: PROCEDURE =
BEGIN
IF target.point >= vmD.GetMessageSize[composeMessage] THEN RETURN;
ForwardSpaceChar[];
RefreshFromFirstChange[target.point, 1, 0, mnp];
END; -- of ForwardSpaceChar --

-- End of procedures local to AcceptTypeIn & start of its main code.

-- Initialization.
target: TextSelectionPtr ← @intC.target;
actionPoint: POINTER TO ovD.CharIndex ← @intC.actionPoint;
commandType: POINTER TO CommandType ← @intC.commandType;
topCharIndex: ovD.CharIndex ← mnp.lines.firstCharIndex;
backSpaceTerminus: ovD.CharIndex;
composeMessage: vmD.ComposeMessagePtr← vmD.MapVirtualToComposeMessage[mnp.message];
modalEditor: BOOLEAN ← (intC.editorType = modal);
keystream: StreamDefs.StreamHandle = intC.keystream;

PrepareForTypeIn[mnp];
backSpaceTerminus ← IF modalEditor THEN actionPoint↑ ELSE 0;

[] ← MakeCharIndexVisible[target.point, mnp];
DO -- read char and dispatch
SELECT char FROM
Ascii.ESC, Ascii.DEL => EXIT;
Ascii.ControlW =>
IF ~modalEditor AND ShiftKey[down]
THEN ForwardSpaceWord[] ELSE BackSpaceWord[];
Ascii.BS, Ascii.ControlA =>
IF ~modalEditor AND ShiftKey[down]
THEN ForwardSpaceCharAndRefresh[] ELSE BackSpaceCharAndRefresh[];
cancelCode, insertDeletionCode, nextCode =>
IF modalEditor THEN InsertChar[] ELSE EXIT;
ENDCASE => InsertChar[];
target.start ← target.end ← target.point;
[] ← MakeCharIndexVisible[target.point, mnp];
IF keystream.endof[keystream] THEN RETURN;
char ← keystream.get[keystream];
ENDLOOP; -- read char and dispatch
keystream.putback[keystream, char];
END; -- of AcceptTypeIn --


PrepareForTypeIn: PROCEDURE [mnp: MessageTextNbrPtr] =
-- Resets primary selection and handles pending delete properly.
BEGIN
target: TextSelectionPtr ← @intC.target;
IF intC.newTargetSelection THEN
BEGIN -- We are beginning a new typein sequence.
intC.newTargetSelection ← FALSE;
IF target.pendingDelete THEN {Deleter[mnp]; ResetInsertionBuffer[mnp]}
ELSE BEGIN
ResetBuffers[mnp];
DeUnderlineSelection[target, target];
target↑ ← TextSelection
[mnp, (intC.actionPoint ← target.point), target.point, target.point, char, FALSE];
END;
END;
END; -- of PrepareForTypeIn --


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


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


END. -- of EditorTypein --