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. @ 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) Types Only common menu used in message set displays Types used in message set displays 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 ]; next, prev: MsgSetFieldHandle, tocButton: TiogaButton, -- TableOfContentsButton msI: MsgSetInfo, -- back pointer to msgSetInfo msgInfo: MsgInfo -- constant for a message ]; May need to be expanded to a record, at some point. MsgInfoRec: TYPE = RECORD[ msg, tocName: ROPE, startOfSubject: INT, hasBeenRead: BOOL, date: BasicTime.GMT ]; Rope literals Procedures of v is a Viewer for a Walnut Entity, then return its name else NIL First display the next message to keep the user busy Finally, make sure the displaying process is done 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 Standard behavior; selected message from selected message set(s) is/are moved. Works only when there is but one selected message. now update display adding to deleted doesn't make sense if this was an addTo from deleted and then remove message from deleted display viewer removing from deleted doesn't make sense if active is displayed, then its version number got incremented twice * * * * * * * * * * * * * * * * * * * * * * after SetMenu[parent, buildingMenu]; ViewerLocks.CallUnderWriteLock[ScrollMsgSet, v]; * * * * * * * * * * * * * * * * * * * * * * for adding new messages to displayed MsgSet called with parent & MsgSetFieldHandle 'displaying' msg to be deleted take which out of chain of mfh's tocButton: NIL, Turn off previous selection(s) turn off previous selection used by selection Getting the menu changed Menus.ChangeNumberOfLines[menu, newLast-1]; higher priority means goes above in series of submenus looks at rope for first item to identify the subMenu. Menus.ChangeNumberOfLines[menu, newLast+1]; * * * * * * * * * * * * * * * * * * * * * * Menu for generic Displayer second line third line Menu for Active second line third line Menu for Deleted second line third line Menu for ReadOnly database second line third line * * * * * * * * * * * * * * * * * * * * * * * * * start code Κ;’•NewlineDelimiter –(cedarcode) style™codešΟn™Kšœ ΟeœC™NK™)Kšœ$™$K™&K™(K™%K™$—K˜Kšœ8™8K˜K™nK™MK™+K™’K˜šΟk ˜ KšœŸœ˜K˜#Kšœ Ÿœ˜,KšœŸœ˜&Kšœ Ÿœ ˜KšœŸœ—˜’KšœŸœ ˜KšœŸœ!˜.Kšœ Ÿœy˜‹K˜Kšœ Ÿœ)˜:KšœŸœ˜(Kšœ Ÿœl˜~Kšœ ŸœV˜hKšœ ŸœŽ˜œKšœ ŸœF˜WKšœŸœ˜%Kšœ Ÿœ˜&Kšœ ŸœH˜Zšœ ŸœA˜PKšœ7˜7—Kšœ Ÿœ1˜Bšœ ŸœL˜]K˜—Kšœ Ÿœ ˜0Kšœ ŸœO˜aKšœ Ÿœ—˜¦K˜KšœŸœζ˜ϊKšœ Ÿœ˜%Kšœ ŸœF˜XKšœŸœ–˜―K˜—šΟbœŸœŸ˜(šŸ˜Kšœ œ?˜aKšœ%˜%Kšœ+˜+K˜:Kšœ<˜<—šŸ˜Kšœ˜—šŸ˜Kšœ˜——K˜KšŸœŸœ˜K™™K˜Kšœ Ÿœ˜-KšœŸœ˜$KšŸœŸœŸœ˜Kšœ Ÿœ˜-K˜KšœŸœ˜ KšœŸœ$˜6KšœŸœ$˜6KšœŸœŸœ'˜CK˜KšœŸœŸœ˜KšœŸœŸœ˜KšœŸœŸœ˜KšœŸœŸœŸœ˜ K˜KšœŸœ Οc˜0KšœŸœ˜K˜KšœŸœŸœ˜+KšœŸœŸœŸœ˜4—K™š -™-K˜.K˜/—K™š "™"K˜Kšœ Ÿœ"˜2šœŸœ%˜8Kšœ‘?™UKšœ‘"™7Kšœ‘ ™4Kšœ Ÿœ™$Kšœ™Kšœ‘-™DKšœ)™)K™—K˜KšœŸœ)˜@šœŸœ,˜FKšœ™Kšœ‘™1Kšœ‘™/Kšœ‘™+K™—K˜K˜šœŸœŸœŸœ˜5K™3K™—KšœŸœŸœ˜-šœŸœŸœ˜"KšœŸœ˜KšœŸœ˜KšœŸœ˜KšœŸœ˜K˜$KšœŸœŸœ˜KšœŸœŸ˜K˜K˜—Kšœ Ÿœ˜,Kšœ Ÿœ"˜2Kšœm™m—K™š  ™ K˜KšœŸœ,˜>Kšœ Ÿœ*˜9Kšœ Ÿœ1˜BKšœŸœF˜YKšœ Ÿœ$˜3KšœŸœ˜/KšœŸœ'˜:KšœŸœ˜!KšœŸœ˜!KšœŸœ˜Kšœ Ÿœ˜Kšœ Ÿœ˜!Kšœ Ÿœ˜%Kšœ Ÿœ˜Kšœ Ÿœ˜#Kšœ Ÿœ˜%KšœŸœ˜Kšœ Ÿœ˜KšœŸœ˜KšœŸœ˜Kšœ Ÿœ˜Kšœ Ÿœ˜Kšœ Ÿœ"˜FKšœŸœ˜4KšœŸœ˜@KšœŸœ˜@KšœŸœ˜'KšœŸœ ˜Kšœ Ÿœ ˜KšœŸœ ˜KšœŸœ˜*KšœŸœ˜"Kšœ Ÿœ ˜Kšœ Ÿœ ˜Kšœ Ÿœ ˜Kšœ Ÿœ ˜Kšœ Ÿœ ˜Kšœ Ÿœ ˜Kšœ Ÿœ ˜Kšœ Ÿœ ˜KšœŸœ ˜KšœŸœ ˜Kšœ Ÿœ˜Kšœ Ÿœ˜ Kšœ Ÿœ˜ KšœŸœ˜"KšœŸœ˜$Kšœ Ÿœ˜ K˜KšœŸœ˜!—K™š  ™ K˜—˜š  œŸœŸœ Ÿœ Ÿœ Ÿœ˜OKšœC™CKšœŸœ&˜>KšŸœŸœŸœŸœ˜K˜Kšœ˜K˜K˜KšœŸœ˜>KšœŸœ,˜5šœ(˜(K˜Z—Kšœ˜—š œ˜$KšœŸœ˜&Kšœ%˜%K˜—K˜ šŸœ ŸœŸœŸœ˜,Kšœ‘˜(KšœXŸœ˜]KšŸœ ŸœŸœ˜KšœC˜CKšŸ˜Kšœ˜—KšœŸœ,˜8K˜cK˜YKšœ7˜7KšŸœ ŸœŸœ˜Kšœ8˜8K˜0K˜%Kšœ˜——˜šœŸœŸœŸœ˜ZKšœ/˜/Kšœ˜K˜Kš ŸœŸœŸœŸœŸœ˜Kšœ<˜<—Kšœ*˜*šœM˜MK˜—KšŸœ˜—K˜K˜—K™NK™2šœŸœŸœ˜2KšœŸœ˜Kšœ ŸœŸœ˜Kšœ˜KšœŸœŸœ'˜>K˜šŸœŸœŸ˜Kšœ(Ÿœ˜0—š ŸœŸœŸœ)ŸœŸœŸ˜KKšœ"˜"Kšœ Ÿœ&˜7šŸœ$ŸœŸœ˜3Kšœ*˜*Kšœ˜Kšœ˜šŸœŸœŸœ˜KšœŸœŸœ"˜/šŸœŸœŸœ˜Kšœ&Ÿœ˜0—K˜šŸœ ŸœŸœ˜Kšœ(Ÿœ˜2—K˜—K˜šŸœEŸ˜KKšŸœŸœŸœ$Ÿœ˜C—šŸœŸ˜˜>Kšœ)˜)——šŸœŸœŸœŸ˜>Kšœ6Ÿœ˜<—Kšœ*˜*šœM˜MK˜—K˜—KšŸœ˜—K˜K˜—š œŸœŸœ˜CKš ŸœŸœŸœŸœŸœ˜Ÿœ˜DKšŸœ$˜(—Kšœ)˜)Kšœ Ÿœ˜#—KšŸœ˜KšŸœ˜—K˜%K™Kšœ™š ŸœŸœŸœ"ŸœŸœŸ˜BKšœ2Ÿœ˜8—KšŸœ˜K˜KšŸœŸœŸœ%˜WšŸœŸ˜KšœE˜E—Kš ŸœŸœŸœŸœŸœ˜A—KšŸœ˜K˜K˜—š  œŸœŸœŸœ Ÿœ)˜lKšœ˜K™%KšŸœ9ŸœŸœŸœ˜NK˜&šŸœLŸœ˜TKšœ(Ÿœ˜-KšœŸœŸœ Ÿœ ˜HKšœ.˜.K˜—K™UKšŸœ;ŸœŸœ;˜„K˜K˜—šœŸœŸœŸœ)˜FKšœ˜Kšœ!˜!K˜šŸœMŸœ˜UKšœ(Ÿœ˜-Kšœ9˜9Kšœ(˜(Kšœ.˜.K˜—K˜K˜—š œŸœŸœŸœ˜@Kšœ˜KšœŸœŸœ˜Kšœ!˜!K™)KšŸœ;ŸœŸœŸœ˜PK˜˜KšœV˜V—Kšœ9˜9Kšœ,˜,Kšœ0˜0šŸœŸ˜Kšœ6Ÿœ˜;—K˜——˜šœŸœ*˜@KšœŸœ˜%Kš ŸœŸœŸœŸœŸœ˜K˜KšŸœŸœŸœ$Ÿœ˜?Kšœ˜K˜——˜š œŸœ˜&KšœK˜KK˜K˜—š œŸœ˜,Kšœ˜K˜&K˜!K˜šŸœŸœŸ˜2K˜*—KšŸœ ŸœŸœŸœ˜šŸœŸœŸ˜ Kšœ)Ÿœ˜1—Kšœ'˜'šŸœŸœŸ˜Kšœc˜c—Kšœ˜—K˜š œŸœ,ŸœŸœ*Ÿœ ˜zKšœŸœ-˜EKšœ˜šœŸœ˜ KšœŸœ>˜XK˜—K˜KšŸœŸœŸœŸœ˜š ŸœŸœŸœ-ŸœŸœŸ˜OK˜šŸœŸœŸ˜ Kšœ)Ÿœ˜1—K˜KšŸœ˜—Kšœ˜—K˜š œŸ œ˜*Kšœ Ÿœ Ÿœ"Ÿœ˜EKšœ*˜*KšŸœŸœŸœŸœ˜KšŸœŸœŸœŸœ˜"Kšœ7˜7K˜K˜—šœŸœ>˜KKšœŸœ/˜9K˜K˜K˜—šœŸœ>˜LKšœŸœ2˜Ÿ˜?K˜NKšœIŸ˜JKšœ˜KšŸœ˜—K˜—KšœZŸœ˜lKšœ6˜6Kšœ9˜9Kšœ7˜7KšœL˜LšŸœŸœŸœŸœ˜&KšœŸœ˜ K˜#šŸœ Ÿœ ŸœŸ˜"K˜ K˜KšŸœ˜—šŸœ Ÿœ ŸœŸœ˜"K˜<—Kšœ˜——Kšœ˜K˜—šœŸœ(Ÿœ˜HKšœ Ÿœ8˜HšŸœŸ˜K˜'K˜DKšŸœŸœ˜K˜—KšœV˜VKšœ˜——˜Kšœ+™+—˜šœŸœ;Ÿœ˜NKšœŸœ˜KšœŸœ4˜=KšœŸœ;˜EKšœŸœŸœ˜K˜'Kšœ˜Kšœ Ÿœ5˜BK˜Kšœ Ÿœ˜Kšœ Ÿœ+Ÿœ˜@šœŸœŸœŸœ˜TšŸœŸœŸ˜KšœŸœŸœ˜(KšœŸœŸœ‘˜AKšœŸœŸœ˜*KšŸœŸœ˜K˜——K˜šŸœ Ÿœ˜KšœŸœ˜3K˜VKšŸœŸœ#˜BKšœ˜KšŸœO˜S—K˜Kš ŸœŸœŸœŸœŸœ˜1šŸœŸœŸœ˜KšœŸœ)˜5šŸœŸœ˜Kšœ‡˜‡KšœŸœ˜ K˜—K˜—šŸœŸœŸœ‘˜-KšœŸœ˜KšœŸœ)˜5K˜'KšœŸœ˜šŸœ)ŸœŸœ˜8K˜Kšœ(Ÿœ˜,K˜*Kšœ˜šŸœ‘)˜0šŸœ<˜>šœ(Ÿœ˜.KšœŸœ˜KšŸœ˜ Kšœ˜——Kšœ˜——Kšœ)˜)KšœŸœ˜KšœŸœ˜KšœŸœ˜K˜ K˜šŸœ˜˜K˜šœ/Ÿœ˜3Kšœ.ŸœŸ˜JKšœ˜Kšœ˜——KšœŸœ˜5KšœŸœ˜K˜K˜K˜8˜Kšœ>Ÿœ˜D—šŸœ Ÿ˜K˜T—˜K˜———šŸœŸœŸœ˜šœŸœŸœŸœŸ˜GKšœ˜—˜,KšœŸœ.˜O—KšœJ˜JKšœ-˜-Kšœ-˜-K˜—K˜Kšœ)˜)K˜4Kšœ1˜1Kš Ÿœ ŸœŸœŸœ Ÿœ˜EKšœ˜—K˜šœ Ÿœ˜K˜˜K˜K˜K˜ —˜ K™——šœ˜+KšœŸœ+˜CK˜=KšœŸœ˜Kšœ9˜9Kšœ˜——˜KšœŸœŸœ˜+šœŸœŸ˜KšœJŸœ˜P——˜šœŸœEŸœ˜bKšŸœ ˜KšœŸœ˜KšœŸœ˜Kšœ ŸœAŸœ˜WK˜K˜˜šŸœŸœŸ˜.šŸœ Ÿ˜šŸœŸœŸœŸ˜BKšŸœ*ŸœŸœŸœ˜]————K˜š œŸœ˜šŸœ Ÿœ Ÿ˜šœŸœŸ˜˜*Kšœ;Ÿœ˜B—šœ™˜(KšŸœ5˜8——K˜KšŸœ ˜$—K˜—K˜Kšœ˜—K˜Kšœ#‘˜‘ ˜JK˜@Kšœ˜——K˜K™+˜Kšœ+™+—˜šœŸœ:˜TšŸœŸœŸœ˜KšŸœŸœŸœŸœ˜>KšŸœŸœŸœŸœ1˜OKšœ!˜!K˜MK˜—Kšœ˜——˜KšœE™EšœŸœ˜ KšœGŸœ˜PK˜Kšœ˜K˜K˜K˜K˜Kšœ ™ KšŸœŸœŸœ˜$KšŸœŸœŸœ˜$Kšœ˜K˜šŸœ Ÿœ˜š ŸœŸœŸœ,ŸœŸœŸ˜LKšŸœŸœŸœ ˜$KšŸ˜šœ ˜ šŸœŸœŸœ˜KšœŸœ˜šœ6˜6Kšœ:˜:—K˜—KšŸœŸœ˜—KšŸœ˜—K˜—KšŸœŸœ˜-K˜8Kšœ˜K˜—šœ‘.˜NKšœŸœ˜&KšŸœ˜ K˜——˜šœŸœ<Ÿœ˜YKšœŸœŸœŸœ˜8Kšœ Ÿœ˜ KšœŸœŸœ˜?K˜š  œŸœŸœŸœŸœ˜)KšŸœŸœŸœ˜KšŸœ˜"Kšœ˜—š œŸœŸœŸœŸœŸœ Ÿœ˜jKšœŸœ˜K˜K˜Kšœ˜KšŸœ"ŸœŸœŸœ˜8K˜DKšœŸœ*Ÿœ.˜gKšŸœŸœŸœ˜&šœ˜Kšœ˜—K˜šŸœŸœ"Ÿ˜=K˜—˜8K˜Z—KšŸœŸœ˜ Kšœ˜—š œ˜&KšœŸœ˜&K˜KšŸœ ŸœŸœ˜/K˜K˜—K˜cKšœ9˜9Kšœ)˜)KšœŸœ˜K˜K˜ Kšœ8Ÿœ˜KKšŸœŸœŸœ%˜=Kšœ*Ÿœ˜/KšœŸœ˜K˜9KšŸœ ŸœŸœŸœ˜/K˜%Kšœ˜K˜—š œŸœ˜'KšŸœŸœŸœ˜KšœŸœ ˜KšœŸœ,˜QKšŸœŸœŸœŸœ˜#KšœŸœ˜"Kšœ&˜&K˜——˜š œŸœŸœŸœ˜JKšœ Ÿœ˜Kšœ Ÿœ˜KšœŸœ˜K˜RKšŸœ<˜BK˜——˜š œŸœŸœŸœ ŸœŸœŸœ˜tš œŸœŸœŸœŸœ˜8KšœŸœ˜!KšŸœ ŸœŸœ˜.KšœcŸœ˜oK˜—Kšœ Ÿœn˜{K˜——˜šœŸœ#˜;KšœŸœ#˜=Kš œ ŸœŸœŸœŸœ˜QKšœ Ÿœ˜ KšœŸœŸœŸœ˜6šœŸœ˜5Kšœ˜Kšœ Ÿœ™Kšœ ˜ Kšœ˜—K˜K˜4K˜(šŸœŸœŸ ˜-K˜-KšŸœ˜—˜0Kšœ˜Kšœ˜K˜K˜ Kšœ˜Kšœ˜Kšœ˜KšœŸ˜Kšœ˜—K˜KšŸœŸœŸœ˜1K˜Kšœ˜——K˜˜šœ"˜5KšœŸœ ˜,K˜Kš œŸœŸœ ŸœŸœ˜'K˜KšŸœŸœ˜ KšŸœŸœŸœŸœ˜K˜KšŸœ ŸœŸœ'Ÿœ˜EšŸœ Ÿ˜šœŸœŸ˜K˜šœ˜K˜$Kšœ@˜@K˜—Kšœ.˜.KšŸœ˜—šœ ŸœŸ˜KšœE˜Ešœ˜K˜$Kšœ@˜@K˜—KšŸœ˜—šœŸœŸ˜K˜KšœŸœ˜ K˜K˜KšŸœ˜—KšŸœ˜KšŸœ Ÿœ#˜6—K˜K˜—KšœŸœ+˜6—˜šœŸœ˜)K˜,šŸœ5Ÿœ˜=KšŸœ'˜+KšŸœŸœ Ÿœ"˜9—Kšœ˜K˜—šœŸœ9˜XKšœ˜Kšœ(˜(KšœŸœŸœ˜)KšŸœŸœŸœ'Ÿœ˜L˜š Ÿœ ŸœŸœŸœŸœŸ˜EKšŸœŸœŸœ˜——šŸœŸ˜˜ Kšœ™š ŸœŸœŸœ,ŸœŸœŸ˜JK˜+šŸœŸœŸœ˜$šœ˜Kšœ˜Kšœ˜Kšœ˜—K˜—KšŸœ˜—KšŸœ ŸœŸœŸœ˜+KšœŸœ˜K˜KšŸœŸœŸœŸœ˜K˜KšœR˜RKšœŸœ˜K˜K˜—šœ ˜ K˜KšœŸœŸœ˜+K˜?šŸœ Ÿ˜Kšœ3Ÿœ˜=KšœŸœ˜KšŸœ˜—KšœR˜RKšŸœŸœŸœ˜BšŸœ˜KšœŸœŸœŸœ˜.K˜#K˜K˜—K˜—šœ ˜ K˜KšœŸœŸœ˜+KšœŸœŸœ˜ Kšœ˜KšœŸœŸœ˜K˜?šŸœ Ÿ˜Kšœ3Ÿœ˜=KšœŸœ˜KšœŸœ˜KšŸœ˜—K˜ KšœŸœŸœŸœ˜?šŸœ Ÿ˜KšœR˜RKšœŸœ ˜KšœŸœŸœ Ÿœ ˜+KšŸœ˜—KšŸœŸœŸœ˜GK˜—˜K˜KšœŸœŸœ˜+KšŸœŸœŸœ+Ÿœ˜NK˜?šŸœ Ÿ˜KšœCŸœ˜MKšŸœ˜—šœ5˜5Kšœ:˜:—KšŸœŸœ&˜šŸ˜šœ˜KšœŸœŸœ"˜8Kšœ Ÿœ˜$KšŸœŸœŸœ$˜=KšŸœŸœŸœ˜"—K™šœ™š ŸœŸœŸœŸœŸ˜9šœ˜Kšœ˜Kšœ˜Kšœ˜——K˜KšœR˜RKšœŸœ˜—K˜—Kšœ˜K˜—š œŸœ2Ÿœ˜NšŸœŸœŸœ˜KšœD˜DKš ŸœŸœŸœŸœŸœ˜UK˜—Kšœ˜——˜š œŸœ.˜AKšœ™Kšœ˜Kšœ3˜3KšœŸœ˜šŸœŸœ˜K˜0KšŸœ ŸœŸœ˜)K˜0Kšœ}˜}K˜K˜KšŸœ ŸœŸœŸœ˜LK˜2Kšœ˜—˜KšœO˜O—Kšœ˜K˜K˜——š ™K˜š œŸœ%Ÿœ˜AKšœ˜KšœŸœ&˜>K˜!KšœŸœ ŸœŸœ˜WKšœŸœŸœ˜K˜8K˜$KšŸœ ŸœŸœ˜šŸœŸœŸœ‘%˜OšŸœ6Ÿœ‘˜SšŸœŸœŸ˜)K˜1KšŸœ˜—Kšœ+™+K˜KšœŸœŸœ˜K˜—KšŸœ˜—šŸœŸœ‘:˜Kš œŸœŸœŸœ˜=šœŸœŸœŸœ˜9Kšœ6™6Kšœ5™5šŸœŸœŸœŸ˜Kšœ ˜ Kšœ*˜*Kšœ&˜&KšŸœ‘!œ˜2—K˜—KšŸœ˜%K˜—KšœŸœ Ÿœ˜>K˜Kšœ+™+šŸœŸœŸ˜)šŸœ-Ÿœ‘˜DšŸœŸ œŸœŸ˜3Kšœ2Ÿœ˜:—K˜ KšœŸœŸœ˜K˜—KšŸœ˜—KšŸœŸœ'˜5K˜—K˜+K˜K™——™+K™šœŸœŸœ˜4K˜K˜/K˜,K˜-K˜.K˜Kšœ!˜!Kšœ!˜!K˜š ™˜$KšœD˜D—˜%KšœF˜F—˜%KšœD˜D—˜%Kšœ@˜@—KšœN˜NKšœN˜NKšœK˜KK˜)šŸœ ŸœŸ˜K˜K˜K˜)KšŸœ˜——K˜šœ ™ ˜%KšœC˜C—˜%KšœC˜C—˜%KšœA˜A—˜%KšœK˜K—˜%KšœM˜M—˜%KšœM˜M—K˜2K˜—šœ ™ ˜%KšœR˜R—˜%KšœO˜O—˜%KšœQ˜Q—˜%KšœM˜M—K˜2K˜,K˜—š ™˜!KšœD˜D—˜"KšœF˜F—˜"KšœD˜D—˜"Kšœ@˜@—˜!Kšœ)‘˜7—KšœK˜KKšœK˜KKšœH˜HK˜)šŸœ ŸœŸ˜K˜K˜K˜&KšŸœ˜——K™šœ ™ K˜.K˜—šœ ™ K˜.K˜)—K˜š™˜"KšœD˜D—˜#KšœF˜F—KšœL˜LKšœL˜LKšœI˜IK˜)šŸœ ŸœŸ˜K˜K˜K˜'KšŸœ˜——K™šœ ™ K˜/—K˜šœ ™ Kšœ/˜/Kšœ*˜*—K˜š™˜$KšœF˜F—KšœM˜MKšœM˜MKšœJ˜JK˜)šŸœ ŸœŸ˜K˜K˜K˜(KšŸœ˜——K™šœ ™ Kšœ0˜0K˜—šœ ™ Kšœ0˜0K˜+—K˜K˜!K˜K˜K˜K˜%K˜%Kšœ˜K˜—šœ$˜6K˜PK˜TK˜OKšœMŸœ˜S˜KšœB˜B—K˜—K™—šœ2™2Kšœ ™ K˜Kšœ7˜7Kšœ[˜[K˜—K™KšŸœ˜K˜—…—ά€%†