-- file: EditorTypein.Mesa
-- last edited by Horning, February 16, 1978 3:24 PM
-- last edited by Brotz, October 1, 1982 4: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.
-- last edited by Taft, May 6, 1983 5:37 PM

DIRECTORY
Ascii USING [BS, ControlA, ControlW, DEL, ESC],
dsD: FROM "DisplayDefs" USING [GetCharBreakProp],
Editor USING [cancelCode, CommandType, Deleter, DeUnderlineSelection,
insertDeletionCode, MakeCharIndexVisible, nextCode, RefreshFromFirstChange,
ResetBuffers, ResetInsertionBuffer, ShiftKey],
inD: FROM "InteractorDefs" USING [CharIndex, MessageTextNbrPtr, TextSelection,
TextSelectionPtr],
Inline USING [BITAND],
intCommon USING [actionPoint, commandType, editorType, keystream, newTargetSelection,
target],
ovD: FROM "OverviewDefs" USING [CharMask],
prD: FROM "ProtectionDefs" USING [FindUnprotectedSubrange],
StreamDefs USING [StreamHandle],
vmD: FROM "VirtualMgrDefs" USING [AppendMessageChar, CharIndex, ComposedMessage,
ComposedMessagePtr, DeleteRangeInMessage, GetMessageChar, GetMessageSize,
InsertMessageChar, MessageRange, StartMessageInsertion, StopMessageInsertion,
UnAppendMessageChar];

EditorTypein: PROGRAM
IMPORTS dsD, Editor, Inline, intC: intCommon, prD, vmD
EXPORTS Editor =

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: PUBLIC 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[composedMessage, target.point];
DO
vmD.InsertMessageChar[composedMessage, char];
vmD.AppendMessageChar[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[composedMessage];
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 = terminus DO
mChar ← vmD.GetMessageChar[composedMessage, 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;
terminus: CharIndex ← FindBackSpaceTerminus[];
-- 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];
vmD.InsertMessageChar[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, composedMessage]];
target.point ← target.point - 1;
actionPoint↑ ← MIN[actionPoint↑, target.point];
END; -- of BackSpaceChar --

BackSpaceCharAndRefresh: PROCEDURE =
BEGIN
IF target.point = FindBackSpaceTerminus[] 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+nChars >= terminus DO
mChar ← vmD.GetMessageChar[composedMessage, 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;
terminus: CharIndex ← FindForwardSpaceTerminus[];
-- 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;
vmD.AppendMessageChar
[mnp.deletionBuffer, vmD.GetMessageChar[mnp.message, target.point]];
vmD.DeleteRangeInMessage
[from: vmD.MessageRange[target.point, target.point + 1, composedMessage]];
END; -- of ForwardSpaceChar --

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

FindBackSpaceTerminus: PROCEDURE RETURNS [ci: CharIndex] =
BEGIN
range: vmD.MessageRange;
ci ← IF modalEditor THEN actionPoint↑ ELSE 0;
IF mnp.protectedFieldPtr = NIL THEN RETURN;
range ← [ci, target.point, composedMessage];
prD.FindUnprotectedSubrange[pfp: mnp.protectedFieldPtr, rangePtr: @range, fixStart:FALSE];
RETURN[range.start];
END; -- of FindBackSpaceTerminus --

FindForwardSpaceTerminus: PROCEDURE RETURNS [ci: CharIndex] =
BEGIN
range: vmD.MessageRange;
ci ← vmD.GetMessageSize[composedMessage];
IF mnp.protectedFieldPtr = NIL THEN RETURN;
range ← [target.point, ci, composedMessage];
prD.FindUnprotectedSubrange[pfp: mnp.protectedFieldPtr, rangePtr: @range, fixStart: TRUE];
RETURN[range.end];
END; -- of FindForwardSpaceTerminus --

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

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

PrepareForTypeIn[mnp];

[] ← 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: PUBLIC 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, 0, char, FALSE];
END;
END;
END; -- of PrepareForTypeIn --


END. -- of EditorTypein --