-- file: EditorFiles.Mesa
-- edited by Schroeder, January 13, 1981 11:37 AM
-- edited by Brotz, May 3, 1982 5:33 PM
-- edited by Levin, February 22, 1980 12:42 PM
-- edited by Taft, May 13, 1983 10:23 AM

DIRECTORY
displayCommon USING [bitmapInMDS],
dsD: FROM "DisplayDefs" USING [ChangeCursor, CursorShape, FaceType, GetCursor,
GetStringWidth, ScreenXCoord],
Editor USING [CancelSourceSelection, RefreshSoThatFirstCharStartsLine, ResetBuffers,
ResetDeletionBuffer, SelectNextBlank, SwapMessageWithDeletionBuffer,
TurnOnDeliver, UnrecognizedCommand, UpdateScreen],
exD: FROM "ExceptionDefs" USING [AppendDecimalToExceptionLine,
AppendStringToExceptionLine, ClearExceptionsRegion, confirmFileName,
DisplayExceptionLine, DisplayExceptionOrStringOnLine,
DisplayExceptionStringOnLine, Exception, ExceptionLineOverflow,
FlashExceptionsRegion, getFailed, messageTooBig, nil, putWillOverwrite, SysBug,
willReplaceMessage],
inD: FROM "InteractorDefs" USING [AskUserToConfirm, BracketsHouseRefresher,
CaretIsBlinking, ChangeCommandMenu, CharIndex, CommandProcedure,
ConfirmBrackets, House, HousePtr, IndicateCommandBusy, IndicateCommandFinished,
leftMargin, maxBracketStringLength, MessageTextNbrPtr, NullCommand, RefreshHouse,
rightMargin, ScreenXCoord, SetCaretBlinking, TextHouseRefresher, TextSelection],
intCommon USING [actionPoint, cmCommandNbr, CMCommandRegion, cmTextNbr,
commandType, composedMessageEdited, copyMenuSegment, copySourceBracketsHouse,
copyTargetBracketsHouse, currentCommand, disableWriting, editorMenuState,
findBracketsHouse, findHouse, findMenuSegment, getPutBracketsHouse,
getPutMenuSegment, newTargetSelection, runBracketsHouse, runCommandMode,
runMenuSegment, substituteHouse, substituteMenuSegment,
substituteNewBracketsHouse, substituteOldBracketsHouse, target],
lmD: FROM "LaurelMenuDefs" USING [copySourceBracketsNumber,
copyTargetBracketsNumber, deliverCommandNumber, EditorMenuState,
findBracketsNumber, findCommandNumber, getPutBracketsNumber, HouseDescriptor,
MapHouseNumberToHousePtr, MapHouseTextToHousePtr, runBracketsNumber,
substituteCommandNumber, substituteNewBracketsNumber,
substituteOldBracketsNumber, SwapInMenu],
opD: FROM "OperationsDefs" USING [Copy, FileError],
Storage USING [FreeString, String],
String USING [AppendLongNumber, AppendString, EquivalentSubString,
SubStringDescriptor],
vmD: FROM "VirtualMgrDefs" USING [CharIndex, ComposedMessage,
ComposedMessagePtr, DeleteRangeInMessage, GetMessageSize, InsertFileInMessage,
InsertRangeInMessage, InvalidateCaches, MessageOverflow, MessageRange,
PutRangeInFile];

EditorFiles: PROGRAM
IMPORTS disC: displayCommon, dsD, Editor, exD, inD, intC: intCommon, lmD,
opD, Storage, String, vmD
EXPORTS Editor, inD, lmD =

BEGIN

OPEN Editor, inD;


GCommand: PUBLIC PROCEDURE [mnp: MessageTextNbrPtr] =
-- Replaces the current primary selection with the contents of the file named in
-- the get/put brackets.
BEGIN
getHouse: HousePtr ← lmD.MapHouseTextToHousePtr[intC.cmCommandNbr.houses, "Get"L];
ChangeEditorMenu[getPut];
IF getHouse # NIL THEN IndicateCommandBusy[intC.currentCommand ← getHouse];
exD.DisplayExceptionLine[exD.confirmFileName, 1];
IF ConfirmBrackets[hp: intC.getPutBracketsHouse, fileExtension: ".form."L] THEN
BEGIN
IF disC.bitmapInMDS THEN vmD.InvalidateCaches[]; -- free up VM buffers
Getter[intC.target.start, intC.target.end, mnp, intC.getPutBracketsHouse.text];
END;
IF getHouse # NIL THEN IndicateCommandFinished[getHouse];
intC.currentCommand ← NIL;
END; -- of GCommand --


GetCommand: PUBLIC CommandProcedure =
-- Replaces the current composed message with the contents of the file named in
-- the brackets.
BEGIN
oldText: STRING ← [maxBracketStringLength];
gpbHp: HousePtr;
ChangeEditorMenu[getPut];
gpbHp ← intC.getPutBracketsHouse;
String.AppendString[oldText, gpbHp.text];
IF ~confirmed AND ~ConfirmBrackets[hp: gpbHp, fileExtension: ".form."L]
THEN RETURN;
IF intC.composedMessageEdited AND vmD.GetMessageSize[intC.cmTextNbr.message] # 0
AND ~AskUserToConfirm[exD.willReplaceMessage]
THEN {RefreshHouse[gpbHp, oldText]; RETURN};
Getter[0, vmD.GetMessageSize[intC.cmTextNbr.message], intC.cmTextNbr, gpbHp.text];
intC.runCommandMode ← FALSE;
END; -- of GetCommand --


Getter: PUBLIC PROCEDURE [start, end: CharIndex, mnp: MessageTextNbrPtr,
fileName: STRING] =
-- Replaces [start .. end) from mnp.message with contents of fileName.
BEGIN
selectionLength: CARDINAL = end - start;
insertionLength: CARDINAL;
preInsertionMessageLength: CARDINAL;
preGetMessageLength: CharIndex ← vmD.GetMessageSize[mnp.message];
whereToInsert: vmD.ComposedMessagePtr;
completeGet: BOOLEAN = (start = 0 AND end = preGetMessageLength);
isForm: BOOLEAN ← FALSE;
dotForm: STRING ← ".form"L;
trailerSSD: String.SubStringDescriptor
← String.SubStringDescriptor[base: fileName, offset: fileName.length - 5, length: 5];
dotFormSSD: String.SubStringDescriptor
← String.SubStringDescriptor[base: dotForm, offset: 0, length: 5];
fName: STRING;

ReportErrorAndCleanup: PROCEDURE [exception: exD.Exception, s: STRING] =
BEGIN
-- sorry, subsequent undo not permitted
IF ~completeGet THEN vmD.InsertRangeInMessage
[targetIndex: start, targetMessage: vmD.ComposedMessage[mnp.message],
from: vmD.MessageRange[0, end - start, mnp.deletionBuffer]];
ResetDeletionBuffer[mnp];
exD.DisplayExceptionLine[exD.getFailed, 1];
exD.DisplayExceptionOrStringOnLine[exception, s, 2];
exD.FlashExceptionsRegion[];
END; -- of ReportErrorAndCleanup --

CancelSourceSelection[mnp];
ResetBuffers[mnp];
IF completeGet THEN whereToInsert ← mnp.deletionBuffer
ELSE BEGIN
vmD.InsertRangeInMessage[targetIndex: 0, targetMessage: mnp.deletionBuffer,
from: vmD.MessageRange[start, end, mnp.message]];
vmD.DeleteRangeInMessage[from: vmD.MessageRange[start, end, mnp.message]];
whereToInsert ← vmD.ComposedMessage[mnp.message];
END;
preInsertionMessageLength ← vmD.GetMessageSize[whereToInsert];
IF fileName.length > 5 AND String.EquivalentSubString[@trailerSSD, @dotFormSSD]
THEN isForm ← TRUE;

fName ← fileName;
BEGIN -- for EXITS --
vmD.InsertFileInMessage[targetIndex: start, targetMessage: whereToInsert, file: fName
! opD.FileError =>
SELECT reason FROM
notFound =>
BEGIN -- try adding .form to the filename.
IF fName.length > 0 THEN
FOR i: CARDINAL IN [0 .. fName.length) DO
IF fName[i] = ’. THEN EXIT;
REPEAT
FINISHED =>
BEGIN -- This can only happen once.
fName ← Storage.String[fileName.length + 5];
String.AppendString[fName, fileName];
String.AppendString[fName, dotForm];
isForm ← TRUE;
RETRY;
END;
ENDLOOP;
ReportErrorAndCleanup[exD.nil, errorString];
GO TO Return;
END;
ENDCASE => {ReportErrorAndCleanup[exD.nil, errorString]; GO TO Return};
vmD.MessageOverflow =>
{ReportErrorAndCleanup[exD.messageTooBig, NIL]; GO TO Return};];

insertionLength ← vmD.GetMessageSize[whereToInsert] - preInsertionMessageLength;
exD.ClearExceptionsRegion[];
exD.AppendDecimalToExceptionLine[insertionLength, 1];
exD.AppendStringToExceptionLine[" characters read."L, 1];
IF completeGet THEN
BEGIN
intC.target ← TextSelection[mnp, 0, 0, 0, 0, char, FALSE];
SwapMessageWithDeletionBuffer[mnp];
intC.commandType ← get;
mnp.message.formatStart ← mnp.message.formatEnd ← 0;
RefreshSoThatFirstCharStartsLine[0, mnp.lines, mnp];
IF isForm THEN Editor.SelectNextBlank[mnp, TRUE];
TurnOnDeliver[];
intC.composedMessageEdited ← FALSE;
END
ELSE BEGIN
intC.target ← TextSelection[mnp, start, start + insertionLength, start, 0, char, FALSE];
vmD.InsertRangeInMessage
[targetIndex: 0, targetMessage: mnp.insertionBuffer,
from: vmD.MessageRange[start, start + insertionLength, mnp.message]];
intC.commandType ← replace;
UpdateScreen[start: start, nCharsDeleted: end - start,
nCharsInserted: insertionLength, mnp: mnp];
END;
intC.newTargetSelection ← TRUE;
intC.actionPoint ← start;
mnp.haveMessage ← TRUE;
GO TO Return;
EXITS
Return => IF fName # fileName THEN Storage.FreeString[fName];
END;
END; -- of Getter --


PCommand: PUBLIC PROCEDURE [mnp: MessageTextNbrPtr] =
-- Writes the current primary selection onto the file named in the get/put
-- brackets.
BEGIN
oldText: STRING ← [maxBracketStringLength];
gpbHp: HousePtr;
putHouse: HousePtr ← lmD.MapHouseTextToHousePtr[intC.cmCommandNbr.houses, "Put"L];
IF intC.disableWriting THEN {UnrecognizedCommand[]; RETURN};
ChangeEditorMenu[getPut];
gpbHp ← intC.getPutBracketsHouse;
IF putHouse # NIL THEN IndicateCommandBusy[intC.currentCommand ← putHouse];
exD.DisplayExceptionLine[exD.confirmFileName, 1];
String.AppendString[oldText, gpbHp.text];
IF ConfirmBrackets[gpbHp] THEN
BEGIN
IF disC.bitmapInMDS THEN vmD.InvalidateCaches[]; -- free up VM buffers
[] ← Putter[intC.target.start, intC.target.end, mnp, gpbHp, oldText];
END;
IF putHouse # NIL THEN IndicateCommandFinished[putHouse];
intC.currentCommand ← NIL;
END; -- of PCommand --


PutCommand: PUBLIC CommandProcedure =
-- Writes the current composed message onto the file named in the get/put
-- brackets.
BEGIN
oldText: STRING ← [maxBracketStringLength];
gpbHp: HousePtr;
ChangeEditorMenu[getPut];
gpbHp ← intC.getPutBracketsHouse;
String.AppendString[oldText, gpbHp.text];
IF (confirmed OR ConfirmBrackets[gpbHp])
AND Putter[0, vmD.GetMessageSize[intC.cmTextNbr.message], intC.cmTextNbr,
gpbHp, oldText]
THEN intC.composedMessageEdited ← FALSE;
IndicateCommandFinished[hp];
IF CaretIsBlinking[] THEN SetCaretBlinking[intC.target.point, intC.target.mnp];
END; -- of PutCommand --


Putter: PUBLIC PROCEDURE [start, end: CharIndex, mnp: MessageTextNbrPtr,
hp: HousePtr, oldText: STRING] RETURNS [successful: BOOLEAN] =
-- Writes [start .. end) from mnp.message onto file named in hp.text.
BEGIN
savedCursor: dsD.CursorShape;
[savedCursor, , ] ← dsD.GetCursor[];
dsD.ChangeCursor[hourGlass];
successful ← TRUE;
vmD.PutRangeInFile[from: vmD.MessageRange[start, end, mnp.message],
file: hp.text, concurrenceNeeded: TRUE, UserConcurs: AskUserToConfirm
! opD.FileError =>
BEGIN
IF reason = cancel THEN RefreshHouse[hp, oldText];
successful ← FALSE;
CONTINUE;
END];
IF successful THEN
BEGIN
exD.AppendDecimalToExceptionLine[end - start, 1];
exD.AppendStringToExceptionLine[" characters written."L, 1];
END;
dsD.ChangeCursor[savedCursor];
END; -- of Putter --


CopyCommand: PUBLIC CommandProcedure =
-- Copies a remote or local file to a remote or local file using opD.Copy. The composed
-- message is not affected by this command.
BEGIN
bytesCopied: LONG CARDINAL;
count: STRING = [12];

OverwriteOK: PROCEDURE RETURNS [BOOLEAN] =
{RETURN[AskUserToConfirm[exD.putWillOverwrite]]};

ChangeEditorMenu[copy];
IF ~(confirmed OR ConfirmBrackets[intC.copySourceBracketsHouse])
OR ~ConfirmBrackets[intC.copyTargetBracketsHouse] THEN RETURN;

exD.ClearExceptionsRegion[];
bytesCopied ← opD.Copy
[intC.copySourceBracketsHouse.text, intC.copyTargetBracketsHouse.text, OverwriteOK
! opD.FileError =>
BEGIN
exD.DisplayExceptionStringOnLine["Copy failed: "L, 1];
exD.AppendStringToExceptionLine
[IF reason = get THEN "can’t get "L ELSE "can’t put "L, 1];
exD.AppendStringToExceptionLine
[IF reason = get THEN intC.copySourceBracketsHouse.text
ELSE intC.copyTargetBracketsHouse.text, 1
! exD.ExceptionLineOverflow => CONTINUE];
exD.DisplayExceptionStringOnLine[errorString, 2];
exD.FlashExceptionsRegion[];
GO TO Return;
END];
String.AppendLongNumber[count, bytesCopied, 10];
exD.AppendStringToExceptionLine[count, 1];
exD.AppendStringToExceptionLine[" bytes copied."L, 1];
EXITS
Return => NULL;
END; -- of CopyCommand --


MenuChange: PUBLIC SIGNAL = CODE;
-- Raised by ChangeCommandMenu to notify any HousePtr holders that they may be
-- invalid.


ChangeEditorMenu: PUBLIC PROCEDURE [newState: lmD.EditorMenuState] =
-- Change the Editor menu to reflect the newState. Raises the resumable SIGNAL
-- MenuChange if the menu is actually changing.
BEGIN
lastHouse: HousePtr;
linesToKeep: CARDINAL;
IF newState = intC.editorMenuState THEN RETURN;
SIGNAL MenuChange;
linesToKeep
← intC.cmCommandNbr.nLines - (IF intC.editorMenuState = singleLine THEN 0 ELSE 1);
IF intC.cmCommandNbr.houses = NIL THEN exD.SysBug[];
FOR lastHouse ← intC.cmCommandNbr.houses, lastHouse.nextHouse
UNTIL lastHouse.nextHouse = NIL OR lastHouse.nextHouse.lineNumber = linesToKeep
DO ENDLOOP;
SELECT newState FROM
singleLine => lastHouse.nextHouse ← NIL;
getPut => BEGIN
lastHouse.nextHouse ← lmD.SwapInMenu[intC.getPutMenuSegment];
intC.getPutBracketsHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.getPutBracketsNumber];
END;
run => BEGIN
lastHouse.nextHouse ← lmD.SwapInMenu[intC.runMenuSegment];
intC.runBracketsHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.runBracketsNumber];
END;
find => BEGIN
lastHouse.nextHouse ← lmD.SwapInMenu[intC.findMenuSegment];
intC.findHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.findCommandNumber];
intC.findBracketsHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.findBracketsNumber];
END;
substitute => BEGIN
lastHouse.nextHouse ← lmD.SwapInMenu[intC.substituteMenuSegment];
intC.substituteHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.substituteCommandNumber];
intC.substituteNewBracketsHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.substituteNewBracketsNumber];
intC.substituteOldBracketsHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.substituteOldBracketsNumber];
END;
copy => BEGIN
lastHouse.nextHouse ← lmD.SwapInMenu[intC.copyMenuSegment];
intC.copySourceBracketsHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.copySourceBracketsNumber];
intC.copyTargetBracketsHouse ← lmD.MapHouseNumberToHousePtr
[lastHouse, lmD.deliverCommandNumber, lmD.copyTargetBracketsNumber];
END;
ENDCASE => exD.SysBug[];
FOR lastHouse ← lastHouse.nextHouse, lastHouse.nextHouse UNTIL lastHouse = NIL DO
lastHouse.lineNumber ← linesToKeep;
ENDLOOP;
ChangeCommandMenu
[cnp: intC.cmCommandNbr, region: intC.CMCommandRegion, linesToKeep: linesToKeep];
intC.editorMenuState ← newState;
END; -- of ChangeEditorMenu --


ShortenEditorMenuCommand: PUBLIC CommandProcedure =
BEGIN
ChangeEditorMenu[singleLine];
IF CaretIsBlinking[] THEN SetCaretBlinking[intC.target.point, intC.target.mnp];
END; -- of ShortenEditorMenuCommand --


CreateCommandHouses: PUBLIC PROCEDURE
[items: DESCRIPTOR FOR ARRAY OF lmD.HouseDescriptor,
allocateNode: PROCEDURE [CARDINAL] RETURNS [POINTER],
allocateString: PROCEDURE [CARDINAL] RETURNS [STRING],
firstLine: CARDINAL ← 0,
firstLeftX: ScreenXCoord ← leftMargin]
RETURNS [hp: HousePtr] =
BEGIN
interCommandSpacing: CARDINAL = 15;
preBracketSpacing: CARDINAL = 7;
bracketWidth: CARDINAL = 80;
h: HousePtr;
x: ScreenXCoord ← firstLeftX;
faceType: dsD.FaceType;
width, totalWidth: CARDINAL;
lineNumber: CARDINAL ← firstLine;

hp ← NIL;
FOR i: CARDINAL IN [0 .. LENGTH[items]) DO
faceType ← IF items[i].type = text THEN plainFace ELSE boldFace;
totalWidth ← width ← dsD.GetStringWidth[items[i].text, faceType];
IF items[i].type = brackets THEN totalWidth ← width + preBracketSpacing + bracketWidth;
IF totalWidth + x > rightMargin THEN {lineNumber ← lineNumber + 1; x ← leftMargin};
IF items[i].rightFlush THEN x ← rightMargin - totalWidth;
IF hp = NIL THEN hp ← h ← allocateNode[SIZE[House]]
ELSE h ← h.nextHouse ← allocateNode[SIZE[House]];
h↑ ← House
[nextHouse: NIL,
lineNumber: lineNumber,
topY: 0,
bottomY: 0,
leftX: x,
rightX: x + width,
text: allocateString[items[i].text.length],
fixedEdge: left,
typeface: faceType,
needsConfirmation: items[i].needsConfirmation,
trackerIndicateDone: items[i].indicDone,
callable: items[i].type # text,
command: IF items[i].command = NIL THEN NullCommand ELSE items[i].command,
houseRefresher: TextHouseRefresher];
String.AppendString[h.text, items[i].text];
IF items[i].type = brackets THEN
BEGIN
h ← h.nextHouse ← allocateNode[SIZE[House]];
h↑ ← House
[nextHouse: NIL,
lineNumber: lineNumber,
topY: 0,
bottomY: 0,
leftX: x + width + preBracketSpacing,
rightX: IF items[i].endOfLine THEN rightMargin ELSE x + totalWidth,
text: allocateString[maxBracketStringLength],
fixedEdge: left,
typeface: plainFace,
needsConfirmation: FALSE,
trackerIndicateDone: TRUE,
callable: FALSE,
command: NullCommand,
houseRefresher: BracketsHouseRefresher];
String.AppendString[h.text, items[i].bracketsText];
END;
IF items[i].endOfLine THEN x ← rightMargin + 1
ELSE x ← x + totalWidth + interCommandSpacing;
ENDLOOP;
END; -- of CreateCommandHouses --


END. -- of EditorFiles --