SendMailControlImpl.mesa
Copyright Ó 1984, 1989, 1990, 1991, 1992, 1993 by Xerox Corporation. All rights reserved.
Willie-Sue, February 2, 1990 5:44:01 pm PST
Willie-s, April 26, 1993 6:33 pm PDT
Theimer, April 30, 1990 10:39 pm PDT
Contents: Control module for SendMail
Last Edited by: Willie-Sue, January 17, 1984 10:20:02 am PST
Last Edited by: Donahue, October 7, 1983 9:42 am
DIRECTORY
Atom USING [GetPName],
BasicTime USING [Now, Period],
Buttons USING [ButtonProc, Create],
Commander USING [CommandProc, Register],
Icons USING [IconFlavor, NewIconFromFile],
InputFocus USING [GetInputFocus],
List USING [Length, LORA],
MailSend USING [Abort, GetRegisteredSendProcsList, MailSendProcsRef],
MailSendSidedoor USING [AbortSendProc],
Menus USING [AppendMenuEntry, CopyEntry, CreateEntry, FindEntry, Menu, MenuEntry, MenuProc, MouseButton, ReplaceMenuEntry, targetNotFound],
IO,
PFS,
PFSNames,
PopUpSelection USING [Request],
Process USING [Detach, MsecToTicks, SetTimeout, Ticks],
Rope,
TEditInputOps USING [DoFindPlaceholders],
TEditSplit USING[ Split ],
TextEdit USING [GetNewlineDelimiter],
TextNode USING [Root],
Tioga,
TiogaAccess,
TiogaAccessViewers,
TiogaMenuOps USING[ tiogaMenu, Empty, Load, Store ],
TiogaOps USING [ Ref, Location, CancelSelection, GetSelection, IsComment, StepForward, ViewerDoc ],
TypeScript USING [Create],
ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc],
ViewerOps USING [AddProp, BlinkIcon, ComputeColumn, CreateViewer, DestroyViewer, FetchProp, MoveBelowViewer, OpenIcon, PaintViewer, SaveViewer, SetMenu, SetNewVersion],
ViewerIO USING [CreateViewerStreams, GetViewerFromStream],
ViewerTools USING [EnableUserEdits, GetSelectionContents, InhibitUserEdits, SelPos, SelPosRec, SetSelection, SetTiogaContents, TiogaContents, TiogaContentsRec],
ViewerClasses USING [Viewer],
UserProfile USING [Boolean, CallWhenProfileChanges, Number, ProfileChangedProc,
ListOfTokens, Token],
SendMailInternal,
SendMailOps;
SendMailControlImpl: CEDAR MONITOR
IMPORTS
Atom, BasicTime, Buttons, Commander, IO, List, PFS, PFSNames, PopUpSelection, Process, Rope, Icons, InputFocus, MailSend, Menus, SendMailInternal, SendMailOps, TEditInputOps, TEditSplit, TextNode, TextEdit, TiogaAccess, TiogaAccessViewers, TiogaMenuOps, TiogaOps, TypeScript, ViewerEvents, ViewerIO, ViewerOps, ViewerTools, UserProfile
EXPORTS
SendMailInternal, SendMailOps
SHARES ViewerClasses =
BEGIN OPEN SendMailOps, SendMailInternal;
-- Global types & variables
STREAM: TYPE = IO.STREAM;
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
TiogaContents: TYPE = ViewerTools.TiogaContents;
OutBoxMsg: TYPE = SendMailOps.OutBoxMsg;
viewerStart: ViewerTools.SelPos = NEW[ViewerTools.SelPosRec ¬ [0, 0]];
alwaysOpenSender: BOOL ¬ FALSE;
destroyAfterSend: BOOL ¬ FALSE;
senderList: LIST OF SenderInfo ¬ NIL;
popUpFormsList: LIST OF ROPE ¬ NIL;
popUpFormsFileNames: LIST OF ROPE ¬ NIL;
sendingSynch: CONDITION;
iconicSender: BOOL ¬ TRUE;  -- make iconic initially only
senderIcon: Icons.IconFlavor ¬ tool;
edittingIcon: Icons.IconFlavor ¬ tool;
notSentIcon: Icons.IconFlavor ¬ tool;
labelFont: PUBLIC ROPE ¬ NIL; -- font to use for printing of header labels
reporter: STREAM ¬ NIL;
senderTS: Viewer ¬ NIL;
senderEvent: ViewerEvents.EventRegistration;
defaultText: PUBLIC ViewerTools.TiogaContents;
answerText: PUBLIC ViewerTools.TiogaContents;
forwardText: PUBLIC ViewerTools.TiogaContents;
SenderPropRec: TYPE = RECORD[proc: PROC[Viewer, ATOM, REF ANY], data: REF ANY];
SenderPropValue: TYPE = REF SenderPropRec;
SenderProp: TYPE = RECORD[key: ATOM, value: SenderPropValue];
senderPropsList: LIST OF SenderProp ¬ NIL;
FileStatus: TYPE = {illegalName, local, remote, notFound, otherError};
************************************************************************
SendMailCmd: Commander.CommandProc = { [] ¬ CreateSendViewer[fromExec: TRUE]};
CreateSendViewer: PUBLIC PROC[fromExec: BOOL, transport: ATOM ¬ NIL]
RETURNS[which: ATOM ¬ NIL] = {
senderInfo: SenderInfo;
IF fromExec THEN fromExec ¬ ~alwaysOpenSender;
IF ~fromExec THEN iconicSender ¬ FALSE;
check for user name being set in defaultForm
[] ¬ CheckDefault[];
senderInfo ¬ BuildSender[
paint: TRUE, iconic: iconicSender, form: defaultForm, forceNew: alwaysNewSender];
IF ~iconicSender THEN GrabFocus[senderInfo.senderV];
iconicSender ¬ FALSE;  -- iconic only the first time
};
GrabFocus: PUBLIC ENTRY PROC[senderV: Viewer] = {
ENABLE UNWIND => NULL;
IF senderV.iconic THEN RETURN;
ViewerTools.SetSelection[senderV, viewerStart];
[] ¬ TEditInputOps.DoFindPlaceholders[next: TRUE, gotoend: FALSE];
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
BuildSendViewer: PUBLIC PROC[paint, iconic: BOOL, form: SendMailOps.Form ¬ NIL,
who: Viewer ¬ NIL, forceNew: BOOL ¬ FALSE, transport: ATOM ¬ NIL]
RETURNS[v: Viewer, which: ATOM ¬ NIL] = {
senderInfo: SenderInfo;
newForm: SendMailOps.Form = IF form # NIL THEN form ELSE CheckDefault[];
senderInfo ¬ BuildSender[paint, iconic, newForm, who, forceNew, transport];
RETURN[senderInfo.senderV, which];
};
CheckDefault: PROC RETURNS[form: SendMailOps.Form] = {
be paranoid here
IF simpleUserNameList = NIL THEN SendMailInit[firstTime]; -- check for login
IF defaultForm.fields = NIL THEN RETURN;
IF defaultForm.fields.rest = NIL THEN RETURN;
IF defaultForm.fields.rest.rest = NIL THEN RETURN;
IF defaultForm.fields.rest.rest.first = NIL THEN
IF simpleUserNameList = NIL THEN SendMailInit[firstTime]; -- fixes up defaultForm
RETURN[defaultForm];
};
BuildSender: ENTRY PROC[paint, iconic: BOOL, form: SendMailOps.Form,
who: Viewer ¬ NIL, forceNew: BOOL ¬ FALSE, transport: ATOM ¬ NIL]
RETURNS[senderInfo: SenderInfo] = {
ENABLE UNWIND => IF senderInfo # NIL AND senderInfo.senderV # NIL
THEN senderInfo.senderV.inhibitDestroy ¬ FALSE;
senderV: Viewer;
form1: SendMailOps.Form ¬ form;
trx: ATOM ¬ transport;
IF ~forceNew THEN
FOR sL: LIST OF SenderInfo ¬ senderList, sL.rest UNTIL sL=NIL DO
senderInfo ¬ sL.first;
IF ~(senderInfo.senderV.newVersion) AND (senderInfo.sendHandle = NIL)
AND ~senderInfo.senderV.destroyed AND (senderInfo.senderV.file = NIL )
THEN
{ senderV ¬ senderInfo.senderV;
IF senderV.link # NIL THEN InternalDestroySplits[senderV];
senderV.inhibitDestroy ¬ TRUE;
IF TiogaOps.GetSelection[feedback].viewer = senderV THEN
 TiogaOps.CancelSelection[feedback];
IF senderV.iconic THEN {  -- inlined a call to ClearOpenEvent
ra: REF ANY ¬ ViewerOps.FetchProp[senderV, $SendMailOpsForm];
IF ra # NIL THEN {
form: SendMailOps.Form = NARROW[ra];
ViewerEvents.UnRegisterEventProc[senderInfo.openEvent, open];
senderInfo.openEvent ¬ NIL;
};
IF (who # NIL) AND (senderV.column = who.column) THEN
{ ViewerOps.OpenIcon[icon: senderV, paint: FALSE];
ViewerOps.MoveBelowViewer[altered: senderV, static: who, paint: TRUE] }
ELSE ViewerOps.OpenIcon[senderV];
};
IF form # NIL THEN InsertForm[senderInfo, form, TRUE]
ELSE TiogaMenuOps.Empty[senderV];
ViewerOps.PaintViewer[senderV, menu];
senderV.inhibitDestroy ¬ FALSE;
RETURN };
ENDLOOP;
-- need to create a new sender
senderV ¬ ViewerOps.CreateViewer[
flavor: $Text,
info: [ name: sendCaption,
 column: IF who#NIL THEN who.column ELSE left,
 iconic: iconic,
 icon: senderIcon],
paint: who = NIL];
IF who # NIL THEN {
ViewerOps.MoveBelowViewer[altered: senderV, static: who, paint: FALSE];
ViewerOps.ComputeColumn[column: senderV.column, paint: TRUE]
};
senderV.inhibitDestroy ¬ TRUE;
senderInfo ¬ NEW[SenderInfoObject ¬ [senderV: senderV]];
TRUSTED { Process.SetTimeout[@senderInfo.timeToWait, halfSecTicks] };
HowCanWeSend[senderInfo, transport];
ViewerOps.SetMenu[senderV, senderInfo.thisMenu];
ViewerOps.AddProp[senderV, $SenderInfo, senderInfo];
FOR spl: LIST OF SenderProp ¬ senderPropsList, spl.rest UNTIL spl=NIL DO
ViewerOps.AddProp[senderV, spl.first.key, spl.first.value];
ENDLOOP;
senderList ¬ CONS[senderInfo, senderList];
senderInfo.destroyEvent ¬ ViewerEvents.RegisterEventProc[
proc: DestroySendViewer, event: destroy, filter: senderV, before: TRUE];
senderInfo.closeEvent ¬ ViewerEvents.RegisterEventProc[
proc: CloseSendViewer, event: close, filter: senderV, before: TRUE];
senderInfo.editEvent ¬ ViewerEvents.RegisterEventProc[
proc: FirstEditSendViewer, event: edit, filter: senderV, before: TRUE];
IF form # NIL THEN InsertForm[senderInfo, form, TRUE] ELSE
TiogaMenuOps.Empty[senderV];
ViewerOps.PaintViewer[senderV, menu]; -- shouldn't be necessary
senderV.inhibitDestroy ¬ FALSE;
IF iconic AND ~senderV.iconic THEN ViewerOps.PaintViewer[senderV, all];
};
HowCanWeSend: PROC[senderInfo: SenderInfo, transport: ATOM] = {
let's sidestep this issue for now
senderInfo.thisMenu ¬ sendMenu;
};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
SendMailInit: UserProfile.ProfileChangedProc = {
defaultFormFile: ROPE ¬ UserProfile.Token[key: "SendMailTool.DefaultForm", default: NIL];
answerFormFile: ROPE ¬ UserProfile.Token[key: "SendMailTool.AnswerForm", default: NIL];
forwardFormFile: ROPE ¬ UserProfile.Token[key: "SendMailTool.ForwardForm", default: NIL];
formContents: ViewerTools.TiogaContents ¬ NIL;
isDefault: BOOL ¬ TRUE;
defaultFields: LIST OF ROPE ¬ NIL;
DoUserNameAndRegistry[];
IF localRNameList # NIL THEN defaultFields ¬ LIST[NIL, NIL, localRNameList.first];
needToAuthenticate ¬ TRUE;
outBoxLength ¬ UserProfile.Number[key: "SendMailTool.OutBoxLength", default: 1];
replyToSelf ¬ UserProfile.Boolean[key: "SendMailTool.ReplyToSelf", default: FALSE];
destroyAfterSend ¬ UserProfile.Boolean[key: "SendMailTool.DestroyAfterSend", default: FALSE];
alwaysNewSender ¬
UserProfile.Boolean[key: "SendMailTool.AlwaysNewSender", default: FALSE];
alwaysOpenSender ¬
UserProfile.Boolean[key: "SendMailTool.AlwaysOpenSender", default: FALSE];
IF defaultFormFile = NIL THEN defaultFormFile ¬ Rope.Concat[initDir, "SendMailStandardSend.form"]
ELSE isDefault ¬ FALSE;
formContents ¬ GetSendForm[defaultFormFile];
IF formContents # NIL THEN {
defaultText ¬ defaultForm.formText ¬ formContents;
defaultForm.fields ¬ IF isDefault THEN defaultFields ELSE NIL;
}
ELSE {
defaultText ¬ defaultForm.formText ¬ standardDefaultText;
defaultForm.fields ¬ defaultFields;
};
formContents ¬ NIL;
IF answerFormFile = NIL THEN answerFormFile ¬ Rope.Concat[initDir, "SendMailStandardAnswer.form"];
formContents ¬ GetSendForm[answerFormFile];
IF formContents # NIL THEN answerText ¬ formContents
ELSE answerText ¬ standardAnswerText;
formContents ¬ NIL;
IF forwardFormFile = NIL THEN forwardFormFile ¬ Rope.Concat[initDir, "SendMailStandardForward.form"];
formContents ¬ GetSendForm[forwardFormFile];
IF formContents # NIL THEN forwardText ¬ formContents
ELSE forwardText ¬ standardForwardText;
labelFont ¬ UserProfile.Token[key: "SendMailTool.MsgHeadersLooks", default: "sb"];
SetFormsList[];
};
DisplayTxtInSender: ENTRY PROC[senderV: Viewer, txt: ROPE] = {
ENABLE UNWIND => NULL;
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[senderV, $SenderInfo]];
tc: ViewerTools.TiogaContents ¬ NEW[ViewerTools.TiogaContentsRec];
tc.contents ¬ txt;
InternalDisplayTioga[senderInfo, tc, TRUE];
};
ClearSendViewer: PUBLIC ENTRY PROC[senderV: Viewer] = {
ENABLE UNWIND => NULL;
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[senderV, $SenderInfo]];
IF senderInfo = NIL THEN RETURN;  -- not a sender
InsertForm[senderInfo, defaultForm, TRUE]
};
StuffForm: PUBLIC ENTRY PROC[v: Viewer, contents: TiogaContents] = {
ENABLE UNWIND => NULL;
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[v, $SenderInfo]];
IF senderInfo = NIL THEN RETURN;  -- not a sender
InternalDisplayTioga[senderInfo, contents, TRUE]
};
AddToSenderProps: PUBLIC ENTRY PROC
[key: ATOM, proc: PROC[Viewer, ATOM, REF ANY], data: REF ANY] = {
ENABLE UNWIND => NULL;
senderPropsList ¬
CONS[[key: key, value: NEW[SenderPropRec ¬ [proc, data]]], senderPropsList];
};
RemoveFromSenderProps: PUBLIC ENTRY PROC[key: ATOM] = {
ENABLE UNWIND => NULL;
sp: LIST OF SenderProp;
IF senderPropsList = NIL THEN RETURN;
FOR spl: LIST OF SenderProp ¬ senderPropsList, spl.rest UNTIL spl = NIL DO
IF spl.first.key = key THEN
IF spl = senderPropsList THEN
{ senderPropsList ¬ senderPropsList.rest; RETURN}
ELSE
{ sp.rest ¬ spl.rest; RETURN};
sp ¬ spl;
ENDLOOP;
};
AddToSendMenu: PUBLIC ENTRY PROC[label: ROPE, proc: Menus.MenuProc] = {
ENABLE UNWIND => NULL;
old: Menus.MenuEntry ¬ Menus.FindEntry[sendMenu, label];
IF old = NIL THEN
Menus.AppendMenuEntry[sendMenu, Menus.CreateEntry[label, proc]];
FOR sL: LIST OF SenderInfo ¬ senderList, sL.rest UNTIL sL = NIL DO
thisMenu: Menus.Menu;
v: Viewer = sL.first.senderV;
IF v = NIL OR v.destroyed THEN LOOP;
old ¬ Menus.FindEntry[thisMenu ¬ sL.first.senderV.menu, label];
IF old = NIL THEN {
Menus.AppendMenuEntry[thisMenu, Menus.CreateEntry[label, proc]];
ViewerOps.PaintViewer[sL.first.senderV, menu];
};
ENDLOOP;
};
ReplaceInSendMenu: PUBLIC ENTRY PROC[label: ROPE, proc: Menus.MenuProc]
RETURNS[oldFound: BOOL] = {
ENABLE UNWIND => NULL;
DoReplace: PROC[thisMenu: Menus.Menu] RETURNS[foundIt: BOOL ¬ TRUE]= {
old: Menus.MenuEntry = Menus.FindEntry[thisMenu, label];
new: Menus.MenuEntry = Menus.CreateEntry[label, proc];
Menus.ReplaceMenuEntry[thisMenu, old, new ! Menus.targetNotFound =>
{ foundIt ¬ FALSE ; CONTINUE }];
IF NOT foundIt THEN Menus.AppendMenuEntry[thisMenu, new];
};
oldFound ¬ DoReplace[sendMenu];
FOR sL: LIST OF SenderInfo ¬ senderList, sL.rest UNTIL sL = NIL DO
v: Viewer = sL.first.senderV;
thisFound: BOOL;
IF v = NIL OR v.destroyed THEN LOOP;
thisFound ¬ DoReplace[sL.first.senderV.menu];
oldFound ¬ thisFound AND oldFound;
ViewerOps.PaintViewer[sL.first.senderV, menu];
ENDLOOP;
};
RemoveFromSendMenu: PUBLIC ENTRY PROC[name: ROPE] = {
removes the named button from the sender menu, if such a button exists; no errors
ENABLE UNWIND => NULL;
old: Menus.MenuEntry ¬ Menus.FindEntry[sendMenu, name];
IF old = NIL THEN RETURN;
Menus.ReplaceMenuEntry[sendMenu, old ! Menus.targetNotFound => CONTINUE];
FOR sL: LIST OF SenderInfo ¬ senderList, sL.rest UNTIL sL = NIL DO
thisMenu: Menus.Menu;
v: Viewer = sL.first.senderV;
IF v = NIL OR v.destroyed THEN LOOP;
old ¬ Menus.FindEntry[thisMenu ¬ sL.first.senderV.menu, name];
IF old # NIL THEN {
Menus.ReplaceMenuEntry[thisMenu, old ! Menus.targetNotFound => CONTINUE];
ViewerOps.PaintViewer[sL.first.senderV, menu];
};
ENDLOOP;
};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- Menu procedures
NewMsgFormProc: ENTRY PROC[viewer: Viewer] = {
ENABLE UNWIND => NULL;
NewMsgForm[viewer]
};
PrevMsgProc: ENTRY PROC[viewer: Viewer, blueButton: BOOL] = {
ENABLE UNWIND => NULL;
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
tc: TiogaContents;
IF viewer.link#NIL THEN InternalDestroySplits[viewer];
ClearFileAssoc[viewer];
[] ¬ CheckDefault[];
IF outBox = NIL THEN InsertForm[senderInfo, defaultForm, TRUE]
ELSE {
IF outBox.next = NIL THEN tc ¬ IF blueButton THEN outBox.sentTC ELSE outBox.replaceTC
ELSE { -- more than one previous message
which: INT;
obm: OutBoxMsg ¬ outBox;
outBoxList: LIST OF ROPE ¬ LIST[outBox.subject];
last: LIST OF ROPE ¬ outBoxList;
FOR oB: OutBoxMsg ¬ outBox.next, oB.next UNTIL oB = NIL DO
last.rest ¬ LIST[oB.subject];
last ¬ last.rest;
ENDLOOP;
which ¬ PopUpSelection.Request[header: "OutBox", choice: outBoxList, position: $Mouse];
IF which = 0 THEN RETURN;
last ¬ outBoxList;
FOR i: INT IN [1..which) DO
obm ¬ obm.next;
ENDLOOP;
tc ¬ IF blueButton THEN obm.sentTC ELSE obm.replaceTC;
};
IF TiogaOps.GetSelection[feedback].viewer = viewer THEN
TiogaOps.CancelSelection[feedback];
ViewerTools.SetTiogaContents[viewer, tc];
UnsetNewVersion[viewer];
};
};
NewSenderClickProc: Buttons.ButtonProc = {
senderInfo: SenderInfo;
[] ¬ CheckDefault[];
senderInfo ¬ BuildSender[paint: TRUE, iconic: FALSE, form: defaultForm, forceNew: TRUE];
GrabFocus[senderInfo.senderV];
};
NewSenderProc: PROC = {
senderInfo: SenderInfo;
[] ¬ CheckDefault[];
senderInfo ¬ BuildSender[paint: TRUE, iconic: FALSE, form: defaultForm, forceNew: TRUE];
GrabFocus[senderInfo.senderV];
};
NewMsgForm: INTERNAL PROC[viewer: Viewer] = {
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
[] ¬ CheckDefault[];
InsertForm[senderInfo, defaultForm, TRUE];
ClearFileAssoc[viewer];
};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DestroySendViewer: ViewerEvents.EventProc = {
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN[FALSE];
IF senderInfo.senderV # NIL THEN senderInfo.senderV.file ¬ NIL;
make sure that there is no file pointed to so that you don't get something saved in UnSavedDocumentCache
IF ViewerOps.FetchProp[viewer, $DestroySenderProc] = NIL THEN RETURN[FALSE];
TRUSTED {Process.Detach[FORK CheckForDestroy[viewer, senderInfo]]};
RETURN[TRUE];
};
EntryInsertForm: PUBLIC ENTRY PROC[senderInfo: SenderInfo, form: SendMailOps.Form] =
{ ENABLE UNWIND => NULL; InsertForm[senderInfo, form, TRUE] };
EntryPlaceHolders: PUBLIC ENTRY PROC[senderInfo: SenderInfo, fieldsList: LIST OF ROPE] =
{ ENABLE UNWIND => NULL; DoPlaceHolders[senderInfo.senderV, fieldsList] };
CloseSendViewer: ViewerEvents.EventProc = {
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN[FALSE];
IF senderInfo.dontClose THEN
{ SenderReport[format: " **** Sender cannot be closed right now\n", blink: TRUE];
RETURN[TRUE]
};
viewer.icon ¬ IF viewer.newVersion THEN edittingIcon ELSE senderIcon;
RETURN[FALSE];
};
FirstEditSendViewer: ViewerEvents.EventProc = {
OPEN Menus;
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN[FALSE];
SenderNewVersion[viewer];  -- show as guarded
RETURN[FALSE];
};
CheckForDestroy: ENTRY PROC[senderV: Viewer, senderInfo: SenderInfo] = {
ENABLE UNWIND => NULL;
okToDestroy: BOOL ¬ TRUE;
IF senderV.destroyed THEN RETURN;  -- woops
IF senderV.newVersion THEN
{InternalReport["\nConfirm quitting with last message unsent"];
IF okToDestroy ¬ SendingConfirm[senderInfo] THEN senderV.newVersion ¬ FALSE;
};
IF ~okToDestroy THEN RETURN;
IF senderInfo.destroyEvent # NIL THEN
ViewerEvents.UnRegisterEventProc[senderInfo.destroyEvent, destroy];
IF senderInfo.closeEvent # NIL THEN
ViewerEvents.UnRegisterEventProc[senderInfo.closeEvent, close];
IF senderInfo.editEvent # NIL THEN
ViewerEvents.UnRegisterEventProc[senderInfo.editEvent, edit];
IF senderInfo.openEvent # NIL THEN
ViewerEvents.UnRegisterEventProc[senderInfo.openEvent, open];
IF senderInfo.focusEvent # NIL THEN
ViewerEvents.UnRegisterEventProc[senderInfo.focusEvent, setInputFocus];
senderInfo.destroyEvent ¬ NIL;
senderInfo.closeEvent ¬ NIL;
senderInfo.editEvent ¬ NIL;
senderInfo.openEvent ¬ NIL;
senderInfo.focusEvent ¬ NIL;
should take senderInfo off of chain of senderInfo's, but why bother?
TRUSTED {Process.Detach[FORK ViewerOps.DestroyViewer[senderV]]};
};
LockViewerOpen: PUBLIC ENTRY PROC[viewer: Viewer] = {
ENABLE UNWIND => NULL;
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN;
senderInfo.dontClose ¬ TRUE;
};
ReleaseOpenViewer: PUBLIC ENTRY PROC[viewer: Viewer] = {
ENABLE UNWIND => NULL;
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN;
senderInfo.dontClose ¬ FALSE;
};
SenderReport: PUBLIC ENTRY PROC[
format: ROPE, v1, v2,v3: IO.Value ¬ [null[]], blink: BOOL ¬ FALSE] = {
ENABLE UNWIND => NULL;
InternalReport[format, v1, v2, v3, blink]
};
InternalReport: INTERNAL PROC[
format: ROPE, v1, v2,v3: IO.Value ¬ [null[]], blink: BOOL ¬ FALSE] = {
done: BOOL ¬ FALSE;
tries: INT ¬ 0;
ReallyReport: PROC = {
IF reporter = NIL THEN {
senderTS ¬ TypeScript.Create[
info: [name: "SendMail", iconic: FALSE, column: right, openHeight: 64]];
reporter ¬ ViewerIO.CreateViewerStreams[NIL, senderTS].out;
senderEvent ¬ ViewerEvents.RegisterEventProc[
proc: DestroyReporter, event: destroy, filter: senderTS, before: TRUE];
};
IF blink THEN
{ viewer: Viewer = ViewerIO.GetViewerFromStream[reporter]; -- NIL for $Dribble class
IF viewer # NIL THEN ViewerOps.BlinkIcon[viewer];
};
reporter.PutF[format, v1, v2, v3];
done ¬ TRUE;
};
DO
ReallyReport[ ! IO.Error => IF ec = StreamClosed THEN {
reporter ¬ NIL;
IF ( tries ¬ tries + 1) > 2 THEN REJECT;
CONTINUE } ];
IF done THEN RETURN;
ENDLOOP;
};
ReporterClump: TYPE = RECORD [reporters: SEQUENCE count: CARD16 OF STREAM];
maxReporters: CARD16 ¬ 16;
registeredReporters: REF ReporterClump ¬ NEW[ReporterClump[maxReporters]];
DestroyReporter: ENTRY ViewerEvents.EventProc = {
ENABLE UNWIND => NULL;
IF senderEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderEvent, destroy];
senderEvent ¬ NIL;
senderTS ¬ NIL;
FOR i: CARD16 IN [0..maxReporters) DO
IF registeredReporters[i] # reporter THEN LOOP;
registeredReporters[i] ¬ NIL;
EXIT;
ENDLOOP;
reporter ¬ NIL;
RETURN[FALSE];
};
RegisterReporter: PUBLIC ENTRY PROC[strm: STREAM] = {
IF senderTS # NIL THEN
{ reporter.Close[];
ViewerEvents.UnRegisterEventProc[senderEvent, destroy];
senderEvent ¬ NIL;
ViewerOps.DestroyViewer[senderTS];
senderTS ¬ NIL;
reporter ¬ NIL;
};
IF reporter = NIL THEN reporter ¬ strm;
FOR i: CARD16 IN [0.. maxReporters) DO
IF registeredReporters[i] = strm THEN EXIT;
IF registeredReporters[i] # NIL THEN LOOP;
registeredReporters[i] ¬ strm;
EXIT;
ENDLOOP;
};
UnregisterReporter: PUBLIC ENTRY PROC[strm: STREAM] = {
ENABLE UNWIND => NULL;
IF reporter = strm THEN reporter ¬ NIL;
FOR i: CARD16 IN [0.. maxReporters) DO
IF registeredReporters[i] # strm THEN LOOP;
registeredReporters[i] ¬ NIL;
ENDLOOP;
IF reporter = NIL THEN
FOR i: CARD16 IN [0.. maxReporters) DO
IF registeredReporters[i] = NIL THEN LOOP;
reporter ¬ registeredReporters[i];
EXIT;
ENDLOOP;
};
DenySendProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
viewer: Viewer ¬ NARROW[parent];
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.confirmed ¬ FALSE;
senderInfo.userResponded ¬ TRUE;
BROADCAST sendingSynch;
};
ConfirmSendProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
viewer: Viewer ¬ NARROW[parent];
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.confirmed ¬ TRUE;
senderInfo.userResponded ¬ TRUE;
BROADCAST sendingSynch;
};
AbortSendProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
viewer: Viewer ¬ NARROW[parent];
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
ViewerOps.SetMenu[viewer, blankMenu];
senderInfo.aborted ¬ TRUE;
};
ReplyToSelfProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
viewer: Viewer ¬ NARROW[parent];
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.replyToResponse ¬ self;
senderInfo.userResponded ¬ TRUE;
BROADCAST sendingSynch;
};
ReplyToAllProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
viewer: Viewer ¬ NARROW[parent];
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.replyToResponse ¬ all;
senderInfo.userResponded ¬ TRUE;
BROADCAST sendingSynch;
};
ReplyToCancelProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
viewer: Viewer ¬ NARROW[parent];
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.replyToResponse ¬ cancel;
senderInfo.userResponded ¬ TRUE;
BROADCAST sendingSynch;
};
ReplyToResponse: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS [HowToReplyTo] = {
ENABLE UNWIND => NULL;
senderV: Viewer ¬ senderInfo.senderV;
ViewerOps.BlinkIcon[senderV, IF senderV.iconic THEN 0 ELSE 1];
UNTIL senderInfo.userResponded DO WAIT sendingSynch; ENDLOOP;
senderInfo.userResponded ¬ FALSE;
RETURN[senderInfo.replyToResponse];
};
CheckForAbortSend: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS[BOOL] = {
ENABLE UNWIND => NULL;
IF senderInfo.aborted THEN {
InternalReport[" Aborting delivery ...\n"];
IF senderInfo.sendHandle#NIL THEN MailSend.Abort[senderInfo.sendHandle];
IF senderInfo.aborted THEN senderInfo.senderV.inhibitDestroy ¬ FALSE;
};
RETURN[senderInfo.aborted];
};
halfSecTicks: Process.Ticks = Process.MsecToTicks[500];
SenderAbortProc: PUBLIC ENTRY MailSendSidedoor.AbortSendProc = {
ENABLE UNWIND => NULL;
FOR sL: LIST OF SenderInfo ¬ senderList, sL.rest UNTIL sL = NIL DO
elapsed, waitTime: INT;
IF sL.first.sendHandle # msH THEN LOOP;
IF NOT lastTime THEN RETURN[sL.first.aborted];
IF ( elapsed ¬ BasicTime.Period[sL.first.startSendTime, BasicTime.Now[]] ) <
( waitTime ¬ UserProfile.Number["SendMailTool.SendWaitSeconds", 8] ) THEN {
IF elapsed < 0 THEN RETURN[sL.first.aborted]; -- strange
FOR i: INT IN [0..2*(waitTime-elapsed)) DO
WAIT sL.first.timeToWait;
IF sL.first.aborted THEN RETURN[TRUE];
ENDLOOP;
ViewerOps.SetMenu[sL.first.senderV, blankMenu];
WAIT sL.first.timeToWait; -- 1/2 sec more
RETURN[sL.first.aborted];
};
ENDLOOP;
RETURN[FALSE];  -- strange but don't argue
};
Confirmation: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS [BOOL] =
{ ENABLE UNWIND => NULL; RETURN[SendingConfirm[senderInfo]]};
SendingConfirm: INTERNAL PROC[senderInfo: SenderInfo] RETURNS [BOOL] = {
senderV: Viewer ¬ senderInfo.senderV;
oldM: Menus.Menu ¬ ChangeMenu[senderV, confirmMenu];
ViewerOps.BlinkIcon[senderV, IF senderV.iconic THEN 0 ELSE 1];
UNTIL senderInfo.userResponded DO WAIT sendingSynch; ENDLOOP;
ViewerOps.SetMenu[senderV, oldM];
senderInfo.userResponded ¬ FALSE;
RETURN[senderInfo.confirmed];
};
ChangeMenu: PROC[v: Viewer, menu: Menus.Menu] RETURNS [oldM: Menus.Menu] = {
oldM ¬ v.menu;
ViewerOps.SetMenu[v, menu];
};
MessageSendProc: PUBLIC Menus.MenuProc = {
self: Viewer ¬ NARROW[parent];
transport: ATOM ¬ NARROW[clientData];
iHadFocus: BOOL ¬ InputFocus.GetInputFocus[].owner = self;
ViewerTools.InhibitUserEdits[self];
IF self.link#NIL THEN DestroySplits[self];
IF ( ( transport = $xns ) AND control ) THEN transport ¬ $newXNS;
TRUSTED { Process.Detach[FORK SendProc[self, mouseButton, iHadFocus, transport, TRUE]] };
};
MessageUSendProc: Menus.MenuProc = {
self: Viewer ¬ NARROW[parent];
transport: ATOM ¬ NARROW[clientData];
iHadFocus: BOOL ¬ InputFocus.GetInputFocus[].owner = self;
ViewerTools.InhibitUserEdits[self];
IF self.link#NIL THEN DestroySplits[self];
TRUSTED
{ Process.Detach[FORK SendProc[self, mouseButton, iHadFocus, transport, FALSE]] };
};
MessageSendCheckedProc: Menus.MenuProc = {
self: Viewer ¬ NARROW[parent];
transport: ATOM ¬ NARROW[clientData];
iHadFocus: BOOL ¬ InputFocus.GetInputFocus[].owner = self;
hasComments: BOOL ¬ FALSE;
ViewerTools.InhibitUserEdits[self];
check if any comment nodes, and alert user if so
FOR next: TiogaOps.Ref ¬ TiogaOps.ViewerDoc[self], TiogaOps.StepForward[next]
UNTIL next = NIL DO
IF hasComments ¬ TiogaOps.IsComment[next] THEN EXIT;
ENDLOOP;
IF hasComments THEN {
TRUSTED { Process.Detach[FORK CheckProc[self, mouseButton, iHadFocus, transport]] };
RETURN;
};
IF self.link#NIL THEN DestroySplits[self];
TRUSTED {Process.Detach[FORK SendProc[self, mouseButton, iHadFocus, transport, TRUE]]};
};
lf: CHAR ~ '\l;
cr: CHAR ~ '\r;
ForceCRLFProc: Menus.MenuProc = {
self: Viewer ~ NARROW[parent];
input: TiogaAccess.Reader ¬ TiogaAccessViewers.FromViewer[self];
output: TiogaAccess.Writer ¬ TiogaAccess.Create[];
root: Tioga.Node ~ TextNode.Root[TiogaAccess.GetLocation[input].node];
for now, we assume single character newline delimiter - beware crlf
changeTo: CHAR ~ Rope.Fetch[TextEdit.GetNewlineDelimiter[root], 0];
assume cr and lf are the only values
changeFrom: CHAR ~ IF changeTo = lf THEN cr ELSE lf;
UNTIL input.EndOf[] DO
char: TiogaAccess.TiogaChar ¬ input.Get[];
IF char.charSet = 0 THEN IF char.char = changeFrom THEN char.char ¬ changeTo;
output.Put[char];
ENDLOOP;
TiogaAccess.DoneWith[input];
TiogaAccessViewers.WriteViewer[output, self];
SendMailInternal.SenderNewVersion[self]; -- show as editted
ViewerOps.PaintViewer[self, caption];
};
CheckProc: PROC[self: Viewer, mouseButton: Menus.MouseButton,
iHadFocus: BOOL, transport: ATOM] = {
senderInfo: SenderInfo = NARROW[ViewerOps.FetchProp[self, $SenderInfo]];
SenderReport["\n*** Message to be sent contains comment nodes - are you sure you want to send it?"];
IF ~Confirmation[senderInfo] THEN {
SenderReport[" Message NOT sent"];
ViewerTools.EnableUserEdits[self];
RETURN;
};
IF self.link#NIL THEN DestroySplits[self];
SendProc[self, mouseButton, iHadFocus, transport, TRUE];
};
SendProc: PROC[viewer: Viewer, mouseButton: Menus.MouseButton,
iHadFocus: BOOL, transport: ATOM, sendFormatted: BOOL] = {
OPEN ViewerOps;
sendOK: BOOL;
senderInfo: SenderInfo = NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
viewer.newVersion ¬ TRUE;
viewer.name ¬ "Sending ...";
PaintViewer[viewer, caption];
viewer.file ¬ NIL;
IF sendFormatted THEN
sendOK ¬ SendMailOps.Send[v: viewer, doClose: mouseButton=blue, transport: transport]
ELSE sendOK ¬ SendMailOps.SendUnformatted[v: viewer, doClose: mouseButton=blue, transport: transport];
IF ~sendOK THEN SenderNewVersion[viewer];
viewer.name ¬ viewer.label ¬ sendCaption;
viewer.icon ¬ IF sendOK THEN senderIcon ELSE notSentIcon;
PaintViewer[viewer, caption];
ViewerTools.EnableUserEdits[viewer];
IF sendOK THEN {
AfterSendInsert: ENTRY PROC =
{ ENABLE UNWIND => NULL; InsertForm[senderInfo, defaultForm, FALSE] };
IF mouseButton=blue AND destroyAfterSend THEN {DestroyViewer[viewer]; RETURN};
UnsetNewVersion[viewer];
AfterSendInsert[];
};
};
SenderSplitProc: Menus.MenuProc = {
self: Viewer = NARROW[parent];
newV: Viewer;
nsI, senderInfo: SenderInfo;
TEditSplit.Split[self];
now find the newest link in the chain to copy properties
FOR newV ¬ self.link, newV.link UNTIL newV.link = self DO ENDLOOP;
senderInfo ¬ NARROW[ViewerOps.FetchProp[self, $SenderInfo]];
ViewerOps.AddProp[newV, $SenderInfo, nsI ¬ NEW[SenderInfoObject]];
nsI.senderV ¬ newV;
nsI.sendingTransports ¬ senderInfo.sendingTransports;
nsI.thisMenu ¬ senderInfo.thisMenu;
nsI.replyToResponse ¬ senderInfo.replyToResponse;
nsI.numCharsToDelete ¬ senderInfo.numCharsToDelete;
nsI.currentOutBox ¬ senderInfo.currentOutBox;
nsI.dontClose ¬ senderInfo.dontClose;
nsI.successfullySent ¬ senderInfo.successfullySent;
nsI.userResponded ¬ senderInfo.userResponded;
nsI.confirmed ¬ senderInfo.confirmed;
nsI.aborted ¬ senderInfo.aborted;
nsI.startSendTime ¬ senderInfo.startSendTime;
nsI.validateFlag ¬ senderInfo.validateFlag;
nsI.sendHandle ¬ senderInfo.sendHandle;
TRUSTED { Process.SetTimeout[@nsI.timeToWait, halfSecTicks] };
nsI.destroyEvent ¬ ViewerEvents.RegisterEventProc[
 proc: DestroySendViewer, event: destroy, filter: newV, before: TRUE];
nsI.closeEvent ¬ ViewerEvents.RegisterEventProc[
 proc: CloseSendViewer, event: close, filter: newV, before: TRUE];
nsI.editEvent ¬ ViewerEvents.RegisterEventProc[
 proc: FirstEditSendViewer, event: edit, filter: newV, before: TRUE];
newV.icon ¬ self.icon
};
DestroySplits: ENTRY PROC[keepThisOne: Viewer] =
{ ENABLE UNWIND => NULL; InternalDestroySplits[keepThisOne] };
InternalDestroySplits: PUBLIC PROC[keepThisOne: Viewer] = {
next: Viewer ¬ keepThisOne.link;
next2: Viewer;
sI: SenderInfo;
DO
IF next = keepThisOne THEN EXIT;
sI ¬ NARROW[ViewerOps.FetchProp[next, $SenderInfo]];
IF sI # NIL THEN {
IF sI.destroyEvent # NIL THEN
 ViewerEvents.UnRegisterEventProc[sI.destroyEvent, destroy];
IF sI.closeEvent # NIL THEN ViewerEvents.UnRegisterEventProc[sI.closeEvent, close];
IF sI.editEvent # NIL THEN ViewerEvents.UnRegisterEventProc[sI.editEvent, edit];
sI.destroyEvent ¬ NIL;
sI.closeEvent ¬ NIL;
sI.editEvent ¬ NIL;
};
next2 ¬ next.link;
IF sI # NIL THEN ViewerOps.DestroyViewer[next];  -- DON'T FORK here
next ¬ next2;
ENDLOOP;
-- make sure this sender is on the list of senders
FOR sL: LIST OF SenderInfo ¬ senderList, sL.rest UNTIL sL=NIL DO
IF sL.first.senderV = keepThisOne THEN RETURN;
ENDLOOP;
sI ¬ NARROW[ViewerOps.FetchProp[keepThisOne, $SenderInfo]];
IF sI = NIL THEN RETURN;
senderList ¬ CONS[sI, senderList];
};
SenderPlacesProc: Menus.MenuProc =
Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Places" ] ].proc;
SenderLevelsProc: Menus.MenuProc =
Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Levels" ] ].proc;
SenderGetFormProc: ENTRY PROC[self: Viewer, filename: ROPE] = {
ENABLE UNWIND => NULL;
fileStatus: FileStatus;
exp, fName: ROPE;
IF filename.Length[] = 0 THEN RETURN;
[fileStatus, exp, fName] ¬ CheckForFile[filename];
IF fileStatus = local OR fileStatus = remote THEN {
tc: ViewerTools.TiogaContents ¬ GetSendForm[fName];
senderInfo: SenderInfo ¬ NARROW[ViewerOps.FetchProp[self, $SenderInfo]];
InternalDisplayTioga[senderInfo, tc, TRUE];
ClearFileAssoc[self];
}
ELSE ErrorReport[exp];
};
SenderGetFileProc: Menus.MenuProc = {
self: Viewer = NARROW[parent];
filename: ROPE ¬ ViewerTools.GetSelectionContents[];
IF filename.Length[] = 0 THEN RETURN;
LoadFile[self, filename];
};
SenderFormsProc: Menus.MenuProc = {
viewer: Viewer = NARROW[parent];
which: INT;
sel: LIST OF ROPE ¬ popUpFormsFileNames;
blueButton: BOOL = mouseButton = blue;
IF popUpFormsList = NIL THEN RETURN;
which ¬ PopUpSelection.Request["Forms", popUpFormsList];
SELECT which FROM
<= 0 => RETURN;    -- no selection
1 => { NewSenderProc[]; RETURN };
2 => { NewMsgFormProc[viewer]; RETURN };
3 => { PrevMsgProc[viewer, blueButton]; RETURN };
ENDCASE => which ¬ which - 3;
FOR i: INT IN [1..which) DO
sel ¬ sel.rest;
ENDLOOP;
SenderGetFormProc[viewer, sel.first];
};
SenderStoreProc: Menus.MenuProc = {
self: Viewer ¬ NARROW[parent];
fileName: Rope.ROPE = ViewerTools.GetSelectionContents[];
status: FileStatus;
exp: Rope.ROPE;
[status, exp] ¬ CheckForFile[fileName];
SELECT status FROM
local, notFound => {
TiogaMenuOps.Store[self, fileName];
IF mouseButton # blue THEN
{ IF ~self.newVersion THEN UnsetNewVersion[self] }
ELSE IF ~self.newVersion THEN ViewerOps.SetNewVersion[self];
};
ENDCASE => BadSaveOrStore[status, exp, fileName];
};
SenderSaveProc: Menus.MenuProc = {
self: Viewer ¬ NARROW[parent];
status: FileStatus;
exp: Rope.ROPE;
IF self.file = NIL THEN { SenderReport["\nCan't save - no file name\n"]; RETURN };
[status, exp] ¬ CheckForFile[self.name];
SELECT status FROM
local, notFound => {
[] ¬ ViewerOps.SaveViewer[ self ];
IF mouseButton # blue THEN
{ IF ~self.newVersion THEN UnsetNewVersion[self] }
ELSE IF ~self.newVersion THEN ViewerOps.SetNewVersion[self];
};
ENDCASE => BadSaveOrStore[status, exp, self.name];
};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
LoadFile: PROC[self: Viewer, filename: ROPE, noName: BOOL ¬ FALSE] = {
status: FileStatus;
s, fName, nameToUse: ROPE;
EntryErrorReport: ENTRY PROC[exp: ROPE] = INLINE {
ENABLE UNWIND => NULL;
ErrorReport[exp];
};
[status, s, fName] ¬ CheckForFile[filename, FALSE];
IF status = notFound THEN {
[status, s, fName] ¬ CheckForFile[filename];  -- try .form extension
nameToUse ¬ fName;
}
ELSE nameToUse ¬ filename;
IF status = local OR status = remote THEN
{ self.file ¬ NIL; -- make sure that no one else thinks you're saving something
TiogaMenuOps.Load[ self, nameToUse ];
ViewerTools.EnableUserEdits[ self ];  -- in case remote file specified
IF noName THEN
{ self.name ¬ sendCaption;
ViewerOps.PaintViewer[self, caption];
self.file ¬ NIL;  -- don't want any file association
};
GrabFocus[self];
UnsetNewVersion[self];
}
ELSE EntryErrorReport[s];
};
CheckForFile: PROC[name: ROPE, addExt: BOOL ¬ TRUE]
RETURNS[fileStatus: FileStatus ¬ local, explanation, fName: ROPE] = {
pathName: PFS.PATH ¬ PFS.PathFromRope[name];
fullPath: PFS.PATH;
fullPath ¬ PFS.FileLookup[pathName, IF addExt THEN LIST[".form"] ELSE NIL ! PFS.Error => CONTINUE];
IF fullPath = NIL THEN {
explanation ¬ IO.PutFR1["%g not found", [rope[name]] ];
fileStatus ¬ notFound;
}
ELSE fName ¬ PFS.RopeFromPath[fullPath];
};
ErrorReport: INTERNAL PROC[exp: ROPE] = {
InternalReport[format: "\n%g\n", v1: [rope[exp]], blink: TRUE];
};
BadSaveOrStore: ENTRY PROC[status: FileStatus, exp, name: ROPE] = {
ENABLE UNWIND => NULL;
SELECT status FROM
notFound =>
InternalReport[format: "Can't store on remote file: \"%g\"\n", v1: [rope[name]], blink: TRUE];
illegalName, otherError => ErrorReport[exp];
ENDCASE => ERROR;
};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- read the user profile line for message forms and add one menu entry for each
SetFormsList: PROC = {
OPEN Menus;
fLLast: LIST OF ROPE ¬ NIL;
popUpFormsFileNames ¬
UserProfile.ListOfTokens[key: "SendMailTool.MsgForms", default: NIL];
popUpFormsList ¬ NIL;
FOR fl: LIST OF ROPE ¬ popUpFormsFileNames, fl.rest UNTIL fl = NIL DO
form: ROPE ¬ fl.first;
shortName: ROPE;
fullPath: PFS.PATH;
IF form.Length[] = 0 THEN LOOP;  -- zero length name not allowed
shortName ¬ PFSNames.ComponentRope[PFSNames.ShortName[PFS.PathFromRope[form]]];
shortName ¬ shortName.Substr[0, shortName.Index[0, "."]];
IF fLLast = NIL THEN popUpFormsList ¬ fLLast ¬ LIST[shortName]
ELSE {
fLLast.rest ¬ LIST[shortName];
fLLast ¬ fLLast.rest;
};
ENDLOOP;
popUpFormsList ¬
CONS["NewSender", CONS["Default Form", CONS["Previous Msg", popUpFormsList]]];
};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- Start code: create menus and register ourselves
DoInit: PROC = {
OPEN Menus;
pL: LIST OF MailSend.MailSendProcsRef ¬ MailSend.GetRegisteredSendProcsList[];
len: INT;
TRUSTED { len ¬ List.Length[LOOPHOLE[pL, List.LORA]] };
FOR procs: LIST OF MailSend.MailSendProcsRef ¬ MailSend.GetRegisteredSendProcsList[],
procs.rest UNTIL procs = NIL DO
tName: ROPE ¬ IF ( len = 1 ) THEN NIL ELSE Atom.GetPName[procs.first.which];
name: ROPE ¬ Rope.Concat[tName, "Send"];
AppendMenuEntry[sendMenu,
CreateEntry[name: name, proc: MessageSendProc, clientData: procs.first.which, fork: FALSE, guarded: procs.first.which = $gv]];
IF (procs.first.which = $xns) THEN
AppendMenuEntry[sendMenu,
CreateEntry[name: Rope.Concat[tName, "USend"], proc: MessageUSendProc, clientData: procs.first.which, fork: FALSE]];
ENDLOOP;
AppendMenuEntry[sendMenu, CreateEntry["Get", SenderGetFileProc]];
AppendMenuEntry[sendMenu,
CreateEntry[name: "Store", proc: SenderStoreProc, guarded: TRUE]];
AppendMenuEntry[sendMenu, CreateEntry["Save", SenderSaveProc]];
AppendMenuEntry[sendMenu, CreateEntry["Forms", SenderFormsProc]];
AppendMenuEntry[sendMenu, CreateEntry["CR/LF", ForceCRLFProc]];
AppendMenuEntry[sendMenu, CreateEntry["Split", SenderSplitProc]];
AppendMenuEntry[sendMenu, CreateEntry["Places", SenderPlacesProc]];
AppendMenuEntry[sendMenu, CreateEntry["Levels", SenderLevelsProc]];
AppendMenuEntry[sendingMenu, CreateEntry["StopSending!", AbortSendProc]];
AppendMenuEntry[confirmMenu, CreateEntry["Confirm", ConfirmSendProc]];
AppendMenuEntry[confirmMenu, CreateEntry["Deny", DenySendProc]];
AppendMenuEntry[replyToMenu, CreateEntry["Self", ReplyToSelfProc]];
AppendMenuEntry[replyToMenu, CreateEntry["All", ReplyToAllProc]];
AppendMenuEntry[replyToMenu, CreateEntry["Cancel", ReplyToCancelProc]];
Commander.Register["SendMail", SendMailCmd, "Mail sending tool"];
Commander.Register["SendMailTool", SendMailCmd, "Mail sending tool"];
SendMailInit[firstTime];
UserProfile.CallWhenProfileChanges[SendMailInit];
{
path: ROPE = "SendMailTool.icons";
senderIcon ¬ Icons.NewIconFromFile[path, 6];
IF senderIcon = unInit THEN senderIcon ¬ tool
ELSE {
edittingIcon ¬ Icons.NewIconFromFile[path, 9];
notSentIcon ¬ Icons.NewIconFromFile[path, 19];
};
};
IF UserProfile.Boolean["SendMailTool.GlobalSenderButton", FALSE] THEN
[] ¬ Buttons.Create[info: [name: "Sender"], proc: NewSenderClickProc];
};
initDir: ROPE ~ PFS.RopeFromPath[PFS.GetWDir[]];
DoInit[];
END.