<<>> <> <> <> <> <> <> <> <> <> <<(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; <<>> <> 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]; <<>> <> buildingMenu: Menus.Menu ¬ Menus.CreateMenu[]; fillingInMenu: Menus.Menu ¬ Menus.CreateMenu[]; <<>> <> MsgSetInfo: TYPE = WalnutWindowPrivate.MsgSetInfo; MsgSetInfoRec: TYPE = WalnutWindowPrivate.MsgSetInfoRec; <> <> <> <> <> <> <> <<];>> MsgSetFieldHandle: TYPE = WalnutWindowPrivate.MsgSetFieldHandle; MsgSetFieldHandleRec: TYPE = WalnutWindowPrivate.MsgSetFieldHandleRec; <> <> <> <> <<];>> MsgSetFieldHandles: TYPE ~ LIST OF MsgSetFieldHandle; <> <<>> 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; <> <<>> <> 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] = { <> 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}; <> 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]; <> IF displayProcess # NIL THEN TRUSTED { [] ¬ JOIN displayProcess } END; }; ComeFrom: PUBLIC PROC[msb: MsgSetButton] = { IF UserProfile.Boolean[walnutDotFancyNewMessageSelect, FALSE] THEN FancyNewComeFrom[msb] ELSE BadOldComeFrom[msb] }; <> <<>> 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; }; <> <> 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]; <<>> <> 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; <> 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 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; <> 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.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]; <> 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]; <> [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[]; <> 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]; }; <<* * * * * * * * * * * * * * * * * * * * * *>> <> 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; }; }; <> RemoveFromDisplayedMsgSet: PROC[ msI: MsgSetInfo, msViewer: Viewer, mfH: MsgSetFieldHandle, selectNext: BOOL] = { prev, next: MsgSetFieldHandle; bt: TiogaButton; bt ¬ mfH.tocButton; prev ¬ mfH.prev; next ¬ mfH.next; <> 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, <> 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 => { <> 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; <<>> <> 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] = { <> 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]; }; <> 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; <> 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] = { <> <> 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; <> 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; <> 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; <> 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]; <> 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]; <> 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; <<>> <> Menus.SetLine[activeMenu, 1, placesMenuEntry]; <> Menus.SetLine[activeMenu, 2, levelsMenuEntry]; Menus.ChangeNumberOfLines[activeMenu, 1]; <> 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; <<>> <> Menus.SetLine[deletedMenu, 1, placesMenuEntry]; <> Menus.SetLine[deletedMenu, 2, levelsMenuEntry]; Menus.ChangeNumberOfLines[deletedMenu, 1]; <> 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; <<>> <> Menus.SetLine[readOnlyMenu, 1, placesMenuEntry]; <> 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]; }; <<>> <<* * * * * * * * * * * * * * * * * * * * * * * * * >> <> UserProfile.CallWhenProfileChanges[WhenProfileChanges]; Menus.AppendMenuEntry[fillingInMenu, Menus.CreateEntry["AbortEnumeration", AbortEnumProc]]; <<>> END.