File: WalnutWindowImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Contents: Top level Viewer for Walnut; initializes database & displays control window
created February, 1981 by Willie-Sue & Rick
Last edit by:
Willie-Sue on: August 28, 1984 11:44:43 am PDT
Donahue, July 26, 1983 9:13 am
Last Edited by: Woosh, August 23, 1984 2:19:53 pm PDT
DIRECTORY
AlpineWalnutCmds USING [CopyForExpunge],
Buttons USING [ButtonProc],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [Run],
Containers,
DefaultRemoteNames USING [Get],
DB USING [Aborted, Failure, Transaction, AbortTransaction, DeclareSegment,
Initialize, TransactionOf],
Labels USING [Set],
FS USING [Error, SetKeep],
GVRetrieve USING [MBXState],
Icons USING [IconFlavor, NewIconFromFile],
IO,
MBQueue 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],
ViewerIO USING [CreateViewerStreams],
WalnutControlPrivate USING [CloseTransactions, ClosingWalnut, FlushWQueue, QuitWalnut,
StartOrRestartWalnut, WalnutNotifier],
WalnutControlMonitorImpl,
WalnutDB USING [RegisterUpdatesPendingProc, UnRegisterUpdatesPendingProc],
WalnutDBLog USING [GetStartExpungePos],
WalnutDisplayerOps USING [msgName, msgSetName],
WalnutLog USING [ExpungeMsgs, LogLength, MarkWalnutTransaction],
WalnutLogExtras USING [onlyOneTempLog, walnutTempLogName, QuietlyMarkTransaction],
WalnutExtras USING [ChangeDBProc, DoWaitCall, DumpProc, EnumWalnutViewers,
EstablishFileNames, ExpungeProc, ReadMailProc, RetrieveNewMailProc,
ScavengeProc, SetWalnutIcons, TakeDownWalnutViewers],
WalnutRetrieve USING [CloseConnection],
WalnutSendOps USING [userRName, RegisterReporter, UnregisterReporter, WalnutSendProc],
WalnutViewer USING [CreateMenuEntry, FirstLabel, MakeRuler, MakeTypescript],
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, DefaultRemoteNames,
Containers, Icons, Labels, MBQueue, Menus, MessageWindow, Rules,
IO, PrincOpsUtils, Process, Rope,
UserCredentials, UserProfile,
ViewerBLT, ViewerEvents, ViewerOps, ViewerIO, ViewerSpecs,
walnutControlLock: WalnutControlMonitorImpl,
WalnutControlPrivate, WalnutDB, WalnutDBLog, WalnutDisplayerOps, WalnutExtras,
WalnutLog, WalnutLogExtras, 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.STREAMNIL;
walnutIcon: PUBLIC Icons.IconFlavor← tool;
newMailIcon: PUBLIC Icons.IconFlavor← tool;
msgSetIcon: PUBLIC Icons.IconFlavor← tool;
msgIcon: PUBLIC Icons.IconFlavor← tool;
iconFilePathName: PUBLIC ROPENIL;
walnutEventReg: ViewerEvents.EventRegistration← NIL;
mustQuitWalnut: PUBLIC ROPENIL;
previousUser: PUBLIC ROPENIL;
doingCheckpoint: PUBLIC BOOLFALSE;
rollbackFinished: PUBLIC CONDITION;
initialActiveIconic: PUBLIC BOOLFALSE;
initialActiveRight: PUBLIC BOOLTRUE;
initialActiveOpen: PUBLIC BOOLTRUE;
msgSetBorders: PUBLIC BOOLFALSE;
enableTailRewrite: PUBLIC BOOLFALSE;
excessBytesInLogFile: PUBLIC INT← 300000;
walnutRulerBefore, walnutRulerAfter, walnutTS: PUBLIC 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: BOOLFALSE;
tryForNewMail: BOOLFALSE;
doAutoCommit: BOOLFALSE;
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: BOOLFALSE;
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]← 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;
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: BOOL] =
BEGIN
sName, lName: ROPE;
alpServer: ROPE = "[Luther.alpine]<";
seg: ROPE = ">Walnut.segment";
log: ROPE = ">Walnut.DBLog";
user: ROPE← UserCredentials.Get[].name;
personalMailDB← TRUE;
IF dirName.Length = 0 THEN
{ sName← UserProfile.Token[key: "Walnut.WalnutSegmentFile", default: ""];
lName← UserProfile.Token[key: "Walnut.WalnutLogFile", default: ""];
IF sName.Length[] = 0 THEN sName← Rope.Cat[alpServer, user, seg];
IF lName.Length[] = 0 THEN lName← Rope.Cat[alpServer, user, log];
captionName← walnutVersion;
}
ELSE
{ sName← Rope.Cat[alpServer, dirName, seg];
lName← Rope.Cat[alpServer, dirName, log];
personalMailDB← FALSE;
captionName← Rope.Cat[walnutVersion, " <", dirName, ">"];
};
[segName, logName, logIsAlp]← 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
doAutoCommit← UserProfile.Boolean[key: "Walnut.DoAutoCommit", default: TRUE];
WalnutLogExtras.onlyOneTempLog←
UserProfile.Boolean[key: "Walnut.OnlyOneTempLog", default: TRUE];
SetTempLogName[];
IF ~BuildWalnutControlWindow[captionName] THEN RETURN;
if doAutoCommit is true, then don't show Commit on the menus
IF doAutoCommit THEN
BEGIN
old: Menus.MenuEntry;
IF (old← Menus.FindEntry[mailDBMenu, "Commit"])#NIL THEN
Menus.ReplaceMenuEntry[mailDBMenu, old, NIL];
IF (old← Menus.FindEntry[nonMailDBMenu, "Commit"])#NIL THEN
Menus.ReplaceMenuEntry[nonMailDBMenu, old, NIL];
END
ELSE
BEGIN
IF Menus.FindEntry[mailDBMenu, "Commit"] = NIL THEN
Menus.AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]];
IF Menus.FindEntry[nonMailDBMenu, "Commit"] = NIL THEN
Menus.AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Commit", MyCommitProc]];
END;
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[];
InternalChangeMenu[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;
IF walnutEventReg # NIL THEN
ViewerEvents.UnRegisterEventProc[walnutEventReg, destroy];
walnutEventReg← NIL;
walnut← NIL;  -- don't let others try to use Report
WalnutRetrieve.CloseConnection[];  -- NOP if no open connection
lastStateReported← unknown;
mailNotifyLabel← NIL;
WalnutSendOps.UnregisterReporter[wtsOut];
wtsOut.Close[];
wtsOut← NIL;
mustQuitWalnut← NIL;
MBQueue.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];
EXITS
noFile => Report["No file specified"];
END;
make separate proc so callable by program
ReadMailFile: PUBLIC PROC[fName, msgSet: ROPE] =
BEGIN
mfp: LIST OF ROPELIST[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: BOOL;
personalMailDB← TRUE;
[segName, logName, captionName, logIsAlp]← CmdProcCall[NIL, cmd];
IF segName=NIL THEN RETURN;
walnutSegmentFile← segName;
walnutLogName← logName;
logIsAlpineFile← logIsAlp;
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;
InternalChangeMenu[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: BOOLTRUE;
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};
remote: ROPE← DefaultRemoteNames.Get[].current;
iconFilePathName← Rope.Concat[remote, "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← walnutRulerBefore← WalnutViewer.MakeRuler[sib: mailNotifyLabel, 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
OpenTS[doRegister: FALSE];
TRUSTED
BEGIN
DO
IF ~PrincOpsUtils.IsBound[WalnutSendOps.RegisterReporter] THEN
{IF ~ContinueThisWalnut["WalnutSend"] THEN RETURN[FALSE] ELSE LOOP};
IF ~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;
tsLogName: ROPE = "///Temp/Walnut/Walnut.TypescriptLog";
OpenTS: PUBLIC PROC[msg: ROPENIL, doRegister: BOOLTRUE] =
BEGIN
IF wtsOut = NIL THEN
{ wtsOut← ViewerIO.CreateViewerStreams[NIL, walnutTS, tsLogName].out;
IF msg # NIL THEN wtsOut.PutRope[msg];
IF doRegister THEN WalnutSendOps.RegisterReporter[wtsOut];
};
END;
CloseTS: PUBLIC PROC =
BEGIN
IF wtsOut # NIL THEN wtsOut.Close[];
WalnutSendOps.UnregisterReporter[wtsOut];
wtsOut← NIL;
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];
mailNotifyLabel← NIL;
v← walnut;
walnut← NIL;
CloseTS[];
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[errMsg=NIL];
IF errMsg.Find["unbound imports", 0, FALSE] >= 0 THEN RETURN[TRUE];
RETURN[FALSE]
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;
MBQueue.QueueClientAction[walnutQueue, proc, ra];
END;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ChangeWalnutMenu: PUBLIC ENTRY PROC[menu: Menus.Menu] =
BEGIN ENABLE UNWIND => NULL;
InternalChangeMenu[menu]
END;
InternalChangeMenu: PUBLIC INTERNAL PROC[menu: Menus.Menu] =
BEGIN ENABLE UNWIND => NULL;
oldCount, newCount: Menus.MenuLine;
IF walnut = NIL THEN RETURN;
oldCount← Menus.GetNumberOfLines[walnut.menu];
newCount← 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
InternalChangeMenu[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;
InternalChangeMenu[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];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Create", CreateMsgSetButtonProc], 1];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Archive", ArchiveButtonProc], 1];
AppendMenuEntry[mailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Append", AppendButtonProc], 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]];
AppendMenuEntry[readOnlyDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Archive", ArchiveButtonProc], 1];
AppendMenuEntry[readOnlyDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Append", AppendButtonProc], 1];
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];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Create", CreateMsgSetButtonProc], 1];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Archive", ArchiveButtonProc], 1];
AppendMenuEntry[nonMailDBMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Append", AppendButtonProc], 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 ENABLE UNWIND => NULL;
cdb: ChangeDBParams← NARROW[ra];
BEGIN ENABLE UNWIND => { NOTIFY cdb.cond };
walnut.inhibitDestroy← TRUE;
InternalChangeMenu[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;
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;
IF shift THEN
{ start: INT← WalnutDBLog.GetStartExpungePos[];
len: INT← WalnutLog.LogLength[FALSE];
dif: INT;
Report[IO.PutFR[" The log file is %g bytes (%g pages) long",
IO.int[len], IO.int[(len+511)/512]]];
Report[IO.PutFR[" An expunge would start at pos %g and read %g bytes (%g pages) of the log",
IO.int[start], IO.int[dif← (len - start)], IO.int[(dif+511)/512]]];
}
ELSE EntryExpunge[];
};
DoExpunge: PUBLIC INTERNAL PROC[doUpdates, tailRewrite: BOOL] =
BEGIN
ok: BOOLTRUE;
tempLogName: ROPE← "Walnut.TempLog";
InternalChangeMenu[workingMenu];
TakeDownWalnutViewers[];
DestroyAllMsgSetButtons[];
WalnutLog.MarkWalnutTransaction[];
FlushWQueue[];
IF WalnutLogExtras.walnutTempLogName = NIL THEN SetTempLogName[];
Report[IO.PutFR["Dumping message database to %g at %g",
IO.rope[WalnutLogExtras.walnutTempLogName], IO.time[]]];
BEGIN ENABLE ABORTED => {ok← FALSE; CONTINUE};
ok← WalnutLog.ExpungeMsgs
[tempLog: NIL, 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"];
InternalChangeMenu[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[];
MBQueue.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: ENTRY ViewerEvents.EventProc =
BEGIN
IF walnutEventReg = NIL THEN RETURN[FALSE];
MBQueue.QueueClientAction[walnutQueue, 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
};
**********************************************************************
can't be an entry proc
SetWalnutUpdatesPending: PUBLIC PROC[newVersion: BOOL] =
BEGIN
old: BOOL;
IF walnut = NIL OR walnut.destroyed OR doAutoCommit THEN RETURN;
old← walnut.newVersion;
IF newVersion#old THEN
{walnut.newVersion← newVersion; ViewerOps.PaintViewer[walnut, caption]};
END;
CheckForAutoCommit: PUBLIC INTERNAL PROC =
BEGIN
IF doAutoCommit THEN WalnutLogExtras.QuietlyMarkTransaction[];
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: ROPENIL] =
{ 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: BOOLTRUE;
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;
SetTempLogName[];
END;
SetTempLogName: PROC =
{ WalnutLogExtras.walnutTempLogName←
Rope.Cat["///Walnut/", WalnutSendOps.userRName, "/Walnut.TempLog"];
};
--------------------------
walnutQueue← MBQueue.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.