-- File: WalnutSendControlImpl.mesa
-- Contents: Control module for WalnutSend
-- Last Edited by: Willie-Sue, December 7, 1982 3:30 pm
-- Rick on: XXX
-- Willie-Sue on: June 3, 1983 4:48 pm
DIRECTORY
CIFS USING [Error],
Commander USING [CommandProc, Register],
FileIO USING [Open, OpenFailed],
GVAnswer USING [MakeHeader],
GVBasics USING [ItemType],
GVSend USING [Abort],
Icons USING [IconFlavor, NewIconFromFile],
InputFocus USING [GetInputFocus],
Menus,
IO,
Process USING [Detach],
Rope,
RopeIO USING [GetRope],
TEditSplit USING[ Split ],
TiogaMenuOps USING[ tiogaMenu ],
TiogaOps USING [CancelSelection, GetSelection],
TypeScript USING [Create],
ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc],
ViewerOps,
ViewerIO USING [CreateViewerStreams],
ViewerTools,
ViewerClasses,
UserCredentials USING [GetUserCredentials],
UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChangedProc],
WalnutSendMail;
WalnutSendControlImpl: CEDAR MONITOR
IMPORTS
CIFS, Commander, FileIO, IO, Process, Rope,
GVSend, GVAnswer,
Icons, InputFocus, Menus,
RopeIO, TEditSplit, TiogaMenuOps, TiogaOps, TypeScript,
ViewerEvents, ViewerIO, ViewerOps, ViewerTools,
UserCredentials, UserProfile,
WalnutSendMail
EXPORTS
WalnutSendMail
SHARES Menus =
BEGIN OPEN WalnutSendMail;
-- Global types & variables
sendCaption: ROPE← "WalnutSend...6/02/83";
userRName: PUBLIC ROPE← NIL; -- user name with regsitry
simpleUserName: PUBLIC ROPE← NIL; -- user name without registry
replyToSelf: PUBLIC BOOL← FALSE;
destroyAfterSend: BOOL← FALSE;
senderList: LIST OF SenderInfo← NIL;
sendMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
sendingMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
blankMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
confirmMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
replyToMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
sendingSynch: CONDITION;
needToAuthenticate: PUBLIC BOOL← TRUE;
iconicSender: BOOL← TRUE; -- make iconic initially only
senderIcon: Icons.IconFlavor← tool;
edittingIcon: Icons.IconFlavor← tool;
reporter: IO.STREAM← NIL;
senderTS: Viewer← NIL;
senderEvent: ViewerEvents.EventRegistration;
TiogaCTRL: PUBLIC GVBasics.ItemType;
subTocc: ROPE←
"Subject: \001Topic\002\nTo: \001Recipients\002\ncc: \001Copies To\002 ";
messageRope: ROPE← "\n\n\001Message\002\n";
answRope: ROPE← "\n\001Message\002\n";
fwdRope: ROPE←
"\n\n\001CoveringMessage\002\n\n-------------------------------------\n";
fwdRope2: ROPE← "\n------------------------------------------------------------\n";
-- ************************************************************************
-- Sending messages is independent of CedarDB
WalnutSendMail: Commander.CommandProc = { WalnutSendProc[fromExec: TRUE]};
WalnutSendProc: PUBLIC PROC[fromExec: BOOL] =
BEGIN
senderInfo: SenderInfo;
IF ~fromExec THEN iconicSender← FALSE;
senderInfo← BuildSender[paint: TRUE, iconic: iconicSender, init: NewMsgForm[]];
IF ~iconicSender THEN GrabFocus[senderInfo.senderV];
iconicSender← FALSE; -- iconic only the first time
END;
GrabFocus: ENTRY PROC[senderV: Viewer] =
BEGIN ENABLE UNWIND => NULL;
ViewerTools.SetSelection[senderV, NEW[ViewerTools.SelPosRec← [0, 0]]];
senderV.class.notify[senderV, LIST[$NextPlaceholder]];
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
BuildSendViewer: PUBLIC PROC[paint, iconic: BOOL, who: Viewer← NIL]
RETURNS[senderV: Viewer] =
{ RETURN[BuildSender[paint, iconic, NewMsgForm[], who].senderV] };
BuildSender: ENTRY PROC[paint, iconic: BOOL, init: ROPE← NIL, who: Viewer← NIL]
RETURNS[senderInfo: SenderInfo] =
BEGIN ENABLE UNWIND => NULL;
senderV: Viewer;
IF senderIcon = tool THEN
BEGIN ENABLE CIFS.Error => TRUSTED {GOTO notFound};
senderIcon← Icons.NewIconFromFile["Walnut.icons", 6];
edittingIcon← Icons.NewIconFromFile["Walnut.icons", 9];
EXITS
notFound =>
BEGIN ENABLE CIFS.Error => TRUSTED {CONTINUE};
senderIcon← Icons.NewIconFromFile["/Indigo/Cedar/Walnut/Walnut.icons", 6];
edittingIcon← Icons.NewIconFromFile["/Indigo/Cedar/Walnut/Walnut.icons", 9];
END;
END;
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
THEN
{ senderV← senderInfo.senderV;
IF senderV.link # NIL THEN InternalDestroySplits[senderV];
senderV.inhibitDestroy← TRUE;
KillFeedbackSel[senderV];
ViewerTools.SetContents[senderV, init];
ViewerOps.PaintViewer[senderV, menu]; -- shouldn't be necessary
IF senderV.iconic THEN
{ 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];
};
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;
ViewerOps.SetMenu[senderV, sendMenu];
senderInfo← NEW[SenderInfoObject← [senderV: senderV]];
ViewerOps.AddProp[senderV, $SenderInfo, senderInfo];
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];
ViewerTools.SetContents[senderV, init];
ViewerOps.PaintViewer[senderV, menu]; -- shouldn't be necessary
senderV.inhibitDestroy← FALSE;
IF iconic AND ~senderV.iconic THEN ViewerOps.PaintViewer[senderV, all];
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
WalnutSendInit: UserProfile.ProfileChangedProc =
{ uN: ROPE← UserCredentials.GetUserCredentials[].name;
pos: INT;
IF (pos← Rope.SkipTo[s: uN, skip: "."]) = Rope.Length[uN]
THEN {simpleUserName← uN; userRName← Rope.Cat[uN, ".", defaultRegistry]}
ELSE {simpleUserName← Rope.Substr[uN, 0, pos]; userRName← uN};
needToAuthenticate← TRUE;
replyToSelf← UserProfile.Boolean[key: "Walnut.ReplyToSelf", default: FALSE];
destroyAfterSend← UserProfile.Boolean[key: "Walnut.DestroyAfterSend", default: FALSE];
};
DisplayTxtInSender: ENTRY PROC[senderV: Viewer, txt: ROPE] =
BEGIN ENABLE UNWIND => NULL;
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[senderV, $SenderInfo]];
InternalDisplayTxt[senderInfo, txt];
END;
InternalDisplayTxt: INTERNAL PROC[senderInfo: SenderInfo, txt: ROPE] =
BEGIN
senderV: Viewer← senderInfo.senderV;
iHadFocus: BOOL← InputFocus.GetInputFocus[].owner = senderV;
tc: ViewerTools.TiogaContents← NEW[ViewerTools.TiogaContentsRec];
tc.contents← txt;
IF senderV.link # NIL THEN InternalDestroySplits[senderV];
KillFeedbackSel[senderV];
ViewerTools.SetTiogaContents[senderV, tc];
ViewerOps.PaintViewer[senderV, menu]; -- shouldn't be necessary
-- test if I had the focus & no-one else has it now
IF iHadFocus AND InputFocus.GetInputFocus[].owner = NIL THEN
{ ViewerTools.SetSelection[senderV, NEW[ViewerTools.SelPosRec← [0, 0]]];
senderV.class.notify[senderV, LIST[$NextPlaceholder]];
};
UnsetNewVersion[senderV];
ViewerOps.PaintViewer[senderV, caption];
senderInfo.successfullySent← FALSE;
END;
AnswerMsg: PUBLIC PROC[msgHeaders: ROPE, who: Viewer← NIL] RETURNS [v: Viewer] =
BEGIN
notOk: BOOL;
errorIndex: INT;
answer, init: ROPE;
senderInfo: SenderInfo;
AnswerGetChar: PROC[pos: INT] RETURNS[CHAR] = {RETURN[msgHeaders.Fetch[pos]]};
[notOk, answer, errorIndex]← GVAnswer.MakeHeader[AnswerGetChar, msgHeaders.Length[],
simpleUserName, defaultRegistry];
IF notOk THEN
{ SenderReport[IO.PutFR[
"\nSyntax error in line previous to line containing pos %g (in message being answered)",
IO.int[errorIndex]]];
RETURN
};
init← Rope.Concat[answer, answRope];
senderInfo← BuildSender[TRUE, FALSE, init, who];
GrabFocus[v← senderInfo.senderV];
END;
ForwardMsg: PUBLIC PROC[oldMsg: ViewerTools.TiogaContents, who: Viewer← NIL]
RETURNS [v: Viewer] =
{ senderInfo: SenderInfo← BuildSender[TRUE, FALSE, NIL, who];
ViewerTools.SetTiogaContents[v← senderInfo.senderV, oldMsg];
ViewerOps.PaintViewer[v, menu]; -- shouldn't be necessary
InsertIntoViewer[v, Rope.Concat[subTocc, fwdRope], 0];
InsertIntoViewer[v, fwdRope2, -1];
ViewerTools.EnableUserEdits[v];
UnsetNewVersion[v];
ViewerOps.PaintViewer[v, caption];
GrabFocus[v];
};
NewMsgForm: PROC RETURNS [r: ROPE] =
{ RETURN[Rope.Cat[subTocc, simpleUserName, messageRope]]};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- Menu procedures
NewMsgFormProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
-- IF ~senderInfo.successfullySent AND viewer.newVersion THEN
-- { InternalReport[confirmR];
-- IF ~SendingConfirm[senderInfo] THEN {InternalReport[denyR]; RETURN};
-- };
InternalDisplayTxt[senderInfo: senderInfo, txt: NewMsgForm[]];
END;
-- confirmR: ROPE← "\nConfirm deletion of unsent message";
-- denyR: ROPE← " .. denied\n";
PrevMsgProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
-- IF viewer.newVersion THEN
-- { InternalReport[confirmR];
-- IF ~SendingConfirm[senderInfo] THEN {InternalReport[denyR]; RETURN};
-- };
IF viewer.link#NIL THEN InternalDestroySplits[viewer];
IF senderInfo.prevMsg = NIL THEN
InternalDisplayTxt[senderInfo, NewMsgForm[]]
ELSE
{ KillFeedbackSel[viewer];
ViewerTools.SetTiogaContents[viewer, senderInfo.prevMsg];
ViewerOps.PaintViewer[viewer, menu]; -- shouldn't be necessary
DeleteChars[viewer, senderInfo.numCharsToDelete];
};
UnsetNewVersion[viewer];
ViewerOps.PaintViewer[viewer, caption];
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
DestroySendViewer: ViewerEvents.EventProc =
BEGIN
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN[FALSE];
IF ViewerOps.FetchProp[viewer, $DestroySenderProc] = NIL THEN RETURN[FALSE];
TRUSTED {Process.Detach[FORK CheckForDestroy[viewer, senderInfo]]};
RETURN[TRUE];
END;
CloseSendViewer: ViewerEvents.EventProc =
BEGIN
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN[FALSE];
viewer.icon←
IF viewer.newVersion THEN edittingIcon ELSE senderIcon;
RETURN[FALSE];
END;
FirstEditSendViewer: ViewerEvents.EventProc =
BEGIN OPEN Menus;
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
IF senderInfo = NIL THEN RETURN[FALSE];
SenderNewVersion[viewer]; -- show as guarded
RETURN[FALSE];
END;
SenderNewVersion: PROC[viewer: Viewer] =
BEGIN OPEN Menus;
menu: Menu;
SetGuarded[FindEntry[menu← viewer.menu, "Clear"], TRUE];
SetGuarded[FindEntry[viewer.menu, "PrevMsg"], TRUE];
SetGuarded[FindEntry[viewer.menu, "GetForm"], TRUE];
ViewerOps.PaintViewer[viewer, menu]; -- show as guarded
viewer.newVersion← TRUE;
END;
UnsetNewVersion: PROC[viewer: Viewer] =
BEGIN OPEN Menus;
menu: Menu;
SetGuarded[FindEntry[menu← viewer.menu, "Clear"], FALSE];
SetGuarded[FindEntry[viewer.menu, "PrevMsg"], FALSE];
SetGuarded[FindEntry[viewer.menu, "GetForm"], FALSE];
ViewerOps.PaintViewer[viewer, menu]; -- show as unguarded
viewer.newVersion← FALSE;
END;
CheckForDestroy: ENTRY PROC[senderV: Viewer, senderInfo: SenderInfo] =
BEGIN 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];
senderInfo.destroyEvent← NIL;
senderInfo.closeEvent← NIL;
senderInfo.editEvent← NIL;
TRUSTED {Process.Detach[FORK ViewerOps.DestroyViewer[senderV]]};
END;
SenderReport: PUBLIC ENTRY PROC[msg: ROPE] =
BEGIN ENABLE UNWIND => NULL;
InternalReport[msg];
END;
InternalReport: INTERNAL PROC[msg: ROPE] =
BEGIN
IF reporter = NIL THEN
{ senderTS← TypeScript.Create[
info: [name: "WalnutSend", iconic: FALSE, column: right, openHeight: 64]];
reporter← ViewerIO.CreateViewerStreams[NIL, senderTS].out;
senderEvent← ViewerEvents.RegisterEventProc[
proc: DestroyReporter, event: destroy, filter: senderTS, before: TRUE];
};
reporter.PutRope[msg];
END;
DestroyReporter: ViewerEvents.EventProc =
BEGIN
IF senderEvent # NIL THEN ViewerEvents.UnRegisterEventProc[senderEvent, destroy];
senderEvent← NIL;
senderTS← NIL;
reporter← NIL;
RETURN[FALSE];
END;
RegisterReporter: PUBLIC ENTRY PROC[strm: IO.STREAM] =
BEGIN ENABLE UNWIND => NULL;
IF senderTS # NIL THEN
{ reporter.Close[];
ViewerEvents.UnRegisterEventProc[senderEvent, destroy];
senderEvent← NIL;
ViewerOps.DestroyViewer[senderTS];
senderTS← NIL;
};
reporter← strm;
END;
UnregisterReporter: PUBLIC ENTRY PROC[strm: IO.STREAM] =
BEGIN ENABLE UNWIND => NULL;
IF reporter = strm THEN reporter← NIL;
END;
DenySendProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.confirmed← FALSE;
senderInfo.userResponded← TRUE;
BROADCAST sendingSynch;
END;
ConfirmSendProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.confirmed← TRUE;
senderInfo.userResponded← TRUE;
BROADCAST sendingSynch;
END;
AbortSendProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.aborted← TRUE;
END;
ReplyToSelfProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.replyToResponse← self;
senderInfo.userResponded← TRUE;
BROADCAST sendingSynch;
END;
ReplyToAllProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.replyToResponse← all;
senderInfo.userResponded← TRUE;
BROADCAST sendingSynch;
END;
ReplyToCancelProc: ENTRY Menus.MenuProc =
BEGIN ENABLE UNWIND => NULL;
viewer: Viewer← NARROW[parent];
senderInfo: SenderInfo← NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]];
senderInfo.replyToResponse← cancel;
senderInfo.userResponded← TRUE;
BROADCAST sendingSynch;
END;
----------------------------
ReplyToResponse: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS [HowToReplyTo] =
BEGIN 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]
END;
CheckForAbortSend: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS[BOOL] =
{ ENABLE UNWIND => NULL;
IF senderInfo.aborted THEN
{ InternalReport[" Aborting delivery ...\n"];
IF senderInfo.sendHandle#NIL THEN GVSend.Abort[senderInfo.sendHandle];
IF senderInfo.aborted THEN senderInfo.senderV.inhibitDestroy← FALSE;
};
RETURN[senderInfo.aborted];
};
Confirmation: PUBLIC ENTRY PROC[senderInfo: SenderInfo] RETURNS [BOOL] =
{ ENABLE UNWIND => NULL; RETURN[SendingConfirm[senderInfo]]};
SendingConfirm: INTERNAL PROC[senderInfo: SenderInfo] RETURNS [BOOL] =
BEGIN
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];
END;
ChangeMenu: PROC[v: Viewer, menu: Menus.Menu] RETURNS [oldM: Menus.Menu] =
BEGIN
oldM← v.menu;
ViewerOps.SetMenu[v, menu];
END;
MessageSendProc: PUBLIC Menus.MenuProc =
BEGIN
self: Viewer← NARROW[parent];
iHadFocus: BOOL← InputFocus.GetInputFocus[].owner = self;
ViewerTools.InhibitUserEdits[self];
IF self.link#NIL THEN DestroySplits[self];
TRUSTED {Process.Detach[FORK SendProc[self, mouseButton, iHadFocus]]};
END;
SendProc: PROC[viewer: Viewer, mouseButton: Menus.MouseButton, iHadFocus: BOOL] =
BEGIN OPEN ViewerOps;
sendOK: BOOL;
viewer.newVersion← TRUE;
viewer.name← "Sending ...";
PaintViewer[viewer, caption];
sendOK← SendMsg[senderV: viewer, doClose: mouseButton=blue];
IF ~sendOK THEN SenderNewVersion[viewer];
viewer.name← sendCaption;
viewer.icon← senderIcon;
PaintViewer[viewer, caption];
ViewerTools.EnableUserEdits[viewer];
IF sendOK THEN
{ IF mouseButton=blue AND destroyAfterSend THEN
{DestroyViewer[viewer]; RETURN};
DisplayTxtInSender[senderV: viewer, txt: NewMsgForm[]];
IF mouseButton#blue THEN
{IF iHadFocus AND InputFocus.GetInputFocus[].owner = NIL THEN GrabFocus[viewer]}
ELSE CloseViewer[viewer];
};
END;
SenderSplitProc: Menus.MenuProc =
BEGIN
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← senderInfo^]];
nsI.senderV← newV;
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
END;
DestroySplits: ENTRY PROC[keepThisOne: Viewer] =
BEGIN ENABLE UNWIND => NULL;
InternalDestroySplits[keepThisOne];
END;
InternalDestroySplits: INTERNAL PROC[keepThisOne: Viewer] =
BEGIN
next: Viewer← keepThisOne.link;
next2: Viewer;
sI: SenderInfo;
DO
IF next = keepThisOne THEN EXIT;
sI← NARROW[ViewerOps.FetchProp[next, $SenderInfo]];
IF sI=NIL THEN LOOP;
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;
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];
END;
SenderPlacesProc: Menus.MenuProc =
Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Places" ] ].proc;
SenderLevelsProc: Menus.MenuProc =
Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Levels" ] ].proc;
SenderGetProc: Menus.MenuProc =
BEGIN
self: Viewer← NARROW[parent];
strm: IO.STREAM;
contents: ViewerTools.TiogaContents;
filename: ROPE← ViewerTools.GetSelectionContents[];
SetTC: ENTRY PROC =
BEGIN ENABLE UNWIND => NULL;
KillFeedbackSel[self];
ViewerTools.SetTiogaContents[self, contents];
ViewerOps.PaintViewer[self, menu]; -- shouldn't be necessary
UnsetNewVersion[self];
END;
IF filename.Length[] = 0 THEN
{ SenderReport["\nNo file name selected ...\n"]; RETURN};
IF (strm← OpenFileForReading[filename]) = NIL THEN
{ SenderReport["\nCouldn't find file: "]; SenderReport[filename]; SenderReport["\n"];
RETURN};
IF self.link # NIL THEN DestroySplits[self];
contents← TiogaTextFromStrm[strm, 0];
strm.Close[];
SetTC[];
GrabFocus[self];
END;
OpenFileForReading: PROC[fileName: ROPE] RETURNS[strm: IO.STREAM] =
BEGIN
BEGIN ENABLE FileIO.OpenFailed => GOTO BadOpen;
strm←
FileIO.Open[fileName: fileName, accessOptions: read, createOptions: oldOnly, raw: TRUE];
EXITS
BadOpen =>
BEGIN ENABLE FileIO.OpenFailed => {strm← NIL; CONTINUE};
IF fileName.Find["."] >= 0 THEN RETURN; -- no hope
fileName← fileName.Concat[".form"];
strm←
FileIO.Open[fileName: fileName, accessOptions: read, createOptions: oldOnly, raw: TRUE];
END;
END;
END;
SenderStoreProc: Menus.MenuProc =
BEGIN
self: Viewer← NARROW[parent];
strm: IO.STREAM;
contents: ViewerTools.TiogaContents;
filename: ROPE← ViewerTools.GetSelectionContents[];
IF filename.Length[] = 0 THEN
{ SenderReport[" No file name selected ...\n"]; RETURN};
IF (strm← OpenFileForWriting[filename]) = NIL THEN
{ SenderReport["Couldn't store to file: "]; SenderReport[filename]; SenderReport["\n"];
RETURN};
contents← ViewerTools.GetTiogaContents[self];
strm.PutRope[contents.contents];
strm.PutRope[contents.formatting];
strm.Close[];
UnsetNewVersion[self];
ViewerOps.PaintViewer[self, caption];
SenderReport["\nMsg has been stored on file: "];
SenderReport[filename];
SenderReport["\n"];
END;
OpenFileForWriting: PROC [fileName: ROPE] RETURNS[strm: IO.STREAM] =
BEGIN
strm← FileIO.Open[fileName: fileName, accessOptions: overwrite, raw: TRUE !
FileIO.OpenFailed => CONTINUE;
CIFS.Error => CONTINUE];
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
TiogaTextFromStrm: PUBLIC PROC[strm: IO.STREAM, startPos: INT← 0, len: INT← LAST[INT]]
RETURNS[contents: TiogaContents] =
-- returns NIL IF endOfStream encountered during read
BEGIN ENABLE IO.EndOfStream => GOTO TooShort;
fulltext: ROPE;
formatPos: INT;
strm.SetIndex[startPos];
fulltext← RopeIO.GetRope[strm, len];
contents← NEW[ViewerTools.TiogaContentsRec];
IF (formatPos← fulltext.Find["\000\000"]) < 0 THEN -- no formatting
{ contents.contents← fulltext; RETURN};
contents.contents← fulltext.Substr[len: formatPos];
contents.formatting← fulltext.Substr[formatPos];
EXITS TooShort => RETURN[NIL];
END;
-- bug in SetTiogaContents necessitates this
KillFeedbackSel: PROC[v: Viewer] =
BEGIN OPEN TiogaOps;
who: Viewer← GetSelection[feedback].viewer;
IF who = v THEN CancelSelection[feedback];
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- Start code: create menus
BEGIN
OPEN Menus;
AppendMenuEntry[sendMenu,
CreateEntry[name: "Send", proc: MessageSendProc, fork: FALSE]];
AppendMenuEntry[sendMenu, CreateEntry["Clear", NewMsgFormProc]];
AppendMenuEntry[sendMenu, CreateEntry["PrevMsg", PrevMsgProc]];
AppendMenuEntry[sendMenu, CreateEntry["GetForm", SenderGetProc]];
AppendMenuEntry[sendMenu, CreateEntry["StoreMsg", SenderStoreProc]];
AppendMenuEntry[sendMenu, CreateEntry["Split", SenderSplitProc]];
AppendMenuEntry[sendMenu, CreateEntry["Places", SenderPlacesProc]];
AppendMenuEntry[sendMenu, CreateEntry["Levels", SenderLevelsProc]];
AppendMenuEntry[sendingMenu, CreateEntry["AbortSend", 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]];
END;
----------------------------
-- start code; make menu and register ourselves
TRUSTED { TiogaCTRL ← LOOPHOLE[1013B, GVBasics.ItemType] };
Commander.Register["WalnutSend", WalnutSendMail, "Mail sending tool"];
UserProfile.CallWhenProfileChanges[WalnutSendInit];
END.