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