-- File: WalnutMsgSetDisplayerImpl.mesa
-- Contents: Implementation of the WalnutMsg Editor windows.
-- Status: mostly here functionally, but not yet completed.
-- Last Edited by: Willie-Sue, December 9, 1982 12:29 pm
-- Last edit by:
-- Rick on: April 29, 1982 7:02 pm
-- Donahue, April 4, 1983 8:38 am
-- Willie-Sue on: June 2, 1983 4:11 pm
DIRECTORY
Atom
USING [GetPropFromList],
Buttons,
Menus
USING [AppendMenuEntry, CreateMenu, Menu, MenuProc],
Rope,
ViewerClasses,
ViewerLocks
USING [CallUnderWriteLock],
ViewerOps
USING [AddProp, ComputeColumn, CreateViewer, DestroyViewer, FetchProp,
GrowViewer, MoveViewer, OpenIcon, PaintViewer, SetMenu],
WalnutDB,
WalnutLog,
WalnutDisplayerOps,
WalnutViewer,
WalnutWindow;
WalnutMsgSetDisplayerImpl:
CEDAR
PROGRAM
IMPORTS
Atom, Rope,
WalnutDB, WalnutLog,
WalnutDisplayerOps, WalnutViewer, WalnutWindow,
Buttons, Menus, ViewerLocks, ViewerOps
EXPORTS
WalnutDisplayerOps =
BEGIN
OPEN WalnutDB, WalnutWindow, WalnutDisplayerOps;
displayerMenu:
PUBLIC Menus.Menu = Menus.CreateMenu[];
activeMenu:
PUBLIC Menus.Menu = Menus.CreateMenu[];
deletedMenu:
PUBLIC Menus.Menu = Menus.CreateMenu[];
buildingMenu:
PUBLIC Menus.Menu = Menus.CreateMenu[];
msgSetName:
PUBLIC
ROPE = "Walnut!MsgSet!";
BuildDisplayerMenu:
PROC =
BEGIN
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Categories", CategoriesProc]];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "MoveTo", MoveToProc]];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Display", DisplayProc]];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Delete", DeleteProc]];
Menus.AppendMenuEntry[displayerMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "AddTo", AddProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Categories", CategoriesProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "MoveTo", MoveToProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Display", DisplayProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Delete", DeleteProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "AddTo", AddProc]];
Menus.AppendMenuEntry[activeMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "NewMail", NewMailProc]];
Menus.AppendMenuEntry[deletedMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Categories", CategoriesProc]];
Menus.AppendMenuEntry[deletedMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "MoveTo", MoveToProc]];
Menus.AppendMenuEntry[deletedMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Display", DisplayProc]];
END;
-- * * * * * * * * * * * * * * * * * * * * * *
MoveToProc: Menus.MenuProc =
BEGIN
viewer: Viewer =
NARROW[parent];
selected: Viewer =
NARROW[ViewerOps.FetchProp[viewer, $selectedMsg]];
IF selected =
NIL
THEN Report[" No selected msg to be moved"]
ELSE
{ ra:
REF
ANY =
NARROW[ViewerOps.FetchProp[viewer, $WalnutEntity]];
thisMsgSet: MsgSet = V2E[ra];
mfh: MsgSetFieldHandle =
NARROW[ViewerOps.FetchProp[selected, $mfH]];
msgSetList:
LIST
OF MsgSet = GetSelectedMsgSets[];
first:
BOOL←
TRUE;
moveToSelf:
BOOL←
FALSE;
IF msgSetList =
NIL
THEN
{ Report[" No selected MsgSets to Move msg to"];
RETURN};
IF ~mfh.hasBeenRead
THEN MarkMsgAsRead[mfh, selected];
FOR msL:
LIST
OF MsgSet ← msgSetList, msL.rest
UNTIL msL=
NIL
DO
IF EqEntities[msL.first, thisMsgSet]
THEN moveToSelf ←
TRUE
ENDLOOP;
ReportDisposition[mfh.msg]; ReportRope["Moved to:"];
FOR msL:
LIST
OF MsgSet← msgSetList, msL.rest
UNTIL msL=
NIL
DO
IF first
THEN first ←
FALSE
ELSE ReportRope[","];
ReportRope[" "]; ReportRope[GetName[msL.first]];
ENDLOOP;
ReportRope["\n"];
IF ~moveToSelf
THEN RemoveFromDisplayedMsgSet[viewer, selected, mfh.msg]
ELSE [] ← AdvanceSelection[mfh];
IF mouseButton#red
THEN DisplaySelectedMsg[viewer, shift];
FOR msL:
LIST
OF MsgSet ← msgSetList, msL.rest
UNTIL msL=
NIL
DO
IF
NOT EqEntities[msL.first, thisMsgSet]
THEN
{ rel: Relship; existed:
BOOL;
WalnutLog.LogAddMsg[mfh.msgName, GetName[msL.first]];
[rel, existed]← WalnutDB.AddMsgToMsgSet[msg: mfh.msg, msgSet: msL.first];
IF ~existed
THEN AddToMsgSetDisplayer[mfh, msL.first, rel] };
ENDLOOP;
IF
NOT moveToSelf
THEN
{ WalnutLog.LogRemoveMsg[mfh.msgName, GetName[thisMsgSet]];
[]← WalnutDB.RemoveMsgFromMsgSet[mfh.msg, thisMsgSet, mfh.relship]
};
};
END;
NewMailProc: Menus.MenuProc =
BEGIN
v: Viewer =
NARROW[parent];
oldM: Menus.Menu = v.menu;
ViewerOps.SetMenu[v, buildingMenu];
[]← WalnutWindow.RetrieveNewMail[];
v.menu ← oldM;
ViewerOps.PaintViewer[v, menu];
END;
CategoriesProc: Menus.MenuProc =
BEGIN
msViewer: Viewer←
NARROW[parent];
selected: Viewer←
NARROW[ViewerOps.FetchProp[msViewer, $selectedMsg]];
mfh: MsgSetFieldHandle;
IF selected =
NIL
THEN {Report[" No selected msg"];
RETURN};
mfh←
NARROW[ViewerOps.FetchProp[selected, $mfH]];
MsgCategories[mfh.msg];
END;
DeleteProc: Menus.MenuProc =
BEGIN
msViewer: Viewer =
NARROW[parent];
msName:
ROPE;
selected: Viewer =
NARROW[ViewerOps.FetchProp[msViewer, $selectedMsg]];
IF selected =
NIL
THEN Report[" No selected msg to be deleted"]
ELSE
{ ra:
REF
ANY =
NARROW[ViewerOps.FetchProp[msViewer, $WalnutEntity]];
thisMsgSet: MsgSet = V2E[ra];
mfh: MsgSetFieldHandle;
newRel: Relship;
IF EqEntities[thisMsgSet, deletedMsgSet]
THEN
RETURN;
-- already deleted
mfh←
NARROW[ViewerOps.FetchProp[selected, $mfH]];
IF ~mfh.hasBeenRead
THEN MarkMsgAsRead[mfh, selected];
RemoveFromDisplayedMsgSet[msViewer, selected, mfh.msg];
ReportDisposition[mfh.msg];
ReportRope["Deleted from "]; Report[msName← GetName[thisMsgSet]];
IF mouseButton#red
THEN DisplaySelectedMsg[msViewer, shift];
WalnutLog.LogRemoveMsg[mfh.msgName, msName];
newRel← WalnutDB.RemoveMsgFromMsgSet[mfh.msg, thisMsgSet, mfh.relship];
IF newRel #
NIL
THEN AddToMsgSetDisplayer[mfh, deletedMsgSet, newRel] };
END;
AddProc: Menus.MenuProc = {
viewer: Viewer =
NARROW[parent];
selected: Viewer =
NARROW[ViewerOps.FetchProp[viewer, $selectedMsg]];
IF selected =
NIL
THEN Report[" No selected msg to be moved"]
ELSE
{ ra:
REF
ANY =
NARROW[ViewerOps.FetchProp[viewer, $WalnutEntity]];
thisMsgSet: MsgSet = V2E[ra];
mfh: MsgSetFieldHandle =
NARROW[ViewerOps.FetchProp[selected, $mfH]];
msgSetList:
LIST
OF MsgSet = GetSelectedMsgSets[];
msgSetAddedTo: MsgSet;
first:
BOOL←
TRUE;
IF msgSetList =
NIL
THEN
{ Report[" No selected MsgSets to Add msg to"];
RETURN};
IF ~mfh.hasBeenRead
THEN MarkMsgAsRead[mfh, selected];
ReportDisposition[mfh.msg]; ReportRope["Added to:"];
FOR msL:
LIST
OF MsgSet ← msgSetList, msL.rest
UNTIL msL=
NIL
DO
IF first
THEN first ←
FALSE
ELSE ReportRope[","];
ReportRope[" "];
ReportRope[GetName[msL.first]];
ENDLOOP;
ReportRope["\n"];
IF mouseButton#red
THEN
{ [] ← AdvanceSelection[mfh]; DisplaySelectedMsg[viewer, shift] };
FOR msL:
LIST
OF MsgSet ← msgSetList, msL.rest
UNTIL msL=
NIL
DO
IF
NOT EqEntities[(msgSetAddedTo← msL.first), thisMsgSet]
THEN
{ rel: Relship; existed:
BOOL;
WalnutLog.LogAddMsg[mfh.msgName, GetName[msgSetAddedTo]];
[rel, existed]← AddMsgToMsgSet[msg: mfh.msg, msgSet: msgSetAddedTo];
IF ~existed
THEN AddToMsgSetDisplayer[mfh, msgSetAddedTo, rel] };
ENDLOOP }; };
AdvanceSelection:
PROCEDURE [msfH: MsgSetFieldHandle]
RETURNS [v: Viewer] = {
v ← msfH.next;
IF v = NIL THEN { Report[" No Next message"]; RETURN };
[] ← SelectMsgInMsgSet[v] };
DisplayProc: Menus.MenuProc =
BEGIN
viewer: Viewer = NARROW[parent];
selected: Viewer ← NARROW[ViewerOps.FetchProp[viewer, $selectedMsg]];
msfH: MsgSetFieldHandle = IF selected = NIL THEN NIL
ELSE NARROW[ViewerOps.FetchProp[selected, $mfH]];
IF mouseButton # red AND msfH # NIL THEN selected ← AdvanceSelection[msfH];
IF selected = NIL THEN {Report[" No selected msg to be displayed"]; RETURN};
DisplayOneMsg[selected, shift]
END;
ReportDisposition: PROC[msg: Msg] =
BEGIN
msgSubject: ROPE← V2S[MGetP[msg, mSubjectIs]];
IF msgSubject.Length[] > 24 THEN msgSubject← Rope.Concat[msgSubject.Substr[0, 21], " ..."];
ReportRope[Rope.Cat["Msg: ", msgSubject, ": has been "]];
END;
DisplaySelectedMsg: PROC[viewer: Viewer, shift: BOOL] =
BEGIN
selected: Viewer← NARROW[ViewerOps.FetchProp[viewer, $selectedMsg]];
IF selected # NIL THEN DisplayOneMsg[selected, shift];
END;
-- * * * * * * * * * * * * * * * * * * * * * *
BuildMsgSetDisplayer: PUBLIC PROC
[msgSet: MsgSet, name: ROPE, shift: BOOL← FALSE, initialActive: BOOL← FALSE]
RETURNS[msV: Viewer] =
BEGIN
iconic: BOOL← FALSE;
whichSide: ViewerClasses.Column← left;
IF EqEntities[msgSet, activeMsgSet] THEN
{ iconic← WalnutWindow.initialActiveIconic AND WalnutWindow.initialActiveOpen;
IF WalnutWindow.initialActiveRight THEN whichSide← right;
};
msV← ViewerOps.CreateViewer[
flavor: $Container, paint: FALSE,
info: [name: Rope.Concat["MsgSet: ", name], column: whichSide, menu: NIL,
iconic: iconic, icon: msgSetIcon, inhibitDestroy: TRUE]];
ViewerOps.AddProp[msV, $Entity, msgSetName.Concat[name]];
ViewerOps.AddProp[msV, $WalnutEntity, msgSet];
IF shift THEN
{ IF iconic THEN ViewerOps.OpenIcon[msV, shift] ELSE ViewerOps.GrowViewer[msV]};
END;
DisplayMsgSetInViewer:
PUBLIC PROC[msgSet: MsgSet, msViewer: Viewer, shift: BOOL← FALSE] =
BEGIN OPEN ViewerOps;
menu: Menus.Menu←
IF EqEntities[msgSet, activeMsgSet] THEN activeMenu ELSE
IF EqEntities[msgSet, deletedMsgSet] THEN deletedMenu ELSE displayerMenu;
IF ~msViewer.iconic THEN ComputeColumn[msViewer.column, TRUE];
msViewer.newVersion← TRUE; PaintViewer[msViewer, caption];
SetMenu[msViewer, buildingMenu];
BuildTupleWindowButtons[msViewer, msgSet];
SetMenu[msViewer, menu];
msViewer.newVersion← FALSE; PaintViewer[msViewer, caption];
msViewer.inhibitDestroy← FALSE;
END;
FixUpMsgSetViewer: PUBLIC PROC[msName: ROPE, v: Viewer] =
BEGIN
msgSet: MsgSet← DeclareMsgSet[msName, OldOnly].msgSet;
child: Viewer;
IF msgSet = NIL THEN
{ Report["MsgSet: ", msName, " doesn't exist; destroying viewer"];
ViewerOps.DestroyViewer[v];
RETURN
};
-- delete buttons & start again
UNTIL (child← v.child) = NIL DO ViewerOps.DestroyViewer[child, FALSE] ENDLOOP;
ViewerOps.PaintViewer[v, client]; -- paint cleared viewer
ViewerOps.AddProp[v, $selectedMsg, NIL]; -- no selected Msg
ViewerOps.AddProp[v, $WalnutEntity, msgSet];
DisplayMsgSetInViewer[msgSet, v];
END;
-- * * * * * * * * * * * * * * * * * * * * * *
-- for adding new messages to displayed MsgSet
AddParsedMsgToMSViewer: PUBLIC PROC
[msg: Msg, msgR: WalnutLog.MsgRec, msViewer: Viewer, rel: Relship] =
BEGIN
prevMfh, mfhN: MsgSetFieldHandle;
name: ROPE;
lastButton: Viewer← NARROW[ViewerOps.FetchProp[msViewer, $lastButton]];
IF lastButton#NIL THEN prevMfh← NARROW[ViewerOps.FetchProp[lastButton, $mfH]]
ELSE lastButton← msViewer;
name← Rope.Cat[IF msgR.hasBeenRead THEN " " ELSE "? ", msgR.tocEntry, msgR.subject];
[mfhN, lastButton]← BuildMsgLineViewer[lastButton, name];
mfhN.posOK← TRUE;
mfhN.headersPos← msgR.headersPos;
mfhN.msgLength← msgR.msgLength;
mfhN.relship← rel;
mfhN.msg← msg;
mfhN.msgName← msgR.gvID;
mfhN.hasBeenRead← msgR.hasBeenRead;
IF prevMfh # NIL THEN prevMfh.next← lastButton;
ViewerOps.AddProp[msViewer, $lastButton, lastButton];
END;
AddToMsgSetDisplayer:PROC[mfh: MsgSetFieldHandle, msAddedTo: MsgSet, rel: Relship] =
BEGIN
prevMfh, mfhN: MsgSetFieldHandle;
lastButton: Viewer;
msgSetViewer: Viewer← FindMSViewer[msAddedTo];
IF msgSetViewer = NIL THEN RETURN; -- msgSet not displayed
lastButton← NARROW[ViewerOps.FetchProp[msgSetViewer, $lastButton]];
IF lastButton#NIL THEN prevMfh← NARROW[ViewerOps.FetchProp[lastButton, $mfH]]
ELSE lastButton← msgSetViewer;
[mfhN, lastButton]← BuildMsgLineViewer[lastButton, mfh.msgTOC];
mfhN.relship← rel;
mfhN.msg← mfh.msg;
IF mfh.posOK THEN
{ mfhN.posOK← TRUE;
mfhN.headersPos← mfh.headersPos;
mfhN.msgLength← mfh.msgLength;
};
IF prevMfh # NIL THEN prevMfh.next← lastButton;
ViewerOps.AddProp[msgSetViewer, $lastButton, lastButton];
END;
AddMsgToMsgSetDisplayer: PUBLIC PROC[msg: Msg, msgSet: MsgSet, rel: Relship] =
BEGIN
prevMfh, mfhN: MsgSetFieldHandle;
lastButton: Viewer;
msgSetViewer: Viewer← FindMSViewer[msgSet];
IF msgSetViewer = NIL THEN RETURN; -- msgSet not displayed
lastButton← NARROW[ViewerOps.FetchProp[msgSetViewer, $lastButton]];
IF lastButton#NIL THEN prevMfh← NARROW[ViewerOps.FetchProp[lastButton, $mfH]]
ELSE lastButton← msgSetViewer;
[mfhN, lastButton]← BuildMsgLineViewer[lastButton, MsgButtonName[msg, TRUE]];
mfhN.relship← rel;
mfhN.msg← msg;
IF prevMfh # NIL THEN prevMfh.next← lastButton;
ViewerOps.AddProp[msgSetViewer, $lastButton, lastButton];
END;
-- called with parent & viewer 'displaying' msg to be deleted
RemoveFromDisplayedMsgSet: PROC[parent, which: Viewer, msg: Msg] =
BEGIN
delta: INTEGER;
mfhT, x: MsgSetFieldHandle;
bt, prev, next: Viewer;
oldName: ROPE;
selected: Viewer← NARROW[ViewerOps.FetchProp[parent, $selectedMsg]];
RemoveFrom: PROC =
BEGIN
delta← mfhT.next.wy - which.wy;
ViewerOps.DestroyViewer[which, FALSE]; -- don't paint
FOR bt← next, mfhT.next UNTIL bt = NIL DO
mfhT← NARROW[ViewerOps.FetchProp[bt, $mfH]];
IF mfhT.next = NIL THEN -- last line, erase in current position
{ oldName← bt.name;
bt.name← NIL;
ViewerOps.PaintViewer[bt, all];
bt.name← oldName;
};
ViewerOps.MoveViewer[bt, bt.wx, bt.wy-delta, bt.ww, bt.wh, FALSE];
ViewerOps.PaintViewer[bt, all];
ENDLOOP;
END;
IF which = NIL THEN which← FindMsgViewer[parent, msg];
IF selected = which THEN Buttons.SetDisplayStyle[which, $BlackOnWhite];
mfhT← NARROW[ViewerOps.FetchProp[which, $mfH]];
prev← mfhT.prev;
IF (next← mfhT.next) = NIL
THEN
{ ViewerOps.AddProp[parent, $lastButton, mfhT.prev]; -- last line
which.name← NIL;
ViewerOps.PaintViewer[which, all]; -- erase line
ViewerOps.DestroyViewer[which, FALSE]; -- don't paint
}
ELSE
IF parent.iconic THEN RemoveFrom[]
ELSE ViewerLocks.CallUnderWriteLock[RemoveFrom, parent];
-- take which out of chain of mfh's
IF prev # NIL THEN
{ x← NARROW[ViewerOps.FetchProp[prev, $mfH]];
IF x # NIL THEN x.next← next};
IF next # NIL THEN
{ x← NARROW[ViewerOps.FetchProp[next, $mfH]];
x.prev← prev};
IF selected = which THEN
{ IF next = NIL THEN ViewerOps.AddProp[parent, $selectedMsg, NIL] -- no selected Msg
ELSE
{ ViewerOps.AddProp[parent, $selectedMsg, next];
Buttons.SetDisplayStyle[next, $BlackOnGrey]; -- show it is selected
ViewerOps.PaintViewer[next, all];
};
};
END;
RemoveFromMsgSetDisplayer: PUBLIC PROC[msgSet: MsgSet, msg: Msg] =
BEGIN
msV: Viewer← FindMSViewer[msgSet];
IF msV = NIL THEN RETURN;
RemoveFromDisplayedMsgSet[msV, FindMsgViewer[msV, msg], msg];
END;
FindMFH: PROC[msViewer: Viewer, rel: Relship] RETURNS[mfh: MsgSetFieldHandle] =
BEGIN
lb: Viewer← NARROW[ViewerOps.FetchProp[msViewer, $lastButton]];
DO
IF lb = NIL THEN RETURN;
mfh← NARROW[ViewerOps.FetchProp[lb, $mfH]];
IF EqEntities[mfh.relship, rel] THEN RETURN;
lb← mfh.prev;
ENDLOOP;
END;
FindMsgViewer: PROC[msViewer: Viewer, msg: Msg] RETURNS[mViewer: Viewer] =
BEGIN
mfh: MsgSetFieldHandle;
mViewer← NARROW[ViewerOps.FetchProp[msViewer, $lastButton]];
DO
IF mViewer = NIL THEN RETURN;
mfh← NARROW[ViewerOps.FetchProp[mViewer, $mfH]];
IF EqEntities[mfh.msg, msg] THEN RETURN;
mViewer← mfh.prev;
ENDLOOP;
END;
BuildTupleWindowButtons: PROC[v: Viewer, e: Entity] =
BEGIN
tuplesList: LIST OF Relship;
prevMfh, mfh: MsgSetFieldHandle← NIL;
lastButton: Viewer← v;
DoTupleWindowButtons: PROC =
{ FOR tL: LIST OF Relship← tuplesList, tL.rest UNTIL tL = NIL DO
m: Msg← V2E[DB.GetF[tL.first, mCategoryOf]]; -- follow relship to the Msg
hasBeenRead: BOOL← V2B[DB.GetP[m, mHasBeenReadIs]];
[mfh, lastButton]← BuildMsgLineViewer[lastButton, MsgButtonName[m, hasBeenRead]];
mfh.relship← tL.first;
mfh.msg← m;
mfh.hasBeenRead← hasBeenRead;
mfh.msgName← DB.GetName[m];
IF prevMfh # NIL THEN prevMfh.next← lastButton;
prevMfh← mfh;
ENDLOOP;
};
tuplesList← RelationSubsetList[mCategory, LIST[[mCategoryIs, e]]];
WalnutDB.AcquireDBLock[DoTupleWindowButtons];
ViewerOps.AddProp[v, $lastButton, lastButton]; -- for updating
END;
MsgButtonName: PROC[msg: Msg, hasBeenRead: BOOL] RETURNS [ROPE] =
{ RETURN[Rope.Cat[
IF hasBeenRead THEN " " ELSE "? ",
V2S[DB.GetP[msg, mTOCEntryIs]],
V2S[DB.GetP[msg, mSubjectIs]]]];
};
BuildMsgLineViewer: PROC[lastButton: Viewer, name: ROPE]
RETURNS[mfh: MsgSetFieldHandle, lb: Viewer] =
BEGIN
isIconic: BOOL;
BuildLine: PROC =
BEGIN
IF lastButton.parent = NIL THEN
lb← WalnutViewer.FirstButton[
q: walnutQueue,
name: name,
proc: MsgSetSelectionProc,
width: 1024, -- hack for now = screenW
parent: lastButton]
ELSE lb← WalnutViewer.AnotherButton[
q: walnutQueue,
name: name,
proc: MsgSetSelectionProc,
sib: lastButton,
width: 1024, -- hack for now = screenW
newLine: TRUE];
END;
isIconic← IF lastButton.parent = NIL THEN lastButton.iconic ELSE lastButton.parent.iconic;
IF isIconic THEN BuildLine[]
ELSE ViewerLocks.CallUnderWriteLock[BuildLine, lastButton];
-- Containers.ChildXBound[lb.parent, lb];
ViewerOps.AddProp[lb, $mfH, mfh← NEW[MsgSetFieldObject←
[msgViewer: lb, msgTOC: name, prev: lastButton, posOK: FALSE]]];
END;
MsgSetSelectionProc: Buttons.ButtonProc =
{ viewer: Viewer = NARROW[parent];
[]← SelectMsgInMsgSet[viewer];
IF control THEN DeleteProc[parent: viewer.parent, mouseButton: mouseButton]
ELSE IF mouseButton#red THEN DisplayOneMsg[viewer, shift];
};
DisplayOneMsg: PROC[viewer: Viewer, shift: BOOL] =
BEGIN
mfh: MsgSetFieldHandle = NARROW[ViewerOps.FetchProp[viewer, $mfH]];
v: Viewer = DisplayMsgFromMsgSet[mfh, viewer.parent, shift];
IF ~mfh.hasBeenRead THEN MarkMsgAsRead[mfh, viewer];
END;
SelectMsgInMsgSet: PROC[msgButton: Viewer] RETURNS[sameAsBefore: BOOL] =
BEGIN
prevSelected: Viewer = NARROW[ViewerOps.FetchProp[msgButton.parent, $selectedMsg]];
IF prevSelected = msgButton THEN RETURN[TRUE];
-- turn off previous selection
IF prevSelected # NIL AND ~prevSelected.destroyed THEN
{ Buttons.SetDisplayStyle[prevSelected, $BlackOnWhite];
ViewerOps.PaintViewer[prevSelected, all]};
IF msgButton.destroyed THEN Report["Msg is no longer in the MsgSet"]
ELSE
{Buttons.SetDisplayStyle[msgButton, $BlackOnGrey]; -- show it is selected
ViewerOps.PaintViewer[msgButton, all];
ViewerOps.AddProp[msgButton.parent, $selectedMsg, msgButton];
};
RETURN[FALSE];
END;
MarkMsgAsRead: PROC[mfh: MsgSetFieldHandle, msgButton: Viewer] =
-- used by selection, MoveTo & Delete
BEGIN
newName: ROPE = msgButton.name.Replace[0, 1, " "]; -- change ? to SP
WalnutLog.LogMsgHasBeenRead[mfh.msgName];
WalnutDB.SetMsgHasBeenRead[mfh.msg];
mfh.hasBeenRead← TRUE;
mfh.msgTOC← newName;
Buttons.ReLabel[msgButton, newName];
END;
----------------------------
-- start code
BuildDisplayerMenu[];
END.