-- File: WalnutWindowImpl.mesa
-- Contents: Top level Viewer for Walnut; initializes database & displays control window
-- created February, 1981 by Willie-Sue & Rick
-- Last edit by:
-- Willie-Sue on: December 13, 1983 3:31 pm
-- Donahue, July 26, 1983 9:13 am
DIRECTORY
AlpineWalnutCmds USING [CopyForExpunge],
Buttons USING [ButtonProc],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [Run],
Containers,
DB USING [Aborted, Failure, Transaction, AbortTransaction, DeclareSegment,
Initialize, TransactionOf],
Labels USING [Set],
FS USING [Error, SetKeep, StreamOpen],
GVRetrieve USING [MBXState],
Icons USING [IconFlavor, NewIconFromFile],
IO,
WQueue USING [Create, QueueClientAction],
Menus,
MessageWindow USING [Append],
PrincOpsUtils USING [IsBound],
Process USING [Detach],
Rope,
Rules USING [Create],
UserCredentials USING [Get],
UserProfile USING[ProfileChangedProc, Boolean, CallWhenProfileChanges, Token],
ViewerBLT USING[ChangeNumberOfLines],
ViewerClasses USING [Viewer],
ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc],
ViewerOps,
ViewerSpecs USING[openLeftWidth, openRightTopY, openRightWidth],
ViewerTools USING [SetSelection],
ViewerIO USING [CreateViewerStreams],
WalnutControlPrivate USING [CloseTransactions, ClosingWalnut, FlushWQueue, QuitWalnut,
StartOrRestartWalnut, WalnutNotifier],
WalnutControlMonitorImpl,
WalnutDB USING [RegisterUpdatesPendingProc, UnRegisterUpdatesPendingProc],
WalnutDisplayerOps USING [msgName, msgSetName],
WalnutLog USING [ExpungeMsgs, MarkWalnutTransaction],
WalnutExtras USING [ChangeDBProc, DoWaitCall, DumpProc, EnumWalnutViewers,
EstablishFileNames, ExpungeProc, ReadMailProc, RetrieveNewMailProc,
ScavengeProc, SetWalnutIcons, TakeDownWalnutViewers],
WalnutRetrieve USING [CloseConnection],
WalnutSendOps USING [RegisterReporter, UnregisterReporter, WalnutSendProc],
WalnutViewer USING [AnotherLabel, CreateMenuEntry, FirstLabel, ImmediateButton,
MakeRuler, MakeTypescript, NextRightTextViewer],
WalnutVoice USING [CheckForWalnuthatch],
WalnutWindow USING [Queue, firstMsgSetButton, lastMsgSetButton, selectedMsgSetButtons,
AppendButtonProc, ArchiveButtonProc, CreateMsgSetButtonProc,
DeleteMsgSetsProc, DestroyAllMsgSetButtons, DoNewMail, PrintMsgSetsProc,
SizeOfMsgSetsProc];
WalnutWindowImpl: CEDAR MONITOR LOCKS walnutControlLock
IMPORTS
AlpineWalnutCmds, DB, FS,
Commander, CommandTool,
Containers, Icons, Labels, WQueue, Menus, MessageWindow, Rules,
IO, PrincOpsUtils, Process, Rope,
UserCredentials, UserProfile,
ViewerBLT, ViewerEvents, ViewerOps, ViewerTools, ViewerIO, ViewerSpecs,
walnutControlLock: WalnutControlMonitorImpl,
WalnutControlPrivate, WalnutDB, WalnutDisplayerOps, WalnutExtras,
WalnutLog, WalnutRetrieve, WalnutSendOps,
WalnutViewer, WalnutVoice, WalnutWindow
EXPORTS WalnutControlPrivate, WalnutExtras, WalnutWindow
SHARES WalnutControlMonitorImpl, WalnutWindow =
BEGIN OPEN WalnutControlPrivate, WalnutExtras, WalnutWindow;
-- Walnut Viewers types and global data
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
InitialNotifyLabel: PUBLIC ROPE←
" ";
-- walnutVersion: ROPE← "WalnutWithVoice 4.4.101";
walnutVersion: ROPE← "Walnut";
walnut: PUBLIC Viewer← NIL;
wtsOut: IO.STREAM← NIL;
walnutIcon: PUBLIC Icons.IconFlavor← tool;
newMailIcon: PUBLIC Icons.IconFlavor← tool;
msgSetIcon: PUBLIC Icons.IconFlavor← tool;
msgIcon: PUBLIC Icons.IconFlavor← tool;
iconFilePathName: PUBLIC ROPE← NIL;
alpineNeeded: BOOL← FALSE;
walnutEventReg: ViewerEvents.EventRegistration← NIL;
mustQuitWalnut: PUBLIC ROPE← NIL;
previousUser: PUBLIC ROPE← NIL;
doingCheckpoint: PUBLIC BOOL← FALSE;
rollbackFinished: PUBLIC CONDITION;
initialActiveIconic: PUBLIC BOOL← FALSE;
initialActiveRight: PUBLIC BOOL← TRUE;
initialActiveOpen: PUBLIC BOOL← TRUE;
msgSetBorders: PUBLIC BOOL← FALSE;
enableTailRewrite: PUBLIC BOOL← FALSE;
excessBytesInLogFile: PUBLIC INT← 300000;
walnutRulerBefore, walnutRulerAfter, walnutTS: PUBLIC Viewer;
createButton: Viewer;
msForCreateButton: Viewer;
msgSetText: PUBLIC Viewer; -- the text argument for Create MsgSet
fileNameText: PUBLIC Viewer; -- fileName for Archiving
mailNotifyLabel: PUBLIC Viewer; -- is there new mail or not
autoNewMail: BOOL← FALSE;
tryForNewMail: BOOL← FALSE;
walnutSegmentFile: PUBLIC ROPE;
walnutLogName: PUBLIC ROPE;
readOnlyAccess: PUBLIC BOOL;
logIsAlpineFile: PUBLIC BOOL;
personalMailDB: PUBLIC BOOL;
segmentName: PUBLIC ATOM← $Walnut;
walnutQueue: PUBLIC Queue;
walnutNullTrans: PUBLIC DB.Transaction;
responseSynch: CONDITION;
userHasResponded, userConfirmed: BOOL← FALSE;
alpineServer: ROPE = "[Luther.alpine]"; -- for now
lastStateReported: PUBLIC GVRetrieve.MBXState← unknown;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
BuildWalnut: PUBLIC ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
h: IO.STREAM;
dirName, segName, logName, captionName: ROPE;
alpNeeded, logIsAlp: BOOL;
IF doingCheckpoint THEN WAIT rollbackFinished;
personalMailDB← TRUE;
IF cmd # NIL THEN
{ h← IO.RIS[cmd.commandLine];
dirName← h.GetTokenRope[IO.IDProc ! IO.EndOfStream => {dirName← NIL; CONTINUE}].token;
};
[segName, logName, captionName, logIsAlp, alpNeeded]← CmdProcCall[dirName, cmd];
IF segName=NIL THEN RETURN;
IF walnut # NIL THEN
IF segName.Equal[walnutSegmentFile, FALSE] THEN
{ Report["\nWalnut is already running"]; RETURN}
ELSE
{ cdb: ChangeDBParams← NEW[ChangeDBObject←
[segName: segName, logName: logName, logIsAlp: logIsAlp, alpNeeded: alpNeeded]];
walnut.name← captionName;
ViewerOps.PaintViewer[walnut, caption];
TRUSTED { Process.Detach[FORK ChangeDB[cdb]]};
WAIT cdb.cond;
IF walnut # NIL THEN ViewerOps.AddProp[walnut, $IconLabel, dirName];
RETURN
};
IF Rope.Length[UserCredentials.Get[].name] = 0 THEN
{ IF cmd # NIL THEN cmd.out.PutRope["Please Login"]; RETURN};
walnutSegmentFile← segName;
walnutLogName← logName;
logIsAlpineFile← logIsAlp;
alpineNeeded← alpNeeded;
WalnutDisplayerOps.msgName ← Rope.Concat[walnutSegmentFile, "!Msg!"];
WalnutDisplayerOps.msgSetName ← Rope.Concat[walnutSegmentFile, "!MsgSet!"];
StartUp[FALSE, captionName];
IF walnut # NIL THEN
{ ViewerOps.AddProp[walnut, $IconLabel, dirName];
IF personalMailDB AND autoNewMail THEN
{ tryForNewMail← FALSE;
TRUSTED {Process.Detach[FORK AutoNewMailProc]};
};
};
END;
CmdProcCall: INTERNAL PROC[dirName: ROPE, cmd: Commander.Handle]
RETURNS[segName, logName, captionName: ROPE, logIsAlp, alpNeeded: BOOL] =
BEGIN
sName, lName: ROPE;
alpServer: ROPE = "[Luther.alpine]<";
seg: ROPE = ">Walnut.segment";
user: ROPE;
personalMailDB← TRUE;
IF dirName.Length = 0 THEN
{ sName← UserProfile.Token[key: "Walnut.WalnutSegmentFile", default: ""];
lName← UserProfile.Token[key: "Walnut.WalnutLogFile", default: "Walnut.DBLog"];
IF sName.Length[] = 0 THEN
{ user← UserCredentials.Get[].name;
IF user # NIL THEN sName← Rope.Cat[alpServer, user, seg];
};
captionName← walnutVersion;
}
ELSE
{ sName← Rope.Cat[alpServer, dirName, seg];
personalMailDB← FALSE;
captionName← Rope.Cat[walnutVersion, " <", dirName, ">"];
};
[segName, logName, logIsAlp, alpNeeded]← EstablishFileNames[sName, lName];
walnutNullTrans← NIL;
IF ~segName.Find[".alpine]", 0, FALSE] > 0 THEN
{ msg: ROPE←
Rope.Concat[segName, ": is not a valid Walnut segment file; it MUST be on alpine"];
IF cmd # NIL THEN {cmd.out.PutRope[msg]; cmd.out.PutChar['\n]}
ELSE ReportRope[msg];
segName← NIL;
};
END;
StartUp: INTERNAL PROC[scavengeFirst: BOOL, captionName: ROPE] =
BEGIN
IF ~BuildWalnutControlWindow[captionName] THEN RETURN;
IF ~StartOrRestartWalnut[TRUE, scavengeFirst] THEN RETURN;
FlushWQueue[]; -- for good measure
Report[" ...Ready"];
TRUSTED { Process.Detach[FORK WalnutNotifier[]] };
END;
CloseDownWalnut: PUBLIC INTERNAL PROC[doCommit: BOOL] =
-- clean up after Walnut
BEGIN
v: Viewer← walnut;
walnut.inhibitDestroy← TRUE;
FlushWQueue[];
IF walnutEventReg # NIL THEN
ViewerEvents.UnRegisterEventProc[walnutEventReg, destroy];
walnutEventReg← NIL;
ChangeWalnutMenu[workingMenu];
WalnutDB.UnRegisterUpdatesPendingProc[SetWalnutUpdatesPending];
selectedMsgSetButtons← firstMsgSetButton← lastMsgSetButton← NIL;
TakeDownWalnutViewers[];
ReportRope["Closing database and saving log ..."];
BEGIN
CloseTransactions[doCommit !
DB.Failure, IO.Error => GOTO failure; -- IO.Error replaces FileIOAlpine.Failure
UNWIND => CONTINUE ];
EXITS
failure => IF DB.TransactionOf[segmentName] # NIL THEN
DB.AbortTransaction[DB.TransactionOf[segmentName ! IO.Error => CONTINUE]];
END;
walnut← NIL; -- don't let others try to use Report
WalnutRetrieve.CloseConnection[]; -- NOP if no open connection
lastStateReported← unknown;
msgSetText← mailNotifyLabel← NIL;
WalnutSendOps.UnregisterReporter[wtsOut];
wtsOut.Close[];
wtsOut← NIL;
mustQuitWalnut← NIL;
WQueue.QueueClientAction[walnutQueue, ClosingWalnut, NIL];
v.inhibitDestroy← FALSE;
doingCheckpoint← FALSE;
ViewerOps.DestroyViewer[v];
END;
WalnutReadMailFile: PUBLIC ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
h: IO.STREAM;
fName, messageSet: ROPE;
IF walnut = NIL THEN {NoWalnutReport[cmd, "read old mail files"]; RETURN};
IF doingCheckpoint THEN WAIT rollbackFinished;
h← IO.RIS[cmd.commandLine];
fName← h.GetTokenRope[IO.IDProc ! IO.EndOfStream => GOTO noFile].token;
messageSet← h.GetTokenRope[IO.IDProc ! IO.EndOfStream => CONTINUE].token;
ReadMailFile[fName, messageSet];
WalnutLog.MarkWalnutTransaction[];
EXITS
noFile => Report["No file specified"];
END;
-- make separate proc so callable by program
ReadMailFile: PUBLIC PROC[fName, msgSet: ROPE] =
BEGIN
mfp: LIST OF ROPE← LIST[fName, IF msgSet=NIL THEN "Active" ELSE msgSet];
DoWaitCall[ReadMailProc, mfp];
END;
WalnutExpunge: PUBLIC ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
IF walnut = NIL THEN {NoWalnutReport[cmd, "do Expunge"]; RETURN};
IF doingCheckpoint THEN WAIT rollbackFinished;
DoWaitCall[ExpungeProc];
FinishCall[];
END;
WalnutDump: PUBLIC ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
IF walnut = NIL THEN {NoWalnutReport[cmd, "do Dump"]; RETURN};
IF doingCheckpoint THEN WAIT rollbackFinished;
DoWaitCall[DumpProc];
FinishCall[];
END;
WalnutScavenge: PUBLIC ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
IF walnut = NIL THEN
{ segName, logName, captionName: ROPE;
logIsAlp, alpNeeded: BOOL;
personalMailDB← TRUE;
[segName, logName, captionName, logIsAlp, alpNeeded]← CmdProcCall[NIL, cmd];
IF segName=NIL THEN RETURN;
walnutSegmentFile← segName;
walnutLogName← logName;
logIsAlpineFile← logIsAlp;
alpineNeeded← alpNeeded;
WalnutDisplayerOps.msgName ← Rope.Concat[walnutSegmentFile, "!Msg!"];
WalnutDisplayerOps.msgSetName ← Rope.Concat[walnutSegmentFile, "!MsgSet!"];
StartUp[TRUE, walnutVersion];
IF walnut # NIL THEN ViewerOps.AddProp[walnut, $IconLabel, NIL];
RETURN
};
IF doingCheckpoint THEN WAIT rollbackFinished;
ChangeWalnutMenu[workingMenu];
DoWaitCall[ScavengeProc];
FinishCall[];
END;
FinishCall: INTERNAL PROC =
BEGIN
Report["\n... Restarting"];
CloseTransactions[ TRUE ! DB.Failure, IO.Error => CONTINUE];
IF ~StartOrRestartWalnut[TRUE] THEN RETURN;
Report[" ... done"];
END;
GetNewMail: PUBLIC PROC = {[]← WalnutNewMail[NIL]};
WalnutNewMail: PUBLIC ENTRY Commander.CommandProc =
BEGIN ENABLE UNWIND => NULL;
IF walnut = NIL THEN {NoWalnutReport[cmd, "retrieve new mail"]; RETURN};
IF doingCheckpoint THEN WAIT rollbackFinished;
DoWaitCall[RetrieveNewMailProc, NIL]
END;
NoWalnutReport: INTERNAL PROC[cmd: Commander.Handle, msg: ROPE] =
BEGIN
r: ROPE← Rope.Concat["Walnut must be running to ", msg];
IF cmd # NIL THEN cmd.out.PutRope[r] ELSE MessageWindow.Append[r]
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
BuildWalnutControlWindow: INTERNAL PROC[captionName: ROPE] RETURNS[ok: BOOL] =
BEGIN
bt: Viewer;
walnutWidth: INTEGER;
transOK: BOOL← TRUE;
tsLogName: ROPE = "Walnut.TypescriptLog";
IF iconFilePathName=NIL THEN iconFilePathName← "Walnut.icons"; -- try local one first
IF walnutIcon = tool THEN
{ BEGIN ENABLE FS.Error => GOTO notFound;
walnutIcon← Icons.NewIconFromFile[iconFilePathName, 0];
EXITS
notFound =>
BEGIN ENABLE FS.Error => { iconFilePathName← NIL; CONTINUE};
iconFilePathName← "[Indigo]<Cedar>Walnut>Walnut.icons";
walnutIcon ← Icons.NewIconFromFile[iconFilePathName, 0];
END;
END;
IF iconFilePathName#NIL THEN SetWalnutIcons[iconFilePathName];
};
walnut← ViewerOps.CreateViewer[
flavor: $Container,
info: [name: captionName, iconic: FALSE, column: right, scrollable: FALSE,
icon: walnutIcon, openHeight: ViewerSpecs.openRightTopY/4]];
walnut.inhibitDestroy← TRUE;
walnutWidth← IF walnut.column = right THEN ViewerSpecs.openRightWidth ELSE
ViewerSpecs.openLeftWidth;
ViewerOps.SetMenu[walnut, blankMenu];
mailNotifyLabel← WalnutViewer.FirstLabel[name: InitialNotifyLabel, parent: walnut];
bt← WalnutViewer.ImmediateButton[name: "Archive", proc: ArchiveButtonProc,
border: TRUE, sib: mailNotifyLabel, fork: FALSE, newLine: TRUE];
bt← WalnutViewer.ImmediateButton[name: "Append", proc: AppendButtonProc,
border: TRUE, sib: bt, fork: FALSE];
bt← WalnutViewer.ImmediateButton
[name: "on File:", proc: FileNameProc, border: FALSE, sib: bt];
fileNameText← WalnutViewer.NextRightTextViewer[sib: bt, w: walnutWidth];
Containers.ChildXBound[walnut, fileNameText];
FixupCreateLine[];
bt← walnutRulerBefore← WalnutViewer.MakeRuler[sib: createButton, h: 2];
walnutRulerAfter← Rules.Create[
info: [parent: walnut, wy: bt.wy+bt.wh+14+3, ww: walnutWidth, wh: 1]];
Containers.ChildXBound[walnut, walnutRulerAfter];
walnutTS← WalnutViewer.MakeTypescript[sib: walnutRulerAfter];
FS.SetKeep[tsLogName, 2 ! FS.Error => CONTINUE]; -- if tsLogName doesn't exist
wtsOut← ViewerIO.CreateViewerStreams[NIL, walnutTS, tsLogName].out;
TRUSTED
BEGIN
DO
IF ~PrincOpsUtils.IsBound[WalnutSendOps.RegisterReporter] THEN
{IF ~ContinueThisWalnut["WalnutSend"] THEN RETURN[FALSE] ELSE LOOP};
IF alpineNeeded AND ~PrincOpsUtils.IsBound[AlpineWalnutCmds.CopyForExpunge] THEN
{IF ~ContinueThisWalnut["AlpineUserImpls"] THEN RETURN[FALSE] ELSE LOOP};
IF ~PrincOpsUtils.IsBound[DB.DeclareSegment] THEN
{IF ~ContinueThisWalnut["Cypress"] THEN RETURN[FALSE] ELSE LOOP};
EXIT;
ENDLOOP;
END;
ViewerOps.SetMenu[walnut, workingMenu];
walnutEventReg←
ViewerEvents.RegisterEventProc[proc: QuitProc, event: destroy, filter: walnut];
WalnutDB.RegisterUpdatesPendingProc[SetWalnutUpdatesPending];
WalnutSendOps.RegisterReporter[wtsOut];
DB.Initialize[nCachePages: 256];
[]← WalnutVoice.CheckForWalnuthatch[];
RETURN[TRUE];
END;
ContinueThisWalnut: INTERNAL PROC[who: ROPE] RETURNS[reTry: BOOL]=
BEGIN
v: Viewer;
IF InternalLoadBcd[who] THEN RETURN[TRUE];
Report["Click Continue to push on anyway, Quit to quit out of Walnut"];
IF InternalConfirm[startupConfirmMenu] THEN RETURN[TRUE];
msgSetText← mailNotifyLabel← NIL;
v← walnut;
walnut← NIL;
wtsOut.Close[];
wtsOut← NIL;
ViewerOps.DestroyViewer[v];
RETURN[FALSE]
END;
LoadBcdForWalnut: PUBLIC ENTRY PROC[who: ROPE] RETURNS[ok: BOOL] =
{ ENABLE UNWIND => NULL;
RETURN[InternalLoadBcd[who]]
};
InternalLoadBcd: INTERNAL PROC[who: ROPE] RETURNS[ok: BOOL] =
BEGIN
errMsg: ROPE;
error: BOOL;
[errMsg, error]← CommandTool.Run[who];
Report[errMsg];
IF ~error THEN RETURN[TRUE];
IF errMsg.Find["unbound imports", 0, FALSE] >= 0 THEN RETURN[TRUE];
RETURN[FALSE]
END;
FixupCreateLine: PUBLIC PROC =
BEGIN
IF createButton # NIL THEN ViewerOps.DestroyViewer[createButton];
IF msForCreateButton # NIL THEN ViewerOps.DestroyViewer[msForCreateButton];
IF msgSetText # NIL THEN ViewerOps.DestroyViewer[msgSetText];
IF readOnlyAccess THEN
createButton← WalnutViewer.AnotherLabel[name: "", sib: fileNameText, newLine: TRUE]
ELSE
{ walnutWidth: INTEGER← IF walnut.column = right THEN ViewerSpecs.openRightWidth
ELSE ViewerSpecs.openLeftWidth;
createButton← WalnutViewer.ImmediateButton[name: "Create", proc: CreateMsgSetButtonProc,
border: TRUE, sib: fileNameText, fork: FALSE, newLine: TRUE];
msForCreateButton← WalnutViewer.ImmediateButton[
name: "MessageSet:", proc: MsgSetNameProc, border: FALSE, sib: createButton];
msgSetText← WalnutViewer.NextRightTextViewer[sib: msForCreateButton, w: walnutWidth];
Containers.ChildXBound[walnut, msgSetText]
}
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
QueueExecCall: PUBLIC ENTRY PROC[proc: PROC[REF ANY], ra: REF ANY] =
BEGIN ENABLE UNWIND => NULL;
IF walnut # NIL THEN IF doingCheckpoint THEN WAIT rollbackFinished;
WQueue.QueueClientAction[walnutQueue, proc, ra];
END;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ChangeWalnutMenu: PUBLIC PROC[menu: Menus.Menu] =
BEGIN
oldCount: Menus.MenuLine = Menus.GetNumberOfLines[walnut.menu];
newCount: Menus.MenuLine = Menus.GetNumberOfLines[menu];
walnut.menu ← menu;
IF oldCount # newCount THEN
ViewerBLT.ChangeNumberOfLines[walnut, newCount];
ViewerOps.PaintViewer[walnut, menu];
END;
UserConfirmed: PUBLIC ENTRY PROC RETURNS[BOOL] =
BEGIN ENABLE UNWIND => NULL;
RETURN[InternalConfirm[confirmMenu]];
END;
InternalConfirm: PUBLIC INTERNAL PROC[m: Menus.Menu← NIL] RETURNS[BOOL] =
BEGIN
ChangeWalnutMenu[IF m = NIL THEN confirmMenu ELSE m];
IF walnut.iconic THEN ViewerOps.BlinkIcon[walnut, 0] ELSE ViewerOps.BlinkIcon[walnutTS];
UNTIL userHasResponded DO WAIT responseSynch; ENDLOOP;
userHasResponded← FALSE;
ChangeWalnutMenu[workingMenu];
RETURN[userConfirmed];
END;
---------------------------
walnutMenu: PUBLIC Menus.Menu;
workingMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
confirmMenu: Menus.Menu← Menus.CreateMenu[];
blankMenu: Menus.Menu← Menus.CreateMenu[];
startupConfirmMenu: Menus.Menu← Menus.CreateMenu[];
forceQuitMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
maybeQuitMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
scavMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
mailDBMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
readOnlyDBMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
nonMailDBMenu: PUBLIC Menus.Menu← Menus.CreateMenu[];
BuildWalnutMenu: PROC =
{ OPEN Menus;
-- menu for personal mail database
AppendMenuEntry[mailDBMenu, CreateEntry["NewForm", NewMsgProc]];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "NewMail", NewMailProc]];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "CloseAll", CloseAllProc]];
AppendMenuEntry[mailDBMenu, CreateEntry["MessageSetOps", MsgSetOpsProc]];
-- now for the second line
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[q: walnutQueue, name: "Expunge", proc: MenuExpunge,
guarded: TRUE], 1];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Delete", DeleteMsgSetsProc], 1];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "SizeOf", SizeOfMsgSetsProc], 1];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Print", PrintMsgSetsProc], 1];
Menus.ChangeNumberOfLines[mailDBMenu, 1];
-- menu for read only database
AppendMenuEntry[readOnlyDBMenu, CreateEntry["NewForm", NewMsgProc]];
AppendMenuEntry[readOnlyDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "CloseAll", CloseAllProc]];
AppendMenuEntry[readOnlyDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "SizeOf", SizeOfMsgSetsProc]];
AppendMenuEntry[readOnlyDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Print", PrintMsgSetsProc]];
-- menu for nonMail writeable database
AppendMenuEntry[nonMailDBMenu, CreateEntry["NewForm", NewMsgProc]];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "CloseAll", CloseAllProc]];
AppendMenuEntry[nonMailDBMenu, CreateEntry["MessageSetOps", MsgSetOpsProc]];
-- now for the second line
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[q: walnutQueue, name: "Expunge", proc: MenuExpunge,
guarded: TRUE], 1];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Delete", DeleteMsgSetsProc], 1];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "SizeOf", SizeOfMsgSetsProc], 1];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Print", PrintMsgSetsProc], 1];
Menus.ChangeNumberOfLines[nonMailDBMenu, 1];
AppendMenuEntry[workingMenu, CreateEntry["NewForm", NewMsgProc]];
AppendMenuEntry[confirmMenu, CreateEntry["NewForm", NewMsgProc]];
AppendMenuEntry[confirmMenu, CreateEntry["Confirm", WWConfirmProc]];
AppendMenuEntry[confirmMenu, CreateEntry["Deny", WWDenyProc]];
AppendMenuEntry[startupConfirmMenu, CreateEntry["Continue", WWConfirmProc]];
AppendMenuEntry[startupConfirmMenu, CreateEntry["Quit", WWDenyProc]];
AppendMenuEntry[forceQuitMenu, CreateEntry["Quit", WWConfirmProc]];
AppendMenuEntry[maybeQuitMenu, CreateEntry["Quit", WWConfirmProc]];
AppendMenuEntry[maybeQuitMenu, CreateEntry["Retry", WWDenyProc]];
AppendMenuEntry[scavMenu, CreateEntry["Scavenge", WWConfirmProc]];
AppendMenuEntry[scavMenu, CreateEntry["Quit", WWDenyProc]];
};
ChangeDBParams: TYPE = REF ChangeDBObject;
ChangeDBObject: TYPE =
MONITORED RECORD[segName, logName: ROPE, logIsAlp, alpNeeded: BOOL, cond: CONDITION];
ChangeDB: PROC[cdb: ChangeDBParams] = { DoWaitCall[ChangeDBProc, cdb] };
ChangeDatabase: PUBLIC ENTRY PROC[ra: REF ANY] =
BEGIN
cdb: ChangeDBParams← NARROW[ra];
BEGIN ENABLE UNWIND => { NOTIFY cdb.cond };
walnut.inhibitDestroy← TRUE;
ChangeWalnutMenu[workingMenu];
Report["\nClosing database on ", walnutSegmentFile];
TakeDownWalnutViewers[];
DestroyAllMsgSetButtons[];
FlushWQueue[];
CloseTransactions[TRUE ! DB.Failure, IO.Error, UNWIND => CONTINUE];
WalnutRetrieve.CloseConnection[];
lastStateReported← unknown;
walnutSegmentFile← cdb.segName;
walnutLogName← cdb.logName;
logIsAlpineFile← cdb.logIsAlp;
alpineNeeded← cdb.alpNeeded;
WalnutDisplayerOps.msgName ← Rope.Concat[walnutSegmentFile, "!Msg!"];
WalnutDisplayerOps.msgSetName ← Rope.Concat[walnutSegmentFile, "!MsgSet!"];
[]← StartOrRestartWalnut[TRUE];
Report["Database is now ", walnutSegmentFile];
NOTIFY cdb.cond;
END;
END;
NewMsgProc: Menus.MenuProc = { WalnutSendOps.WalnutSendProc[fromExec: FALSE]};
-- Create a new blank form in a viewer for user to send msg with.
MsgSetOpsProc: Menus.MenuProc =
BEGIN
v: Viewer = NARROW[parent];
count: NAT = Menus.GetNumberOfLines[v.menu];
newCount: NAT = IF count = 2 THEN 1 ELSE 2;
Menus.ChangeNumberOfLines[v.menu, newCount];
ViewerBLT.ChangeNumberOfLines[v, newCount];
END;
MenuExpunge: Menus.MenuProc =
{ EntryExpunge: ENTRY PROC =
BEGIN ENABLE UNWIND => NULL;
DoExpunge[doUpdates: TRUE, tailRewrite: mouseButton=red];
FinishCall[];
END;
EntryExpunge[];
};
DoExpunge: PUBLIC PROC[doUpdates, tailRewrite: BOOL] =
BEGIN
ok: BOOL← TRUE;
tempLogName: ROPE← "Walnut.TempLog";
tempLog: IO.STREAM;
ChangeWalnutMenu[workingMenu];
TakeDownWalnutViewers[];
DestroyAllMsgSetButtons[];
WalnutLog.MarkWalnutTransaction[];
FlushWQueue[];
tempLog← FS.StreamOpen[tempLogName, $create];
Report["Dumping message database to ", tempLogName, "..."];
BEGIN ENABLE ABORTED => {ok← FALSE; CONTINUE};
ok← WalnutLog.ExpungeMsgs
[tempLog: tempLog, doUpdates: doUpdates, tailRewrite: (tailRewrite AND enableTailRewrite)];
END;
IF ~ok THEN
{ DB.AbortTransaction[
DB.TransactionOf[segmentName ! DB.Aborted, DB.Failure => CONTINUE]];
Report["Expunge failed or aborted; changes to database have been aborted"];
}
ELSE Report[" Expunge complete"];
ChangeWalnutMenu[walnutMenu];
END;
MyCommitProc: Menus.MenuProc =
{ ENABLE UNWIND => ChangeWalnutMenu[walnutMenu];
ChangeWalnutMenu[workingMenu];
WalnutLog.MarkWalnutTransaction[];
ChangeWalnutMenu[walnutMenu]
};
NewMailProc: Menus.MenuProc =
{ ENABLE UNWIND => ChangeWalnutMenu[walnutMenu];
ChangeWalnutMenu[workingMenu];
RetrieveNewMail[];
ChangeWalnutMenu[walnutMenu]
};
AutoNewMailProc: PROC =
BEGIN ENABLE UNWIND => tryForNewMail← TRUE;
DoWaitCall[RetrieveNewMailProc];
tryForNewMail← TRUE;
END;
RetrieveNewMail: PUBLIC ENTRY PROC =
BEGIN ENABLE UNWIND => NULL;
IF doingCheckpoint THEN WAIT rollbackFinished;
InternalNewMail[];
END;
InternalNewMail: PUBLIC PROC =
BEGIN ENABLE UNWIND => tryForNewMail← TRUE;
thisUser: ROPE← UserCredentials.Get[].name;
IF ~Rope.Equal[thisUser, previousUser, FALSE] THEN
{ r: ROPE← "User has changed";
Report[IO.PutFR
["******%g may not retrieve %g's new mail", IO.rope[thisUser], IO.rope[previousUser]]];
Report["You will be forced to quit out of Walnut; you should then Login to the exec"];
FlushWQueue[];
WQueue.QueueClientAction[walnutQueue, QuitWalnut, r];
}
ELSE
{ tryForNewMail← FALSE;
DoNewMail[];
tryForNewMail← TRUE
};
END;
CloseAllProc: Menus.MenuProc =
BEGIN
msgSetList, msgList, queryList: LIST OF Viewer;
[msgSetList, msgList, queryList]← EnumWalnutViewers[TRUE];
FOR vL: LIST OF Viewer← msgList, vL.rest UNTIL vL=NIL DO
ViewerOps.DestroyViewer[vL.first]; ENDLOOP;
FOR vL: LIST OF Viewer← queryList, vL.rest UNTIL vL=NIL DO
ViewerOps.CloseViewer[vL.first]; ENDLOOP;
FOR vL: LIST OF Viewer← msgSetList, vL.rest UNTIL vL=NIL DO
ViewerOps.CloseViewer[vL.first]; ENDLOOP;
MyCommitProc[parent];
ViewerOps.CloseViewer[walnut];
END;
QuitProc: ViewerEvents.EventProc =
BEGIN
IF walnutEventReg = NIL THEN RETURN[FALSE];
TRUSTED {Process.Detach[ FORK QuitWalnut[NIL]]};
RETURN[TRUE];
END;
WWDenyProc: ENTRY Menus.MenuProc =
{ ENABLE UNWIND => NULL;
userConfirmed← FALSE;
userHasResponded← TRUE;
BROADCAST responseSynch
};
WWConfirmProc: ENTRY Menus.MenuProc =
{ ENABLE UNWIND => NULL;
userConfirmed← TRUE;
userHasResponded← TRUE;
BROADCAST responseSynch
};
----------------------------
MsgSetNameProc: Buttons.ButtonProc =
BEGIN
ViewerTools.SetSelection[msgSetText, NIL];
END;
FileNameProc: Buttons.ButtonProc =
BEGIN
ViewerTools.SetSelection[fileNameText, NIL];
END;
-- **********************************************************************
-- can't be an entry proc
SetWalnutUpdatesPending: PUBLIC PROC[newVersion: BOOL] =
BEGIN
old: BOOL;
IF walnut = NIL OR walnut.destroyed THEN RETURN;
old← walnut.newVersion;
IF newVersion#old THEN
{walnut.newVersion← newVersion; ViewerOps.PaintViewer[walnut, caption]};
END;
UncomittedUpdates: PUBLIC ENTRY PROC RETURNS[updatesPending: BOOL] =
{ ENABLE UNWIND => NULL;
RETURN[IF walnut#NIL THEN walnut.newVersion ELSE FALSE];
};
-- **********************************************************************
Report: PUBLIC PROC[msg1, msg2, msg3, msg4: ROPE← NIL] =
{ IF wtsOut = NIL THEN
{ IF msg1#NIL THEN MessageWindow.Append[msg1];
IF msg2#NIL THEN MessageWindow.Append[msg2];
IF msg3#NIL THEN MessageWindow.Append[msg3];
IF msg4#NIL THEN MessageWindow.Append[msg4];
RETURN
};
IF msg1#NIL THEN wtsOut.PutRope[msg1];
IF msg2#NIL THEN wtsOut.PutRope[msg2];
IF msg3#NIL THEN wtsOut.PutRope[msg3];
IF msg4#NIL THEN wtsOut.PutRope[msg4];
wtsOut.PutChar[IO.CR];
};
ReportRope: PUBLIC PROC[msg1: ROPE] =
{ IF wtsOut # NIL THEN wtsOut.PutRope[msg1] ELSE MessageWindow.Append[msg1];
};
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
WatchMailBox: PUBLIC PROC[newState: GVRetrieve.MBXState] =
-- This is called when the condition of the mailbox changes
BEGIN
showTime: BOOL← TRUE;
status: ROPE;
icon: Icons.IconFlavor← walnutIcon;
IF walnut = NIL THEN RETURN;
IF walnut.destroyed THEN RETURN;
IF newState = unknown THEN RETURN;
IF (lastStateReported = notEmpty) AND (newState = someEmpty OR newState = allEmpty) THEN
status← "New mail was read"
ELSE
{ SELECT newState FROM
badName => {status← "Your user name is invalid, please log in"; showTime← FALSE};
badPwd => {status← "Your password is invalid"; showTime← FALSE};
cantAuth => {status← "Can't check your credentials at this time"; showTime← FALSE};
userOK => {status← "Your credentials are OK"; showTime← FALSE};
allDown => status← "All of the mail servers are down";
someEmpty => status← "All of the mail servers checked are empty";
allEmpty => status← "There is no new mail";
notEmpty => {status← "You have new mail"; icon← newMailIcon};
ENDCASE => status← "Bad State!";
};
lastStateReported ← newState;
IF walnut.icon # icon THEN
{ walnut.icon← icon; IF walnut.iconic THEN ViewerOps.PaintViewer[walnut, all]};
Labels.Set[mailNotifyLabel,
IF showTime THEN Rope.Concat[status, IO.PutFR[" at %g", IO.time[]]] ELSE status];
IF newState = notEmpty AND autoNewMail AND tryForNewMail THEN
{ tryForNewMail← FALSE;
TRUSTED {Process.Detach[FORK AutoNewMailProc]};
};
END;
SetAutoNewMailVars: ENTRY UserProfile.ProfileChangedProc = CHECKED
BEGIN ENABLE UNWIND => NULL;
autoNewMail← UserProfile.Boolean[key: "Walnut.AutoNewMail", default: FALSE];
tryForNewMail← autoNewMail;
END;
----------------------------
walnutQueue← WQueue.Create[pushModel: FALSE]; -- queue for menu/button pushes
BuildWalnutMenu[];
Commander.Register["Walnut", BuildWalnut, "For retrieving and sending mail"];
Commander.Register["WalnutScavenge", WalnutScavenge, "Rebuilds Mail database from log file"];
Commander.Register["WalnutOldMailReader",
WalnutReadMailFile, "Reads Laurel, Hardy or Archive files into Mail database"];
Commander.Register["WalnutExpunge", WalnutExpunge, "Expunges deleted messages"];
Commander.Register["WalnutDump", WalnutDump, "Writes an expunged log file on Walnut.TempLog; does not change database"];
Commander.Register["WalnutNewMail", WalnutNewMail, "Retrieves new mail"];
UserProfile.CallWhenProfileChanges[SetAutoNewMailVars];
END.