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