WalnutMsgSetDisplayerImpl.mesa
Copyright Ó 1985, 1989, 1991, 1992 by Xerox Corporation. All rights reserved.
Willie-sue, February 15, 1993 4:41 pm PST
Donahue, May 9, 1986 10:39:11 am PDT
Jack Kent April 9, 1987 4:56:33 pm PDT
Doug Terry, October 2, 1992 10:25 am PDT
Swinehart, April 11, 1993 8:49 pm PDT
Chauser, October 3, 1991 2:57 pm PDT
Contents: Implementation of the WalnutMsgSet displayers.
(Changed message set version number to be part of viewer information, rather than part of message set buttons)
(Display now checks to see if versions match and does not do redisplay if so)
(Changed to use TiogaButtons - May 6, 1985)
(Use new TiogaButtons capability permitting inter-document insertion of new nodes. Use it to sort displayed message sets by date. D. Swinehart February 3, 1991)
DIRECTORY
Basics USING [Comparison],
BasicTime USING [earliestGMT, GMT],
Containers USING [ChildXBound, ChildYBound],
Convert USING [ Error, TimeFromRope ],
EditSpan USING [ Place ],
Menus USING [MouseButton, Menu, MenuEntry, MenuLine, MenuProc, AppendMenuEntry, ChangeNumberOfLines, CreateMenu, CreateEntry, GetNumberOfLines, GetLine, SetLine],
PopUpSelection USING [Request],
Process USING [Pause, PauseMsec, MsecToTicks],
RedBlackTree USING [ Compare, Create, DestroyTable, DuplicateKey, EachNode, EnumerateIncreasing, GetKey, Insert, LookupNextLarger, Table ],
Rope,
SendMailOps USING [CreateSendViewer, GetCRTiogaContents],
TBQueue USING [CreateTiogaButtonAtNode],
TiogaButtons USING [ TiogaButton, TiogaButtonProc, CreateViewer, ChangeButtonLooks, DeleteButton, SetState, SetStyleFromRope],
TiogaMenuOps USING [AllLevels, FewerLevels, FirstLevelOnly, MoreLevels, Normalize, Position, PrevPlace],
TiogaOps USING [CancelSelection, Delete, GetSelection, FindDef, FindText, FindWord, InsertChar, InsertRope, RestoreSelA, SaveSelA, SearchDir, SetSelection],
UserProfile USING [Boolean, CallWhenProfileChanges, ProfileChangedProc, Number, Token],
ViewerClasses USING [Column, Viewer],
ViewerBLT USING [ChangeNumberOfLines],
ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc],
ViewerOps USING [AddProp, ComputeColumn, CreateViewer, BlinkIcon, DestroyViewer,
FetchProp, GrowViewer, OpenIcon, PaintViewer, SetMenu],
ViewerSpecs USING [bwScreenHeight, openLeftWidth, openRightWidth],
ViewerTools USING [TiogaContents, TiogaContentsRec, GetSelectedViewer, GetSelectionContents],
WalnutDefs USING [dontCareMsgSetVersion, Error],
WalnutFilter USING [InitialSortOrder, MenuExtras, MessageTOC, MsgInterestLevel, SortCompareProc],
WalnutOps USING [ AcceptNewMail, ActiveName, AddMsg, CreateMsg, DeletedName, dontCareMsgSetVersion, EnumerateMsgsInSet, GenerateUniqueMsgName, GetCategories, GetDisplayProps, GetMsgSize, GetNewMail, MsgExists, MsgSet, MoveMsg, MsgSetExists, RemoveMsg, ServerInfo, SetHasBeenRead, SizeOfMsgSet],
WalnutInternal USING [DisplayMsgFromMsgSet, DoRemoveOverThere, DoWaitCall, GetButton, GetSelectedMsgSets, HowToPrint, initialActiveIconic, initialActiveOpen, msgSetIcon, MsgCategories, MsgSetPrintProc, MsgSetTOCPrintProc, PrintMsgList, SetMailState],
WalnutViewer USING [CreateMenuEntry],
WalnutWindow USING [GetNewMail, GetRootFileForHandle, Report, ReportFormat, ReportRope],
WalnutWindowPrivate USING [MsgAndHandle, MsgSetButton, MsgSetFieldHandle, MsgSetFieldHandleRec, MsgInfo, MsgInfoRec, MsgSetInfo, MsgSetInfoRec, WalnutHandle, WalnutHandleRec];
WalnutMsgSetDisplayerImpl: CEDAR MONITOR
IMPORTS
BasicTime, Convert, Menus, Process, PopUpSelection, RedBlackTree, Rope, SendMailOps, UserProfile,
WalnutDefs, WalnutFilter, WalnutOps,
WalnutInternal, WalnutWindow, WalnutViewer,
Containers, TBQueue, TiogaButtons, TiogaMenuOps, TiogaOps,
ViewerBLT, ViewerEvents, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS
WalnutInternal, WalnutWindow
SHARES
ViewerClasses =
BEGIN OPEN WalnutInternal;
Types
TiogaButton: TYPE = TiogaButtons.TiogaButton;
Viewer: TYPE = ViewerClasses.Viewer;
ROPE: TYPE = Rope.ROPE;
HowToPrint: TYPE = WalnutInternal.HowToPrint;
MsgSet: TYPE = WalnutOps.MsgSet;
MsgSetButton: TYPE = WalnutWindowPrivate.MsgSetButton;
WalnutHandle: TYPE = WalnutWindowPrivate.WalnutHandle;
WalnutHandleRec: PUBLIC TYPE = WalnutWindowPrivate.WalnutHandleRec;
tocDefaultLooks: PUBLIC ROPE;
tocSelectedLooks: PUBLIC ROPE;
tocUnreadLooks: PUBLIC ROPE;
userWantsQMs: PUBLIC BOOL¬FALSE;
scrollWaitTime: CARDINAL ¬ 500;  -- Milliseconds
openHeightForActive: INT ¬ 0;
DisplayingRef: TYPE = REF DisplayingRefRec;
DisplayingRefRec: TYPE = RECORD[userSaidStop: BOOL];
Only common menu used in message set displays
buildingMenu: Menus.Menu ¬ Menus.CreateMenu[];
fillingInMenu: Menus.Menu ¬ Menus.CreateMenu[];
Types used in message set displays
MsgSetInfo: TYPE = WalnutWindowPrivate.MsgSetInfo;
MsgSetInfoRec: TYPE = WalnutWindowPrivate.MsgSetInfoRec;
button: MsgSetButton, -- the button in the control panel that this msgSet is bound to
tiogaViewer: Viewer, -- only child, $TiogaButton viewer
container: Viewer,  -- back pointer to msgSet viewer
selected: LIST OF MsgSetFieldHandle,
lastMFH: MsgSetFieldHandle,
lastDeleted: MsgInfo,  -- last message deleted from this message set
destroyER: ViewerEvents.EventRegistration
];
MsgSetFieldHandle: TYPE = WalnutWindowPrivate.MsgSetFieldHandle;
MsgSetFieldHandleRec: TYPE = WalnutWindowPrivate.MsgSetFieldHandleRec;
next, prev: MsgSetFieldHandle,
tocButton: TiogaButton,  -- TableOfContentsButton
msI: MsgSetInfo,  -- back pointer to msgSetInfo
msgInfo: MsgInfo  -- constant for a message
];
MsgSetFieldHandles: TYPE ~ LIST OF MsgSetFieldHandle;
May need to be expanded to a record, at some point.
MsgSetOpHandle: TYPE ~ REF MsgSetOpHandleRec;
MsgSetOpHandleRec: TYPE ~ RECORD [
mfH: MsgSetFieldHandle¬NIL,
msI: MsgSetInfo¬NIL,
msViewer: Viewer¬NIL,
wH: WalnutHandle¬NIL,
mouseButton: Menus.MouseButton¬$red,
shift, control: BOOL¬FALSE,
lastSelected: BOOL¬FALSE
];
MsgInfo: TYPE = WalnutWindowPrivate.MsgInfo;
MsgInfoRec: TYPE = WalnutWindowPrivate.MsgInfoRec;
MsgInfoRec: TYPE = RECORD[ msg, tocName: ROPE, startOfSubject: INT, hasBeenRead: BOOL, date: BasicTime.GMT ];
Rope literals
destroyedMsg: ROPE = "Selected msg viewer has been destroyed";
noSelDest: ROPE = "No selected destination message sets";
noSelSource: ROPE = "No selected source messages or message sets";
moreSelSource: ROPE = "More than one selected message; not permitted for this operation";
noMsgRope: ROPE = "Msg is no longer in the MsgSet";
noCurrentSel1: ROPE = "No selection to extend";
noCurrentSel2: ROPE = "Message is not currently selected";
noNext: ROPE = "No Next message";
noUndo: ROPE = "Nothing to undo";
comma: ROPE = ", ";
movedTo: ROPE = "Moved to: ";
movedFrom: ROPE = "Moved from: ";
deletedFrom: ROPE = "Deleted from: ";
addedToRL: ROPE = "Added to: ";
appendedTo: ROPE = "Appended to: ";
removedFrom: ROPE = "Removed from: ";
space: ROPE = " ";
dollarSign: ROPE = "$";
questionMark: ROPE = "?";
atSign: ROPE = "@";
tabChar: ROPE = "\t";
newLine: ROPE = "\n";
walnutDotFancyNewMessageSelect: ROPE = "Walnut.FancyNewMessageSelect";
walnutDotMsgSetColumn: ROPE = "Walnut.MsgSetColumn";
walnutDotActiveMsgSetColumn: ROPE = "Walnut.ActiveMsgSetColumn";
walnutDotMovieMailFrameRate: ROPE = "Walnut.MovieMailFrameRate";
formatGToGNewLine: ROPE = "%g to %g\n";
leftRL: ROPE = "left";
rightRL: ROPE = "right";
findRL: ROPE = "Find";
firstLevelOnlyRL: ROPE = "FirstLevelOnly";
categoriesRL: ROPE = "Categories";
moveToRL: ROPE = "MoveTo";
displayRL: ROPE = "Display";
deleteRL: ROPE = "Delete";
addToRL: ROPE = "AddTo";
placesRL: ROPE = "Places";
levelsRL: ROPE = "Levels";
newMailRL: ROPE = "NewMail";
msgOpsRL: ROPE = "MsgOps";
wordRL: ROPE = "Word";
defRL: ROPE = "Def";
positionRL: ROPE = "Position";
normalizeRL: ROPE = "Normalize";
prevPlaceRL: ROPE = "PrevPlace";
moreLevelsRL: ROPE = "MoreLevels";
fewerLevelsRL: ROPE = "FewerLevels";
allLevelsRL: ROPE = "AllLevels";
defaultMovieMailFrameRate: INT¬1;
Procedures
GetMsgSetName: PUBLIC PROC[v: Viewer] RETURNS[msName: ROPE, rootFile: ROPE] = {
of v is a Viewer for a Walnut Entity, then return its name else NIL
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[v, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
rootFile ¬ WalnutWindow.GetRootFileForHandle[msI.button.wH];
RETURN[msI.button.msgSet.name, rootFile];
};
QDisplayMsgSet: PUBLIC PROC[wH: WalnutHandle, msb: MsgSetButton, oldV: Viewer, shift, repaint: BOOL]
RETURNS[v: Viewer] = {
IF oldV = NIL THEN {
IF msb = NIL THEN RETURN[NIL]; -- no such msgSet
oldV ¬ msb.msViewer
};
IF oldV # NIL THEN
IF oldV.destroyed THEN oldV ¬ msb.msViewer ¬ NIL;
v ¬ MSViewer[wH: wH, msb: msb, oldV: oldV, shift: shift];
msb.msViewer ¬ v;
};
AddNewMsgsToActive: PUBLIC PROC[active: MsgSetButton]
RETURNS[responses: LIST OF WalnutOps.ServerInfo, complete: BOOL, numNew: INT] = {
activeV: Viewer = active.msViewer;
wH: WalnutHandle = active.wH;
msI: MsgSetInfo;
sortTable: RedBlackTree.Table;
NewMsgProc: PROC[msg, tocEntry: ROPE, startOfSubject: INT] = {
msgInfo: MsgInfo;
mfh: MsgSetFieldHandle;
numNew ¬ numNew + 1;
msgInfo ¬ CreateMsgInfo[msg, FALSE, tocEntry, startOfSubject];
mfh ¬ NEW[MsgSetFieldHandleRec ¬ [msgInfo: msgInfo]];
RedBlackTree.Insert[sortTable, mfh, mfh!
RedBlackTree.DuplicateKey => WalnutWindow.Report[wH, "Duplicate Msg ID in message set!"]];
};
AppendMsg: RedBlackTree.EachNode ~ {
mfh: MsgSetFieldHandle ¬ NARROW[data];
BuildMsgLineViewer[msI, mfh.msgInfo];
};
numNew ¬ 0;
IF activeV = NIL OR activeV.destroyed THEN {
numNew ¬ -1;  -- don't know, won't count
[responses, complete] ¬ WalnutOps.GetNewMail[wH.opsH, WalnutDefs.dontCareMsgSetVersion, NIL];
IF ~complete THEN RETURN;
WalnutOps.AcceptNewMail[wH.opsH, WalnutDefs.dontCareMsgSetVersion];
RETURN
};
msI ¬ NARROW[ViewerOps.FetchProp[activeV, $MsgSetInfo]];
sortTable ¬ RedBlackTree.Create[getKey: GetKey, compare: WalnutFilter.SortCompareProc[msI.sortby]];
[responses, complete] ¬ WalnutOps.GetNewMail[wH.opsH, active.msgSet.version, NewMsgProc];
RedBlackTree.EnumerateIncreasing[sortTable, AppendMsg];
IF ~complete THEN RETURN;
WalnutOps.AcceptNewMail[wH.opsH, active.msgSet.version];
active.msgSet.version ¬ active.msgSet.version+1;
RedBlackTree.DestroyTable[sortTable];
};
RemoveMsgFromMsgSetDisplayer: PUBLIC PROC[wH: WalnutHandle, msg: ROPE, msgSet: MsgSet] = {
msb: MsgSetButton = GetButton[wH, msgSet.name];
mfH: MsgSetFieldHandle;
msI: MsgSetInfo;
IF msb.msViewer = NIL OR msb.msViewer.destroyed THEN RETURN;
msI ¬ NARROW[ViewerOps.FetchProp[msb.msViewer, $MsgSetInfo]];
mfH ¬ msI.lastMFH;
DO
IF mfH = NIL THEN RETURN;  -- not there
IF msg.Equal[mfH.msgInfo.msg, FALSE] THEN EXIT;
mfH ¬ mfH.prev;
ENDLOOP;
RemoveFromDisplayedMsgSet[msI, msb.msViewer, mfH, TRUE];
};
MsgSetNamePrefix: PUBLIC PROC[wH: WalnutHandle] RETURNS[ROPE] =
{ RETURN[wH.msgSetNamePrefix] };
TryToDisplayMsg: PROC[wH: WalnutHandle, mfH: MsgSetFieldHandle] = {
DisplayOneMsg[wH, mfH, FALSE ! WalnutDefs.Error => CONTINUE]
};
DispatchOpOnSelections: PROC[
opProc: PROC[ops: MsgSetOpHandle],
parent: Viewer,
mouseButton: Menus.MouseButton,
shift, control: BOOL] ~ {
ops: MsgSetOpHandle ¬ NEW[MsgSetOpHandleRec ¬ []];
mfHs: MsgSetFieldHandles;
ops.msViewer ¬ NARROW[parent];
IF ops.msViewer#NIL THEN
ops.msI ¬ NARROW[ViewerOps.FetchProp[ops.msViewer, $MsgSetInfo]];
IF ops.msI=NIL THEN RETURN;
ops.mouseButton ¬ mouseButton;
ops.shift ¬ shift;
ops.control ¬ control;
FOR mfHs: MsgSetFieldHandles ¬ ops.msI.selected, mfHs.rest WHILE mfHs#NIL DO
ops.mfH ¬ mfHs.first;
ops.lastSelected ¬ mfHs.rest=NIL;
opProc[ops];
ENDLOOP;
};
MoveToProc: PUBLIC Menus.MenuProc ~ {
DispatchOpOnSelections[MoveToOpProc, parent, mouseButton, shift, control];
};
MoveToOpProc: PROC[ops: MsgSetOpHandle] ~ {
wH: WalnutHandle = ops.msI.button.wH;
selected: MsgSetFieldHandle;
displayProcess: PROCESS;
selected ¬ ops.mfH;
IF selected.tocButton = NIL THEN
{ WalnutWindow.Report[wH, destroyedMsg]; RETURN};
BEGIN
thisMsgSetB: MsgSetButton = ops.msI.button;
thisName: ROPE = thisMsgSetB.msgSet.name;
msgSetList: LIST OF MsgSetButton = GetSelectedMsgSets[wH];
first: BOOL ¬ TRUE;
msg: ROPE = selected.msgInfo.msg;
moveToSelf: BOOL ¬ FALSE;
fromDeleted: BOOL ¬ (thisMsgSetB = wH.deletedMsgSetButton);
IF msgSetList = NIL THEN
{ WalnutWindow.Report[wH, noSelDest]; RETURN};
First display the next message to keep the user busy
IF ops.lastSelected AND ops.mouseButton#red THEN
displayProcess ¬ FORK TryToDisplayMsg[wH, selected.next];
FOR msL: LIST OF MsgSetButton ¬ msgSetList, msL.rest UNTIL msL = NIL DO
IF thisName.Equal[msL.first.msgSet.name, FALSE] THEN moveToSelf ¬ TRUE
ELSE IF ~WalnutOps.AddMsg[wH.opsH, msg, thisMsgSetB.msgSet, msL.first.msgSet] THEN {
IF msL.first.msViewer # NIL THEN
AddToDisplayedMsgSet[msL.first, selected.msgInfo, NIL];
};
ENDLOOP;
IF ~fromDeleted THEN
IF NOT moveToSelf THEN [] ¬ WalnutOps.RemoveMsg[
wH.opsH, msg, thisMsgSetB.msgSet, MsgSetVersion[wH.deletedMsgSetButton] ];
FOR msL: LIST OF MsgSetButton ¬ msgSetList, msL.rest UNTIL msL=NIL DO
IF first THEN
{ ReportDisposition[wH, selected.msgInfo, movedTo]; first ¬ FALSE}
ELSE WalnutWindow.ReportRope[wH, comma];
WalnutWindow.ReportRope[wH, space];
WalnutWindow.ReportRope[wH, msL.first.msgSet.name];
ENDLOOP;
WalnutWindow.ReportRope[wH, newLine];
IF ~moveToSelf OR fromDeleted THEN
RemoveFromDisplayedMsgSet[ops.msI, ops.msViewer, ops.mfH, ops.lastSelected]
ELSE IF ops.lastSelected THEN [] ¬ AdvanceSelection[wH, selected];
Finally, make sure the displaying process is done
IF displayProcess # NIL THEN TRUSTED { [] ¬ JOIN displayProcess }
END;
};
ComeFrom: PUBLIC PROC[msb: MsgSetButton] = {
IF UserProfile.Boolean[walnutDotFancyNewMessageSelect, FALSE] THEN
FancyNewComeFrom[msb]
ELSE BadOldComeFrom[msb]
};
November 11, 1991 8:58:32 am PST; user can select a message from at most one message set at a time (within the same database). ComeFrom operation does not require that a particular message set be selected; the single selection determines the message set. D. Swinehart
FancyNewComeFrom: PROC[msb: MsgSetButton] = {
toName: ROPE ¬ msb.msgSet.name;
moveToSelf: BOOL ¬ FALSE;
wH: WalnutHandle = msb.wH;
fromMsb: MsgSetButton;
fromName: ROPE;
mfHL: LIST OF MsgSetFieldHandle;
mfH: MsgSetFieldHandle;
msgInfo: MsgInfo;
fromDeleted: BOOL;
msgWasDeleted: BOOL¬FALSE;
msI: MsgSetInfo ¬ wH.selectedMsgSet;
IF msI=NIL OR msI.button=NIL OR msI.selected=NIL THEN
{ WalnutWindow.Report[wH, noSelSource]; RETURN};
fromMsb ¬ msI.button;
fromDeleted ¬ fromMsb = wH.deletedMsgSetButton;
fromName ¬ fromMsb.msgSet.name;
FOR mfHL: LIST OF MsgSetFieldHandle ¬ msI.selected, mfHL.rest WHILE mfHL#NIL DO
last: BOOL ¬ mfHL.rest=NIL;
mfH ¬ mfHL.first;
IF toName.Equal[fromName, FALSE] THEN {
IF last THEN []¬AdvanceSelection[wH, mfH];
LOOP;
};
msgInfo ¬ mfH.msgInfo;
IF msb # wH.deletedMsgSetButton THEN
IF ~WalnutOps.AddMsg[wH.opsH, msgInfo.msg, fromMsb.msgSet, msb.msgSet]
THEN AddToDisplayedMsgSet[msb, msgInfo, NIL];
IF ~fromDeleted THEN
msgWasDeleted ¬ WalnutOps.RemoveMsg[wH.opsH, msgInfo.msg, fromMsb.msgSet,
 MsgSetVersion[wH.deletedMsgSetButton] ];
IF msgWasDeleted THEN {
AddToDisplayedMsgSet[msb, msgInfo, NIL];
msI.lastDeleted ¬ msgInfo;
};
IF fromMsb.msViewer # NIL AND ~fromMsb.msViewer.destroyed THEN
RemoveFromDisplayedMsgSet[msI, fromMsb.msViewer, mfH, last];
ReportDisposition[wH, msgInfo, movedFrom];
WalnutWindow.ReportFormat[wH, formatGToGNewLine, [rope[fromMsb.msgSet.name]],
[rope[msb.msgSet.name]] ];
ENDLOOP;
};
Standard behavior; selected message from selected message set(s) is/are moved.
Works only when there is but one selected message.
BadOldComeFrom: PUBLIC PROC[msb: MsgSetButton] = {
toName: ROPE = msb.msgSet.name;
moveToSelf: BOOL ¬ FALSE;
wH: WalnutHandle = msb.wH;
fromMsgSetList: LIST OF MsgSetButton = GetSelectedMsgSets[wH];
IF fromMsgSetList = NIL THEN
{ WalnutWindow.Report[wH, noSelSource]; RETURN};
FOR msL: LIST OF MsgSetButton ¬ fromMsgSetList, msL.rest UNTIL msL = NIL DO
fromMsb: MsgSetButton = msL.first;
fromDeleted: BOOL = (fromMsb = wH.deletedMsgSetButton);
IF ~toName.Equal[fromMsb.msgSet.name, FALSE] THEN {
msI: MsgSetInfo = GetMSIFromMSB[fromMsb];
mfH: MsgSetFieldHandle;
msgInfo: MsgInfo;
IF msI # NIL THEN {
mfHL: LIST OF MsgSetFieldHandle ¬ msI.selected;
IF mfHL=NIL THEN {
WalnutWindow.Report[wH, noSelSource]; RETURN; };
mfH ¬ mfHL.first;
IF mfHL.rest#NIL THEN {
WalnutWindow.Report[wH, moreSelSource]; RETURN; };
};
msgInfo ¬ mfH.msgInfo;
IF ~WalnutOps.AddMsg[wH.opsH, msgInfo.msg, fromMsb.msgSet, msb.msgSet] THEN
IF msb.msViewer # NIL THEN AddToDisplayedMsgSet[msb, msgInfo, NIL];
IF ~fromDeleted THEN
[] ¬ WalnutOps.RemoveMsg[wH.opsH, msgInfo.msg, fromMsb.msgSet,
 MsgSetVersion[wH.deletedMsgSetButton] ];
IF fromMsb.msViewer # NIL AND ~fromMsb.msViewer.destroyed THEN
RemoveFromDisplayedMsgSet[msI, fromMsb.msViewer, mfH, TRUE];
ReportDisposition[wH, msgInfo, movedFrom];
WalnutWindow.ReportFormat[wH, formatGToGNewLine, [rope[fromMsb.msgSet.name]],
[rope[msb.msgSet.name]] ];
};
ENDLOOP;
};
GetMSIFromMSB: PROC[msb: MsgSetButton] RETURNS[msI: MsgSetInfo] = {
IF msb.msViewer = NIL OR msb.msViewer.destroyed THEN RETURN;
msI ¬ NARROW[ViewerOps.FetchProp[msb.msViewer, $MsgSetInfo]];
};
MsgSetVersion: PROC[msb: MsgSetButton] RETURNS[msgSetVersion: INT] = {
IF msb = NIL OR msb.msViewer = NIL OR msb.msViewer.destroyed THEN
RETURN[WalnutDefs.dontCareMsgSetVersion]
ELSE RETURN[msb.msgSet.version]
};
NewMail: Menus.MenuProc = {
viewer: Viewer = NARROW[parent];
oldM: Menus.Menu = viewer.menu;
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[viewer, $MsgSetInfo]];
ViewerOps.SetMenu[viewer, buildingMenu];
BEGIN ENABLE UNWIND => ViewerOps.SetMenu[viewer, oldM];
WalnutWindow.GetNewMail[msI.button.wH];
ViewerOps.SetMenu[viewer, oldM];
END;
};
DeleteProc: PUBLIC Menus.MenuProc ~ {
DispatchOpOnSelections[DeleteOpProc, parent, mouseButton, shift, control];
};
DeleteOpProc: PROC[ops: MsgSetOpHandle] ~ {
msI: MsgSetInfo ¬ ops.msI;
wH: WalnutHandle ¬ msI.button.wH;
displayProcess: PROCESS;
selected: MsgSetFieldHandle ¬ ops.mfH;
IF selected.tocButton = NIL THEN
{ WalnutWindow.Report[wH, destroyedMsg]; RETURN};
BEGIN
thisMsgSet: MsgSet ¬ msI.button.msgSet;
nowInDeleted: BOOL;
IF thisMsgSet.name.Equal[WalnutOps.DeletedName] THEN RETURN; -- already deleted
IF ops.lastSelected AND ops.mouseButton#red THEN
displayProcess ¬ FORK TryToDisplayMsg[wH, selected.next];
nowInDeleted ¬ WalnutOps.RemoveMsg[
wH.opsH, selected.msgInfo.msg, thisMsgSet,
MsgSetVersion[wH.deletedMsgSetButton] ];
RemoveFromDisplayedMsgSet[msI, ops.msViewer, selected, ops.lastSelected];
ReportDisposition[wH, selected.msgInfo, deletedFrom];
WalnutWindow.Report[wH, thisMsgSet.name];
IF nowInDeleted THEN
AddToDisplayedMsgSet[wH.deletedMsgSetButton, selected.msgInfo, NIL];
IF displayProcess # NIL THEN TRUSTED { [] ¬ JOIN displayProcess }
END;
msI.lastDeleted ¬ selected.msgInfo;
};
AddProc: Menus.MenuProc ~ {
DispatchOpOnSelections[AddOpProc, parent, mouseButton, shift, control];
};
AddOpProc: PROC[ops: MsgSetOpHandle] ~ {
msI: MsgSetInfo ¬ ops.msI;
wH: WalnutHandle ¬ msI.button.wH;
selected: MsgSetFieldHandle ¬ ops.mfH;
displayProcess: PROCESS;
IF selected.tocButton = NIL THEN
{ WalnutWindow.Report[wH, destroyedMsg]; RETURN};
BEGIN
thisMsgSet: MsgSet ¬ msI.button.msgSet;
msgSetList: LIST OF MsgSetButton ¬ GetSelectedMsgSets[wH];
msg: ROPE ¬ selected.msgInfo.msg;
isDeleted: BOOL ¬ thisMsgSet.name.Equal[WalnutOps.DeletedName, FALSE];
removedFromDeleted: BOOL ¬ ~isDeleted;
first: BOOL ¬ TRUE;
addedTo: LIST OF MsgSetButton;
IF msgSetList = NIL THEN
{ WalnutWindow.Report[wH, noSelDest]; RETURN};
IF ops.lastSelected AND ops.mouseButton#red THEN
displayProcess ¬ FORK TryToDisplayMsg[wH, selected.next];
FOR msL: LIST OF MsgSetButton ¬ msgSetList, msL.rest UNTIL msL=NIL DO
exists: BOOL;
msgSet: MsgSet;
exists ¬ WalnutOps.AddMsg[wH.opsH, msg, thisMsgSet, msgSet ¬ msL.first.msgSet];
removedFromDeleted ¬ removedFromDeleted OR ~exists;
IF ~exists THEN
BEGIN
IF first THEN
{ ReportDisposition[wH, selected.msgInfo, addedToRL]; first ¬ FALSE}
ELSE WalnutWindow.ReportRope[wH, comma];
WalnutWindow.ReportRope[wH, msgSet.name];
addedTo ¬ CONS[msL.first, addedTo];
END;
ENDLOOP;
WalnutWindow.ReportRope[wH, newLine];
now update display
FOR msL: LIST OF MsgSetButton ¬ addedTo, msL.rest UNTIL msL=NIL DO
AddToDisplayedMsgSet[msL.first, selected.msgInfo, NIL];
ENDLOOP;
IF ops.lastSelected AND ops.mouseButton # red THEN [] ¬ AdvanceSelection[wH, selected];
IF removedFromDeleted THEN
RemoveMsgFromMsgSetDisplayer[wH, msg, wH.deletedMsgSetButton.msgSet];
IF displayProcess # NIL THEN TRUSTED { [] ¬ JOIN displayProcess }
END;
};
AddorAppendTo: PUBLIC PROC[wH: WalnutHandle, msg: ROPE, isAdd: BOOL,
fromButton, toButton: MsgSetButton] = {
msgInfo: MsgInfo;
adding to deleted doesn't make sense
IF Rope.Equal[toButton.msgSet.name, WalnutOps.DeletedName, FALSE] THEN RETURN;
msgInfo ¬ DoMsgInfo[wH: wH, msg: msg];
IF ~WalnutOps.AddMsg[wH.opsH, msg, fromButton.msgSet, toButton.msgSet].exists THEN {
AddToDisplayedMsgSet[toButton, msgInfo, NIL];
ReportDisposition[wH, msgInfo, IF isAdd THEN addedToRL ELSE appendedTo];
WalnutWindow.Report[wH, toButton.msgSet.name];
};
if this was an addTo from deleted and then remove message from deleted display viewer
IF Rope.Equal[fromButton.msgSet.name, WalnutOps.DeletedName, FALSE] THEN RemoveMsgFromMsgSetDisplayer[wH, msg, fromButton.msgSet];
};
MoveTo: PUBLIC PROC[msg: ROPE, fromButton, toButton: MsgSetButton] = {
msgInfo: MsgInfo;
wH: WalnutHandle = fromButton.wH;
msgInfo ¬ DoMsgInfo[wH, msg];
IF ~WalnutOps.MoveMsg[wH.opsH, msg, fromButton.msgSet, toButton.msgSet].exists THEN {
AddToDisplayedMsgSet[toButton, msgInfo, NIL];
RemoveMsgFromMsgSetDisplayer[wH, msg, fromButton.msgSet];
ReportDisposition[wH, msgInfo, movedTo];
WalnutWindow.Report[wH, toButton.msgSet.name];
};
};
RemoveFrom: PUBLIC PROC[msg: ROPE, fromButton: MsgSetButton] = {
msgInfo: MsgInfo;
nowInDeleted: BOOL ¬ FALSE;
wH: WalnutHandle = fromButton.wH;
removing from deleted doesn't make sense
IF Rope.Equal[fromButton.msgSet.name, WalnutOps.DeletedName, FALSE] THEN RETURN;
msgInfo ¬ DoMsgInfo[wH, msg];
nowInDeleted ¬
WalnutOps.RemoveMsg[wH.opsH, msg, fromButton.msgSet, WalnutOps.dontCareMsgSetVersion];
RemoveMsgFromMsgSetDisplayer[wH, msg, fromButton.msgSet];
ReportDisposition[wH, msgInfo, removedFrom];
WalnutWindow.Report[wH, fromButton.msgSet.name];
IF nowInDeleted THEN
AddToDisplayedMsgSet[wH.deletedMsgSetButton, msgInfo, NIL];
};
AdvanceSelection: PROC[wH: WalnutHandle, mfH: MsgSetFieldHandle]
RETURNS[next: MsgSetFieldHandle] = {
IF mfH = NIL THEN RETURN[NIL];
next ¬ mfH.next;
IF next = NIL THEN { WalnutWindow.Report[wH, noNext]; RETURN };
SelectMsgInMsgSet[wH, next]
};
DisplayProc: PUBLIC Menus.MenuProc ~ {
DispatchOpOnSelections[DisplayOpProc, parent, mouseButton, shift, control];
};
DisplayOpProc: PROC[ops: MsgSetOpHandle] ~ {
msI: MsgSetInfo ~ ops.msI;
selected: MsgSetFieldHandle ¬ ops.mfH;
wH: WalnutHandle = msI.button.wH;
IF ops.lastSelected AND ops.mouseButton # red THEN
selected ¬ AdvanceSelection[wH, selected];
IF selected = NIL THEN RETURN;
IF selected.tocButton = NIL THEN
{ WalnutWindow.Report[wH, destroyedMsg]; RETURN};
DisplayOneMsg[wH, selected, ops.shift];
IF NOT ops.lastSelected THEN
Process.PauseMsec[1000*UserProfile.Number[walnutDotMovieMailFrameRate, defaultMovieMailFrameRate]];
};
PrintSel: PROC[wH: WalnutHandle,msViewer: Viewer, server: ROPE ¬ NIL,
howToPrint: HowToPrint ¬ press, copies: INT ¬ 1] = {
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
selected: MsgSetFieldHandle;
Ps: PROC = {
[] ¬ PrintMsgList[wH, LIST[selected.msgInfo.msg], msViewer, server, howToPrint, copies];
};
IF msI = NIL THEN RETURN;
FOR mfHL: LIST OF MsgSetFieldHandle ¬ msI.selected, mfHL.rest WHILE mfHL#NIL DO
selected ¬ mfHL.first;
IF selected.tocButton = NIL THEN
{ WalnutWindow.Report[wH, destroyedMsg]; RETURN};
[] ¬ DoWaitCall[wH, Ps];
ENDLOOP;
};
PrintSelCmd: PUBLIC PROC[wH: WalnutHandle,
 msgSet: ROPE, server: ROPE, howToPrint: HowToPrint, copies: INT] = {
msb: MsgSetButton = GetButton[wH, msgSet];
IF msb = NIL THEN RETURN;
IF msb.msViewer = NIL THEN RETURN;
PrintSel[wH, msb.msViewer, server, howToPrint, copies];
};
MSPProc: PROC[wH: WalnutHandle, viewer: Viewer, howToPrint: HowToPrint] = {
Mspp: PROC = { MsgSetPrintProc[wH, viewer, howToPrint] };
[] ¬ DoWaitCall[wH, Mspp];
};
MSTPProc: PROC[wH: WalnutHandle, viewer: Viewer, howToPrint: HowToPrint] = {
Mstp: PROC = { MsgSetTOCPrintProc[wH, viewer, howToPrint] };
[] ¬ DoWaitCall[wH, Mstp];
};
DoAppendMsg: PUBLIC PROC[wH: WalnutHandle, curSel: ROPE, curV: Viewer,
msgSetList: LIST OF MsgSetButton] = {
tc: ViewerTools.TiogaContents;
Amp: PROC = {
active: MsgSetButton = wH.activeMsgSetButton;
msgID, msgName: ROPE;
includesActive: BOOL ¬ FALSE;
nowInDeleted: BOOL ¬ FALSE;
IF curSel.Length[] < 2 THEN {
msgAndWH: WalnutWindowPrivate.MsgAndHandle;
IF curV = NIL THEN {
WalnutWindow.Report[wH, noSelSource];
RETURN
};
msgAndWH ¬ NARROW[ViewerOps.FetchProp[curV, $WalnutMsgName]];
IF msgAndWH # NIL THEN {
pos: INT = Rope.Find[msgID ¬ msgAndWH.msg, dollarSign];
IF pos = 0 THEN msgName ¬ msgID
ELSE msgName ¬ msgID.Substr[pos];
}
ELSE {
another: ROPE ¬ NARROW[ViewerOps.FetchProp[curV, $CherryMsgName]];
IF another # NIL THEN {
pos: INT = Rope.Find[msgID ¬ another, dollarSign];
IF pos = 0 THEN msgName ¬ msgID
ELSE msgName ¬ msgID.Substr[pos];
};
};
tc ¬ SendMailOps.GetCRTiogaContents[curV];
}
ELSE {
tc ¬ NEW[ViewerTools.TiogaContentsRec];
tc.contents ¬ curSel;
};
IF msgID = NIL THEN
msgID ¬ msgName ¬ WalnutOps.GenerateUniqueMsgName[wH.opsH];
IF NOT WalnutOps.MsgExists[wH.opsH, msgName] THEN {
WalnutOps.CreateMsg[wH.opsH, msgID, tc];
WalnutOps.SetHasBeenRead[wH.opsH, msgName];
IF active.msViewer # NIL THEN active.msgSet.version ¬ active.msgSet.version + 1;
};
FOR msL: LIST OF MsgSetButton ¬ msgSetList, msL.rest UNTIL msL = NIL DO
IF msL.first = active THEN {
includesActive ¬ TRUE;
AddToDisplayedMsgSet[active, DoMsgInfo[wH, msgName], NIL];
if active is displayed, then its version number got incremented twice
IF active.msViewer # NIL THEN active.msgSet.version ¬ active.msgSet.version - 1;
}
ELSE AddorAppendTo[wH: wH, msg: msgName, isAdd: FALSE, fromButton: active, toButton: msL.first];
ENDLOOP;
IF NOT includesActive THEN {
nowInDeleted ¬ WalnutOps.RemoveMsg[wH.opsH, msgName, active.msgSet, WalnutOps.dontCareMsgSetVersion];
IF active.msViewer # NIL THEN active.msgSet.version ¬ active.msgSet.version + 1;
};
IF nowInDeleted THEN
AddToDisplayedMsgSet[wH.deletedMsgSetButton, DoMsgInfo[wH, msgName], NIL];
};
[] ¬ DoWaitCall[wH, Amp];
};
FindProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
IF ~TiogaOps.FindText[
viewer: msI.tiogaViewer,
whichDir: GetDirection[mouseButton],
which: feedback,
case: ~shift]
THEN ViewerOps.BlinkIcon[msViewer];
};
WordProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
IF ~TiogaOps.FindWord[
viewer: msI.tiogaViewer,
whichDir: GetDirection[mouseButton],
which: feedback,
case: ~shift]
THEN ViewerOps.BlinkIcon[msViewer];
};
DefProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
IF ~TiogaOps.FindDef[
viewer: msI.tiogaViewer,
whichDir: GetDirection[mouseButton],
which: feedback,
case: ~shift]
THEN ViewerOps.BlinkIcon[msViewer];
};
PositionProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
TiogaMenuOps.Position[msI.tiogaViewer];
};
NormalizeProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
TiogaMenuOps.Normalize[msI.tiogaViewer];
};
PrevPlaceProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
TiogaMenuOps.PrevPlace[msI.tiogaViewer];
};
FirstOnlyProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
TiogaMenuOps.FirstLevelOnly[msI.tiogaViewer];
};
MoreLevelsProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
TiogaMenuOps.MoreLevels[msI.tiogaViewer];
};
FewerLevelsProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
TiogaMenuOps.FewerLevels[msI.tiogaViewer];
};
AllLevelsProc: Menus.MenuProc = {
msViewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]];
IF msI = NIL THEN RETURN;
TiogaMenuOps.AllLevels[msI.tiogaViewer];
};
LevelsProc: Menus.MenuProc = { ChangeTheMenu[v: NARROW[parent], isLevels: TRUE] };
PlacesProc: Menus.MenuProc = { ChangeTheMenu[v: NARROW[parent], isLevels: FALSE] };
GetDirection: PROC[mb: Menus.MouseButton] RETURNS[sd: TiogaOps.SearchDir¬forwards] = {
SELECT mb FROM
red => RETURN[forwards];
yellow => RETURN[anywhere];
blue => RETURN[backwards];
ENDCASE;
};
OpsProc: Menus.MenuProc = {
ChoicesFromMenuEntry: PROC [me: Menus.MenuEntry] RETURNS [choices: LIST OF ROPE] ~ {
l: LIST OF ROPE ¬ NIL;
IF me = NIL THEN RETURN[NIL];
choices ¬ LIST[me.name];
me ¬ me.link;
l ¬ choices;
WHILE me # NIL DO
l.rest ¬ LIST[me.name];
me ¬ me.link;
l ¬ l.rest;
ENDLOOP;
};
ConcatLists: PROC [l1, l2: LIST OF ROPE] RETURNS [LIST OF ROPE] ~ {
l: LIST OF ROPE ¬ l1;
WHILE l.rest # NIL DO l ¬ l.rest; ENDLOOP;
l.rest ¬ l2;
RETURN[l1];
};
viewer: Viewer = NARROW[parent];
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[viewer, $MsgSetInfo]];
wH: WalnutHandle = msI.button.wH;
mouseNotRed: BOOL = (mouseButton # red);
choices: LIST OF ROPE ¬ LIST[
-- 1-- "Sender",
-- 2-- "UndoLastDelete",
-- 3-- "Size Of MsgSet",
-- 4-- "Msg Info",
-- 5-- "Append Msg",
-- 6-- "Interpress3.0PrintMsgSet",
-- 7-- "Interpress3.0PrintSelectedMsg",
-- 8-- "Interpress3.0PrintTOC",
-- 9-- "R.O.T. MsgSet"];
moreChoices: LIST OF ROPE;
moreProcs: Menus.MenuEntry;
which: INT;
moreProcs ¬ WalnutFilter.MenuExtras[$msgsetops];
moreChoices ¬ ChoicesFromMenuEntry[moreProcs];
choices ¬ ConcatLists[choices, moreChoices];
which ¬ PopUpSelection.Request[header: "MessageOps", choice: choices];
SELECT which FROM
1 => [] ¬ SendMailOps.CreateSendViewer[fromExec: FALSE];
2 => {
current: MsgSet;
msg: ROPE;
IF msI = NIL THEN RETURN;
IF msI.lastDeleted = NIL THEN
{ WalnutWindow.Report[wH, noUndo]; RETURN };
msg ¬ msI.lastDeleted.msg;
current ¬ [WalnutOps.GetCategories[wH.opsH, msg].first, WalnutOps.dontCareMsgSetVersion];
[] ¬ WalnutOps.AddMsg[opsHandle: wH.opsH, msg: msg, from: current, to: msI.button.msgSet];
IF current.name.Equal[WalnutOps.DeletedName, FALSE] THEN {
dViewer: Viewer = wH.deletedMsgSetButton.msViewer;
dMsi: MsgSetInfo;
mfH: MsgSetFieldHandle;
IF dViewer # NIL AND ~dViewer.destroyed THEN {
dMsi ¬ NARROW[ViewerOps.FetchProp[dViewer, $MsgSetInfo]];
mfH ¬ dMsi.lastMFH;
DO
IF mfH = NIL THEN EXIT;
IF mfH.msgInfo = msI.lastDeleted THEN EXIT;
mfH ¬ mfH.prev;
ENDLOOP;
IF mfH # NIL THEN RemoveFromDisplayedMsgSet[dMsi, dViewer, mfH, FALSE];
};
};
AddToDisplayedMsgSet[msI.button, msI.lastDeleted, msI];
ReportDisposition[wH, msI.lastDeleted, addedToRL];
WalnutWindow.Report[wH, msI.button.msgSet.name];
msI.lastDeleted ¬ NIL;
};
3 => {
msgSet: ROPE = msI.button.msgSet.name;
num: INT = WalnutOps.SizeOfMsgSet[wH.opsH, msgSet].messages;
WalnutWindow.ReportFormat[wH,
" %g msgs in MsgSet %g", [integer[num]], [rope[msgSet]] ];
};
4 => {
IF msI = NIL OR msI.selected = NIL THEN { WalnutWindow.Report[wH, noSelSource]; RETURN };
FOR selList: LIST OF MsgSetFieldHandle ¬ msI.selected, selList.rest
WHILE selList#NIL DO
textLen, formatLen: INT;
msg: ROPE;
selected: MsgSetFieldHandle ¬ selList.first;
IF selected.tocButton = NIL THEN
{ WalnutWindow.Report[wH, destroyedMsg]; RETURN };
[textLen, formatLen] ¬ WalnutOps.GetMsgSize[wH.opsH, msg ¬ selected.msgInfo.msg];
WalnutWindow.ReportFormat[wH, "\nPostmark: %g\n", [rope[msg]]];
WalnutWindow.ReportFormat[wH, " TOC: %g\n", [rope[selected.msgInfo.tocName]]];
WalnutWindow.ReportFormat[wH, " Length: %g bytes\n", [integer[textLen]] ];
MsgCategories[wH, msg];
ENDLOOP;
};
5 => DoAppendMsg[wH, ViewerTools.GetSelectionContents[], ViewerTools.GetSelectedViewer[], LIST[msI.button]];
6 => MSPProc[wH: wH, viewer: viewer, howToPrint: ip3];
7 => PrintSel[wH: wH, msViewer: viewer, howToPrint: ip3];
8 => MSTPProc[wH: wH, viewer: viewer, howToPrint: ip3];
9 => DoRemoveOverThere[wH, msI.button.msgSet.name, GetSelectedMsgSets[wH] ];
ENDCASE => IF moreChoices # NIL THEN {
i: INT ¬ 10;
plist: Menus.MenuEntry ¬ moreProcs;
WHILE i < which AND plist # NIL DO
i ¬ i + 1;
plist ¬ plist.link;
ENDLOOP;
IF i = which AND plist # NIL THEN
plist.proc[parent, clientData, mouseButton, shift, control];
};
};
ReportDisposition: PROC[wH: WalnutHandle, msgInfo: MsgInfo, r: ROPE] = {
msgSubject: ROPE ¬ Rope.Substr[msgInfo.tocName, msgInfo.startOfSubject];
SELECT msgSubject.Length[] FROM
< 2 => msgSubject ¬ "No Subject field";
> 24 => msgSubject ¬ Rope.Concat[msgSubject.Substr[0, 21], " ..."];
ENDCASE => NULL;
WalnutWindow.ReportFormat[wH, "Msg: %g: has been %g", [rope[msgSubject]], [rope[r]] ];
};
* * * * * * * * * * * * * * * * * * * * * *
MSViewer: PROC[wH: WalnutHandle, msb: MsgSetButton, oldV: Viewer, shift: BOOL]
RETURNS[msV: Viewer] = {
col: ROPE = UserProfile.Token[walnutDotMsgSetColumn, leftRL];
aCol: ROPE = UserProfile.Token[walnutDotActiveMsgSetColumn, rightRL];
iconic: BOOL ¬ FALSE;
whichSide: ViewerClasses.Column ¬ left;
msgSet: MsgSet = msb.msgSet;
caption: ROPE = wH.identifierPrefix.Cat[msgSet.name, " Messages"];
msI: MsgSetInfo;
openHeight: INTEGER ¬ 0;
isActive: BOOL = msgSet.name.Equal[WalnutOps.ActiveName, FALSE];
WhichCol: PROC[rp: ROPE, df: ViewerClasses.Column] RETURNS[ViewerClasses.Column] = {
SELECT TRUE FROM
rp.Equal[leftRL, FALSE] => RETURN[left];
rp.Equal["color", FALSE] => RETURN[df]; -- ignores color request
rp.Equal[rightRL, FALSE] => RETURN[right];
ENDCASE => RETURN[df];
};
IF isActive THEN {
iconic ¬ initialActiveIconic AND initialActiveOpen;
whichSide ¬ WhichCol[UserProfile.Token[walnutDotActiveMsgSetColumn, rightRL], right ];
IF openHeightForActive > 0 THEN openHeight ¬ openHeightForActive;
}
ELSE whichSide ¬ WhichCol[UserProfile.Token[walnutDotMsgSetColumn, leftRL], left ];
IF oldV # NIL AND oldV.destroyed THEN oldV ¬ NIL;
IF oldV # NIL THEN {
msI ¬ NARROW[ViewerOps.FetchProp[oldV, $MsgSetInfo]];
IF msI.button.wH # wH THEN {
WalnutWindow.ReportFormat[wH, "MsgSet displayer for %g belongs to a different Walnut Control Window", [rope[msI.button.msgSet.name]] ];
oldV ¬ NIL;
};
};
IF oldV # NIL THEN {  -- must check once more
oldMsgSetName: ROPE;
msI ¬ NARROW[ViewerOps.FetchProp[oldV, $MsgSetInfo]];
oldMsgSetName ¬ msI.button.msgSet.name;
oldV.inhibitDestroy ¬ TRUE;
IF ~Rope.Equal[msgSet.name, oldMsgSetName, FALSE] THEN {
oldV.name ¬ caption;
GetButton[wH, oldMsgSetName].msViewer ¬ NIL;
GetButton[wH, msgSet.name].msViewer ¬ oldV
}
ELSE { -- check the version to see if all's well
IF msI.button.msgSet.version = WalnutOps.MsgSetExists[wH.opsH,
msgSet.name, wH.msgSetsVersion].version THEN {
oldV.inhibitDestroy ¬ FALSE;
RETURN[oldV]
}
};
ViewerOps.DestroyViewer[msI.tiogaViewer];
msI.selected ¬ NIL;
msI.lastMFH ¬ NIL;
msI.tiogaViewer ¬ NIL;
msV ¬ oldV;
}
ELSE {
msV ¬ ViewerOps.CreateViewer[
flavor: $Container,
info: [name: caption, column: whichSide, menu: NIL,
iconic: iconic, icon: msgSetIcon, scrollable: FALSE, inhibitDestroy: TRUE,
openHeight: openHeight
]];
wH.msgSetViewerList ¬ CONS[msV, wH.msgSetViewerList];
msI ¬ NEW[MsgSetInfoRec];
msI.button ¬ msb;
msI.container ¬ msV;
msI.sortby ¬ WalnutFilter.InitialSortOrder[msgSet.name];
msI.destroyER ¬
ViewerEvents.RegisterEventProc[DestroyMSViewer, destroy, msV, TRUE];
IF isActive THEN
[] ¬ ViewerEvents.RegisterEventProc[proc: ResetMailState, event: open, filter: msV];
};
IF msI.tiogaViewer = NIL THEN {
width: INT ¬ IF msV.column = right THEN ViewerSpecs.openRightWidth ELSE
ViewerSpecs.openLeftWidth;
msI.tiogaViewer ¬ TiogaButtons.CreateViewer[
info: [parent: msV, border: FALSE, ww: width, wh: ViewerSpecs.bwScreenHeight]];
TiogaButtons.SetStyleFromRope[v: msI.tiogaViewer, styleRope: msgSetStyle];
Containers.ChildXBound[msV, msI.tiogaViewer];
Containers.ChildYBound[msV, msI.tiogaViewer];
};
ViewerOps.AddProp[msV, $MsgSetInfo, msI];
msV.label ¬ wH.identifierPrefix.Concat[msgSet.name];
MsgSetInViewer[wH, msgSet, msI, shift, isActive];
IF isActive AND NOT iconic THEN [] ¬ ResetMailState[msV, open, TRUE];
};
msgSetStyle: ROPE ¬ "BeginStyle
(Cedar) AttachStyle
(header) \"a message header\" {
default
200 pt restIndent
} StyleRule
EndStyle";
DestroyMSViewer: ViewerEvents.EventProc = {
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[viewer, $MsgSetInfo]];
msI.button.msgSet.version ¬ WalnutDefs.dontCareMsgSetVersion;
msI.button.msViewer ¬ NIL;
ViewerEvents.UnRegisterEventProc[msI.destroyER, destroy];
};
AutoScrollRef: TYPE = REF AutoScrollObject;
AutoScrollObject: TYPE = RECORD
[ eventReg: ViewerEvents.EventRegistration, v: Viewer, firstUnread, last: INT ];
MsgSetInViewer: PROC[wH: WalnutHandle, msgSet: MsgSet, msI: MsgSetInfo, shift, isActive: BOOL] = {
OPEN ViewerOps;
firstUnread, last: INT;
userSaidStop: BOOL;
autoScroll: BOOL = UserProfile.Boolean[key: "Walnut.AutoScrollMsgSets", default: TRUE];
v: Viewer = msI.tiogaViewer;
parent: Viewer = v.parent;
menu: Menus.Menu ¬
IF wH.readOnlyAccess THEN wH.readOnlyMenu ELSE
IF isActive THEN
IF wH.personalMailDB THEN wH.activeMenu ELSE wH.displayerMenu ELSE
IF msgSet.name.Equal[WalnutOps.DeletedName, FALSE] THEN wH.deletedMenu ELSE wH.displayerMenu;
ScrollMsgSet: PROC = {
IF autoScroll AND last#-1 THEN
{ IF v.parent.iconic THEN
{ scroll: ViewerEvents.EventRegistration ¬
ViewerEvents.RegisterEventProc[AutoScroll, open, v.parent, FALSE];
after
ViewerOps.AddProp[v.parent, $autoScroll,
NEW[AutoScrollObject ¬ [scroll, v, firstUnread, last]]];
}
ELSE DoScroll[v, firstUnread, last];
};
SetMenu[parent, menu];
};
[] ¬ v.class.scroll[v, thumb, 0];  -- position at beginning
IF ~parent.iconic THEN ComputeColumn[parent.column, TRUE];
parent.newVersion ¬ TRUE; PaintViewer[parent, caption];
SetMenu[parent, buildingMenu];
[firstUnread, last, userSaidStop] ¬ FillInMsgSetWindow[wH, parent, msI, msgSet.name];
IF userSaidStop THEN {
parent.inhibitDestroy ¬ FALSE;
msI.button.msViewer ¬ NIL;
ViewerEvents.UnRegisterEventProc[msI.destroyER, destroy];
ViewerOps.DestroyViewer[parent];
RETURN;
};
ScrollMsgSet[];
ViewerLocks.CallUnderWriteLock[ScrollMsgSet, v];
parent.newVersion ¬ FALSE; PaintViewer[parent, caption];
IF shift THEN
{ IF parent.iconic THEN ViewerOps.OpenIcon[parent, shift] ELSE ViewerOps.GrowViewer[parent]};
parent.inhibitDestroy ¬ FALSE;
};
DoScroll: PROC[viewer: Viewer, firstUnread, last: INT] = {
top, bottom, howMuch: INTEGER;
count: INTEGER ¬ 0;
DO
[top, bottom] ¬ viewer.class.scroll[viewer, query, 0];
IF (bottom = LAST[INTEGER]) AND ((count ¬ count + 1) < 5) THEN {
Process.Pause[Process.MsecToTicks[scrollWaitTime]];
LOOP
};
EXIT;
ENDLOOP;
IF bottom >= 95 OR last <=0 THEN RETURN;
howMuch ¬ IF firstUnread <= 0 THEN (last - 3) ELSE
IF firstUnread > (last - 3) THEN (last - 3) ELSE firstUnread;
[] ¬ viewer.class.scroll[viewer, thumb, (howMuch*100)/last];
};
ResetMailState: ViewerEvents.EventProc = {
IF ( UserProfile.Boolean[key: "Walnut.AutoNewMail", default: FALSE] ) AND
( NOT viewer.destroyed ) THEN {
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[viewer, $MsgSetInfo]];
IF msI.button.wH.personalMailDB THEN
WalnutInternal.SetMailState[msI.button.wH, noMail];
};
};
AutoScroll: ViewerEvents.EventProc = {
autoScroll: AutoScrollRef = NARROW[ViewerOps.FetchProp[viewer, $autoScroll]];
ViewerEvents.UnRegisterEventProc[autoScroll.eventReg, open];  -- once only
DoScroll[autoScroll.v, autoScroll.firstUnread, autoScroll.last];
};
* * * * * * * * * * * * * * * * * * * * * *
for adding new messages to displayed MsgSet
AddToDisplayedMsgSet: PROC[msb: MsgSetButton, msgInfo: MsgInfo, msI: MsgSetInfo] = {
IF msb.msViewer # NIL THEN {
IF msb.msViewer.destroyed THEN { msb.msViewer ¬ NIL; RETURN };
IF msI = NIL THEN msI ¬ NARROW[ViewerOps.FetchProp[msb.msViewer, $MsgSetInfo]];
BuildMsgLineViewer[msI, msgInfo];
msI.button.msgSet.version ¬ msb.msgSet.version ¬ msI.button.msgSet.version+1;
};
};
called with parent & MsgSetFieldHandle 'displaying' msg to be deleted
RemoveFromDisplayedMsgSet: PROC[
msI: MsgSetInfo, msViewer: Viewer, mfH: MsgSetFieldHandle, selectNext: BOOL] = {
prev, next: MsgSetFieldHandle;
bt: TiogaButton;
bt ¬ mfH.tocButton;
prev ¬ mfH.prev;
next ¬ mfH.next;
take which out of chain of mfh's
IF prev # NIL THEN prev.next ¬ next;
IF next # NIL THEN next.prev ¬ prev;
TiogaButtons.DeleteButton[bt];
IF selectNext THEN {
FOR msS: LIST OF MsgSetFieldHandle ¬ msI.selected, msS.rest WHILE msS#NIL DO
IF msS.first=mfH THEN GOTO SelectIt;
REPEAT
SelectIt =>
IF next#NIL THEN {
msI.selected ¬ LIST[next];
TiogaButtons.ChangeButtonLooks[button: next.tocButton,
addLooks: tocSelectedLooks, removeLooks: tocDefaultLooks];
}
ELSE msI.selected ¬ NIL;
ENDLOOP;
};
IF msI.lastMFH = mfH THEN msI.lastMFH ¬ prev;
msI.button.msgSet.version ¬ msI.button.msgSet.version+1;
};
GetKey: RedBlackTree.GetKey ~ { -- can't be an internal procedure in Cedar10.1
mfh: MsgSetFieldHandle ¬ NARROW[data];
RETURN[mfh];
};
FillInMsgSetWindow: PROC[wH: WalnutHandle, parent: Viewer, msI: MsgSetInfo, msgSet: ROPE]
RETURNS[firstUnread, last: INT, userSaidStop: BOOL] = {
version: INT;
displayingRef: DisplayingRef ¬ NEW[DisplayingRefRec ¬ [FALSE]];
sortTable: RedBlackTree.Table;
WantsToStop: ENTRY PROC RETURNS[BOOL] = {
ENABLE UNWIND => NULL;
RETURN[displayingRef.userSaidStop]
};
MsgInfoProc: PROC[msg, tocEntry: ROPE, hasBeenRead: BOOL, startOfSubject: INT] RETURNS[continue: BOOL] = {
oldTB: TiogaButton¬NIL;
where: EditSpan.Place ¬ before;
mfh: MsgSetFieldHandle;
msgInfo: MsgInfo;
IF ( userSaidStop ¬ WantsToStop[] ) THEN RETURN[FALSE];
msgInfo ¬ CreateMsgInfo[msg, hasBeenRead, tocEntry, startOfSubject];
mfh ¬ NARROW[RedBlackTree.LookupNextLarger[sortTable, NEW[MsgSetFieldHandleRec ¬ [msgInfo: msgInfo]]]];
IF mfh#NIL THEN oldTB ¬ mfh.tocButton;
BuildMsgLineViewer[
msI, msgInfo, oldTB, where];
last ¬ last + 1;
IF firstUnread = -1 AND ~msI.lastMFH.msgInfo.hasBeenRead THEN
firstUnread ¬ last;
RedBlackTree.Insert[sortTable, msI.lastMFH, msI.lastMFH!
RedBlackTree.DuplicateKey => WalnutWindow.Report[wH, "Duplicate Msg ID in message set!"]];
RETURN[TRUE];
};
ReorderMsgs: RedBlackTree.EachNode ~ {
mfh: MsgSetFieldHandle ¬ NARROW[data];
mfh.prev ¬ msI.lastMFH;
IF msI.lastMFH#NIL THEN msI.lastMFH.next ¬ mfh;
msI.lastMFH ¬ mfh;
};
sortTable ¬ RedBlackTree.Create[getKey: GetKey, compare: WalnutFilter.SortCompareProc[msI.sortby]];
ViewerOps.AddProp[parent, $DisplayingRef, displayingRef];
ViewerOps.SetMenu[parent, fillingInMenu];
userSaidStop ¬ FALSE;
firstUnread ¬ -1;
last ¬ 0;
version ¬ WalnutOps.EnumerateMsgsInSet[wH.opsH, msgSet, TRUE, MsgInfoProc];
IF NOT userSaidStop THEN msI.button.msgSet.version ¬ version;
ViewerOps.AddProp[parent, $DisplayingRef, NIL];
msI.lastMFH ¬ NIL;
RedBlackTree.EnumerateIncreasing[sortTable, ReorderMsgs];
IF msI.lastMFH#NIL THEN msI.lastMFH.next ¬ NIL;
RedBlackTree.DestroyTable[sortTable];
};
AbortEnumProc: ENTRY Menus.MenuProc = {
ENABLE UNWIND => NULL;
self: Viewer ¬ NARROW[parent];
displayingRef: DisplayingRef ¬ NARROW[ViewerOps.FetchProp[self, $DisplayingRef]];
IF displayingRef = NIL THEN RETURN;
displayingRef.userSaidStop ¬ TRUE;
ViewerOps.SetMenu[self, buildingMenu];
};
DoMsgInfo: PROC[wH: WalnutHandle, msg: ROPE] RETURNS[msgInfo: MsgInfo] = {
tocEntry: ROPE;
hasBeenRead: BOOL;
startOfSubject: INT;
[hasBeenRead, tocEntry, startOfSubject] ¬ WalnutOps.GetDisplayProps[wH.opsH, msg];
RETURN[CreateMsgInfo[msg, hasBeenRead, tocEntry, startOfSubject]];
};
CreateMsgInfo: PROC[msg: ROPE, hasBeenRead: BOOL, tocEntry: ROPE, startOfSubject: INT]
RETURNS[msgInfo: MsgInfo] = {
GetDate: PROC[msg: ROPE] RETURNS [bt: BasicTime.GMT] ~ {
at: INT ¬ Rope.Find[msg, atSign];
IF at = -1 THEN RETURN[BasicTime.earliestGMT];
bt ¬ Convert.TimeFromRope[msg.Substr[start: at+1] ! Convert.Error =>
{ bt ¬ BasicTime.earliestGMT; CONTINUE} ];
};
msgInfo ¬ NEW[MsgInfoRec ¬ [msg, tocEntry, startOfSubject, hasBeenRead, GetDate[msg], WalnutFilter.MsgInterestLevel[msg]]];
};
BuildMsgLineViewer: PROC[msI: MsgSetInfo, msgInfo: MsgInfo,
oldButton: TiogaButton¬NIL, where: EditSpan.Place¬before] = {
tocLooks: ROPE ¬ IF msgInfo.hasBeenRead THEN tocDefaultLooks ELSE tocUnreadLooks;
tocRope: ROPE ¬ msgInfo.tocName;
needsQ: BOOL ¬ userWantsQMs OR (tocUnreadLooks = NIL);
mfh: MsgSetFieldHandle ¬ NEW[MsgSetFieldHandleRec ¬ [
prev: msI.lastMFH,
tocButton: NIL,
msI: msI,
msgInfo: msgInfo]];
tocRope ¬ WalnutFilter.MessageTOC[tocRope, msgInfo];
tocRope ¬ Rope.Concat[tabChar, tocRope];
IF ~msgInfo.hasBeenRead AND needsQ THEN BEGIN
tocRope ¬ Rope.Concat[questionMark, tocRope];
END;
mfh.tocButton ¬ TBQueue.CreateTiogaButtonAtNode[
q: msI.button.wH.walnutQueue,
viewer: msI.tiogaViewer,
oldButton: oldButton,
where: where,
rope: tocRope,
format: "header",
looks: tocLooks,
proc: MsgSetSelectionProc,
clientData: mfh];
IF msI.lastMFH # NIL THEN msI.lastMFH.next ¬ mfh;
msI.lastMFH ¬ mfh;
};
MsgSetSelectionProc: TiogaButtons.TiogaButtonProc = {
mfH: MsgSetFieldHandle = NARROW[clientData];
wH: WalnutHandle;
ctrlSh: INT ¬ IF control THEN 1 ELSE 0;
selOp: SelOp ¬ $none;
IF shift THEN ctrlSh ¬ ctrlSh+2;
IF mfH = NIL THEN RETURN;
wH ¬ mfH.msI.button.wH;
IF button = NIL THEN { WalnutWindow.Report[wH, noMsgRope]; RETURN; };
SELECT mouseButton FROM
red => SELECT ctrlSh FROM
0 => selOp ¬ $selOne;
1 => {
SelectMsgInMsgSet[wH, mfH, $selOne];
DeleteProc[parent: mfH.msI.container, mouseButton: mouseButton];
};
2 => TiogaOps.InsertRope[mfH.msgInfo.tocName];
ENDCASE;
yellow => SELECT ctrlSh FROM
0,2 => {SelectMsgInMsgSet[wH, mfH]; DisplayOneMsg[wH, mfH, shift]; };
1 => {
SelectMsgInMsgSet[wH, mfH, $selOne];
DeleteProc[parent: mfH.msI.container, mouseButton: mouseButton];
};
ENDCASE;
blue => SELECT ctrlSh FROM
0 => selOp ¬ $extend;
1 => NULL;
2 => selOp ¬ $deselect;
3 => selOp ¬ $add;
ENDCASE;
ENDCASE;
IF selOp#$none THEN SelectMsgInMsgSet[wH, mfH, selOp];
};
SelOp: TYPE ~ { none, selOne, extend, deselect, add };
SelectMsgInMsgSet: PROC[wH: WalnutHandle,
mfH: MsgSetFieldHandle, op: SelOp¬$selOne]~{
IF UserProfile.Boolean[walnutDotFancyNewMessageSelect, FALSE]
THEN FancyNewSelectMsgInMsgSet[wH, mfH, op]
ELSE IF op=$selOne THEN BadOldSelectMsgInMsgSet[wH, mfH];
};
FancyNewSelectMsgInMsgSet: PROC[wH: WalnutHandle, mfH: MsgSetFieldHandle, op: SelOp] ~ {
msI: MsgSetInfo;
prevMSI: MsgSetInfo = wH.selectedMsgSet;
prevSelecteds: LIST OF MsgSetFieldHandle;
IF mfH.tocButton = NIL THEN { WalnutWindow.Report[wH, noMsgRope]; RETURN; };
prevSelecteds ¬
IF prevMSI#NIL AND prevMSI.button#NIL AND prevMSI.button.msViewer#NIL
THEN prevMSI.selected ELSE NIL;
SELECT op FROM
$selOne => {
Turn off previous selection(s)
FOR pS: LIST OF MsgSetFieldHandle ¬ prevSelecteds, pS.rest WHILE pS#NIL DO
prevSelected: MsgSetFieldHandle ¬ pS.first;
IF prevSelected.tocButton#NIL THEN {
TiogaButtons.ChangeButtonLooks[
button: prevSelected.tocButton,
addLooks: tocDefaultLooks,
removeLooks: tocSelectedLooks];
};
ENDLOOP;
IF prevMSI#NIL THEN prevMSI.selected ¬ NIL;
wH.selectedMsgSet ¬ NIL;
IF mfH=NIL THEN RETURN;
msI ¬ mfH.msI;
TiogaButtons.ChangeButtonLooks[button: mfH.tocButton, addLooks: tocSelectedLooks];
msI.selected ¬ LIST[mfH];
wH.selectedMsgSet ¬ msI;
};
$add => {
selKind: SelKind ¬ $none;
prevSelectedMFH: LIST OF MsgSetFieldHandle;
[prevSelectedMFH, selKind] ¬ AnalyzeNewSel[prevSelecteds, mfH];
SELECT selKind FROM
$none => { WalnutWindow.Report[wH, noCurrentSel1]; RETURN; };
$first, $equal => RETURN;
ENDCASE;
TiogaButtons.ChangeButtonLooks[button: mfH.tocButton, addLooks: tocSelectedLooks];
IF selKind=$above THEN prevMSI.selected ¬ CONS[mfH, prevSelecteds]
ELSE {
newSel: LIST OF MsgSetFieldHandle ¬ LIST[mfH];
newSel.rest ¬ prevSelectedMFH.rest;
prevSelectedMFH.rest ¬ newSel;
};
};
$extend => {
selKind: SelKind ¬ $none;
prevSelectedMFH: LIST OF MsgSetFieldHandle;
tail: LIST OF MsgSetFieldHandle;
stopper: MsgSetFieldHandle;
above: BOOL¬FALSE;
[prevSelectedMFH, selKind] ¬ AnalyzeNewSel[prevSelecteds, mfH];
SELECT selKind FROM
$none => { WalnutWindow.Report[wH, noCurrentSel1]; RETURN; };
$first, $equal => RETURN;
$above => above ¬ TRUE;
ENDCASE;
stopper ¬ prevSelectedMFH.first;
tail ¬ IF above THEN prevSelectedMFH ELSE prevSelectedMFH.rest;
WHILE mfH#stopper DO
TiogaButtons.ChangeButtonLooks[button: mfH.tocButton, addLooks: tocSelectedLooks];
tail ¬ CONS[mfH, tail];
mfH ¬ IF above THEN mfH.next ELSE mfH.prev;
ENDLOOP;
IF above THEN prevMSI.selected ¬ tail ELSE prevSelectedMFH.rest ¬ tail;
};
$deselect => {
selKind: SelKind ¬ $none;
prevSelectedMFH: LIST OF MsgSetFieldHandle;
IF prevSelecteds=NIL THEN { WalnutWindow.Report[wH, noCurrentSel2]; RETURN; };
[prevSelectedMFH, selKind] ¬ AnalyzeNewSel[prevSelecteds, mfH];
SELECT selKind FROM
$none, $above, $below => { WalnutWindow.Report[wH, noCurrentSel2]; RETURN; };
ENDCASE;
TiogaButtons.ChangeButtonLooks[button: mfH.tocButton,
addLooks: tocDefaultLooks, removeLooks: tocSelectedLooks];
IF selKind=$first THEN prevMSI.selected ¬ prevSelecteds.rest
ELSE prevSelectedMFH.rest ¬ prevSelectedMFH.rest.rest;
};
ENDCASE;
};
SelKind: TYPE ~ { none, above, first, below, equal };
AnalyzeNewSel: PROC[selected: LIST OF MsgSetFieldHandle, mfH: MsgSetFieldHandle]
RETURNS [prevSelectedMFH: LIST OF MsgSetFieldHandle¬NIL, selKind: SelKind¬$none] ~ {
prevSelectedMFH ¬ selected;
IF prevSelectedMFH=NIL THEN RETURN;
IF mfH = prevSelectedMFH.first THEN RETURN[NIL, $first];
FOR pSM: MsgSetFieldHandle ¬ prevSelectedMFH.first, pSM.next
WHILE pSM#NIL DO
IF prevSelectedMFH.rest#NIL AND pSM=prevSelectedMFH.rest.first THEN
IF mfH=prevSelectedMFH.rest.first THEN RETURN[prevSelectedMFH, $equal]
ELSE prevSelectedMFH ¬ prevSelectedMFH.rest
ELSE IF pSM=mfH THEN RETURN[prevSelectedMFH, $below];
REPEAT
FINISHED => RETURN[selected, $above];
ENDLOOP;
};
BadOldSelectMsgInMsgSet: PROC[wH: WalnutHandle, mfH: MsgSetFieldHandle] ~ {
IF mfH.tocButton = NIL THEN WalnutWindow.Report[wH, noMsgRope]
ELSE
{ msI: MsgSetInfo = mfH.msI;
prevSelecteds: LIST OF MsgSetFieldHandle ¬ msI.selected;
prevSelected: MsgSetFieldHandle¬NIL;
IF prevSelecteds#NIL THEN prevSelected ¬ prevSelecteds.first;
IF prevSelected = mfH THEN RETURN;
turn off previous selection
IF prevSelected # NIL AND prevSelected.tocButton#NIL THEN
TiogaButtons.ChangeButtonLooks[
button: prevSelected.tocButton,
addLooks: tocDefaultLooks,
removeLooks: tocSelectedLooks];
TiogaButtons.ChangeButtonLooks[button: mfH.tocButton, addLooks: tocSelectedLooks];
msI.selected ¬ LIST[mfH];
};
};
DisplayOneMsg: PROC[wH: WalnutHandle, mfH: MsgSetFieldHandle, shift: BOOL] = {
IF mfH # NIL THEN {
DisplayMsgFromMsgSet[wH, mfH.msgInfo.msg, mfH.msI.container, shift];
IF NOT mfH.msgInfo.hasBeenRead AND NOT wH.readOnlyAccess THEN MarkMsgAsRead[wH, mfH];
};
};
MarkMsgAsRead: PROC[wH: WalnutHandle, mfH: MsgSetFieldHandle] = {
used by selection
msI: MsgSetInfo = mfH.msI;
WalnutOps.SetHasBeenRead[wH.opsH, mfH.msgInfo.msg];
mfH.msgInfo.hasBeenRead ¬ TRUE;
IF userWantsQMs THEN {
viewer: Viewer = TiogaOps.GetSelection[].viewer;
IF viewer # NIL THEN TiogaOps.SaveSelA[];
TiogaButtons.SetState[msI.tiogaViewer, editing];
TiogaOps.SetSelection[viewer: msI.tiogaViewer, start: [mfH.tocButton.startLoc.node, 0], end: [mfH.tocButton.endLoc.node, 0]];
TiogaOps.Delete[];
TiogaOps.InsertChar[' ];
IF viewer # NIL THEN TiogaOps.RestoreSelA[] ELSE TiogaOps.CancelSelection[];
TiogaButtons.SetState[msI.tiogaViewer, buttoning];
};
TiogaButtons.ChangeButtonLooks[
button: mfH.tocButton, addLooks: tocDefaultLooks, removeLooks: tocUnreadLooks];
};
Getting the menu changed
ChangeTheMenu: PROC [v: ViewerClasses.Viewer, isLevels: BOOL] = {
menu: Menus.Menu = v.menu;
msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[v, $MsgSetInfo]];
wH: WalnutHandle = msI.button.wH;
subMenu: Menus.MenuEntry = IF isLevels THEN wH.levelsMenuEntry ELSE wH.placesMenuEntry;
found: BOOL ¬ FALSE;
numLines: Menus.MenuLine = Menus.GetNumberOfLines[menu];
newLines: Menus.MenuLine ¬ numLines;
IF v.destroyed THEN RETURN;
FOR i: Menus.MenuLine IN [1..numLines) DO -- see if already showing the submenu
IF Rope.Equal[Menus.GetLine[menu,i].name, subMenu.name] THEN { -- yes, so remove it
FOR j: Menus.MenuLine IN (i..numLines) DO
Menus.SetLine[menu, j-1, Menus.GetLine[menu, j]];
ENDLOOP;
Menus.ChangeNumberOfLines[menu, newLast-1];
newLines ¬ newLines-1;
found ¬ TRUE; EXIT
};
ENDLOOP;
IF ~found THEN { -- add it. do insertion sort to get it in the right place
GoesBefore: PROC [m1, m2: Menus.MenuEntry] RETURNS [BOOL] = {
Priority: PROC [m: Menus.MenuEntry] RETURNS [INTEGER] = {
higher priority means goes above in series of submenus
looks at rope for first item to identify the subMenu.
RETURN [SELECT TRUE FROM
Rope.Equal[m.name, findRL] => 2,
Rope.Equal[m.name, firstLevelOnlyRL] => 1,
Rope.Equal[m.name, categoriesRL] => 0,
ENDCASE => -1 -- unknown menu goes at bottom -- ]
};
RETURN [Priority[m1] > Priority[m2]]
};
newLast: Menus.MenuLine = MIN[numLines, LAST[Menus.MenuLine]];
newLines ¬ newLines+1;
Menus.ChangeNumberOfLines[menu, newLast+1];
FOR i: Menus.MenuLine IN [1..numLines) DO
IF GoesBefore[subMenu, Menus.GetLine[menu, i]] THEN { -- put it here
FOR j: Menus.MenuLine DECREASING IN (i..newLast] DO
Menus.SetLine[menu, j, Menus.GetLine[menu, j-1]]; ENDLOOP;
Menus.SetLine[menu, i, subMenu];
found ¬ TRUE; EXIT
};
ENDLOOP;
IF ~found THEN Menus.SetLine[menu, newLast, subMenu];
};
ViewerBLT.ChangeNumberOfLines[v, newLines];
};
* * * * * * * * * * * * * * * * * * * * * *
CreateMsgSetMenus: PUBLIC PROC[wH: WalnutHandle] = {
added: Menus.MenuEntry;
displayerMenu: Menus.Menu ¬ Menus.CreateMenu[];
activeMenu: Menus.Menu ¬ Menus.CreateMenu[];
deletedMenu: Menus.Menu ¬ Menus.CreateMenu[];
readOnlyMenu: Menus.Menu ¬ Menus.CreateMenu[];
placesMenuEntry: Menus.MenuEntry;
levelsMenuEntry: Menus.MenuEntry;
Menu for generic Displayer
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, moveToRL, MoveToProc]];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, displayRL, DisplayProc]];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, deleteRL, DeleteProc]];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, addToRL, AddProc]];
Menus.AppendMenuEntry[displayerMenu, Menus.CreateEntry[placesRL, PlacesProc]];
Menus.AppendMenuEntry[displayerMenu, Menus.CreateEntry[levelsRL, LevelsProc]];
Menus.AppendMenuEntry[displayerMenu, Menus.CreateEntry[msgOpsRL, OpsProc]];
added ¬ WalnutFilter.MenuExtras[$msgset];
UNTIL added = NIL DO
me: Menus.MenuEntry ¬ added;
added ¬ added.link;
Menus.AppendMenuEntry[displayerMenu, me];
ENDLOOP;
second line
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, findRL, FindProc], 1];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, wordRL, WordProc], 1];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, defRL, DefProc], 1];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, positionRL, PositionProc], 1];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, normalizeRL, NormalizeProc], 1];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, prevPlaceRL, PrevPlaceProc], 1];
placesMenuEntry ¬ Menus.GetLine[displayerMenu, 1];
third line
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, firstLevelOnlyRL, FirstOnlyProc], 2];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, moreLevelsRL, MoreLevelsProc], 2];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, fewerLevelsRL, FewerLevelsProc], 2];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, allLevelsRL, AllLevelsProc], 2];
levelsMenuEntry ¬ Menus.GetLine[displayerMenu, 2];
Menus.ChangeNumberOfLines[displayerMenu, 1];
Menu for Active
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, moveToRL, MoveToProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, displayRL, DisplayProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, deleteRL, DeleteProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, addToRL, AddProc]];
Menus.AppendMenuEntry[activeMenu,
Menus.CreateEntry[newMailRL, NewMail]];  -- "immediate"
Menus.AppendMenuEntry[activeMenu, Menus.CreateEntry[placesRL, PlacesProc]];
Menus.AppendMenuEntry[activeMenu, Menus.CreateEntry[levelsRL, LevelsProc]];
Menus.AppendMenuEntry[activeMenu, Menus.CreateEntry[msgOpsRL, OpsProc]];
added ¬ WalnutFilter.MenuExtras[$msgset];
UNTIL added = NIL DO
me: Menus.MenuEntry ¬ added;
added ¬ added.link;
Menus.AppendMenuEntry[activeMenu, me];
ENDLOOP;
second line
Menus.SetLine[activeMenu, 1, placesMenuEntry];
third line
Menus.SetLine[activeMenu, 2, levelsMenuEntry];
Menus.ChangeNumberOfLines[activeMenu, 1];
Menu for Deleted
Menus.AppendMenuEntry[deletedMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, moveToRL, MoveToProc]];
Menus.AppendMenuEntry[deletedMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, displayRL, DisplayProc]];
Menus.AppendMenuEntry[deletedMenu, Menus.CreateEntry[placesRL, PlacesProc]];
Menus.AppendMenuEntry[deletedMenu, Menus.CreateEntry[levelsRL, LevelsProc]];
Menus.AppendMenuEntry[deletedMenu, Menus.CreateEntry[msgOpsRL, OpsProc]];
added ¬ WalnutFilter.MenuExtras[$msgset];
UNTIL added = NIL DO
me: Menus.MenuEntry ¬ added;
added ¬ added.link;
Menus.AppendMenuEntry[deletedMenu, me];
ENDLOOP;
second line
Menus.SetLine[deletedMenu, 1, placesMenuEntry];
third line
Menus.SetLine[deletedMenu, 2, levelsMenuEntry];
Menus.ChangeNumberOfLines[deletedMenu, 1];
Menu for ReadOnly database
Menus.AppendMenuEntry[readOnlyMenu,
WalnutViewer.CreateMenuEntry[wH.walnutQueue, displayRL, DisplayProc]];
Menus.AppendMenuEntry[readOnlyMenu, Menus.CreateEntry[placesRL, PlacesProc]];
Menus.AppendMenuEntry[readOnlyMenu, Menus.CreateEntry[levelsRL, LevelsProc]];
Menus.AppendMenuEntry[readOnlyMenu, Menus.CreateEntry[msgOpsRL, OpsProc]];
added ¬ WalnutFilter.MenuExtras[$msgset];
UNTIL added = NIL DO
me: Menus.MenuEntry ¬ added;
added ¬ added.link;
Menus.AppendMenuEntry[readOnlyMenu, me];
ENDLOOP;
second line
Menus.SetLine[readOnlyMenu, 1, placesMenuEntry];
third line
Menus.SetLine[readOnlyMenu, 2, levelsMenuEntry];
Menus.ChangeNumberOfLines[readOnlyMenu, 1];
wH.displayerMenu ¬ displayerMenu;
wH.activeMenu ¬ activeMenu;
wH.deletedMenu ¬ deletedMenu;
wH.readOnlyMenu ¬ readOnlyMenu;
wH.placesMenuEntry ¬ placesMenuEntry;
wH.levelsMenuEntry ¬ levelsMenuEntry;
};
WhenProfileChanges: UserProfile.ProfileChangedProc = {
tocDefaultLooks ¬ UserProfile.Token[key: "Walnut.TOCDefaultLooks", default: ""];
tocSelectedLooks ¬ UserProfile.Token[key: "Walnut.TOCSelectedLooks", default: "sb"];
tocUnreadLooks ¬ UserProfile.Token[key: "Walnut.TOCUnreadLooks", default: "i"];
userWantsQMs ¬ UserProfile.Boolean[key: "Walnut.ShowUnreadWithQMs", default: TRUE];
openHeightForActive ¬
UserProfile.Number[key: "Walnut.OpenHeightForActive", default: 0];
};
* * * * * * * * * * * * * * * * * * * * * * * * *
start code
UserProfile.CallWhenProfileChanges[WhenProfileChanges];
Menus.AppendMenuEntry[fillingInMenu, Menus.CreateEntry["AbortEnumeration", AbortEnumProc]];
END.