<> <> <> <> <<>> <> <> <> <<(Added Nut connection)>> <<(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)>> DIRECTORY Containers USING [ChildXBound, ChildYBound], IO, Menus USING [MouseButton, Menu, MenuProc], Nut USING[SetNutInfo], Process USING [Pause, MsecToTicks], Rope, TBQueue USING [CreateTiogaButton], ThisMachine USING [Address], TiogaButtons USING [ TiogaButton, TiogaButtonProc, CreateViewer, ChangeButtonLooks, DeleteButton, SetStyleFromRope], TiogaOps USING [InsertRope, SaveSelA, SetSelection, Delete, RestoreSelA, CancelSelection, GetSelection, InsertChar], UserProfile USING [Boolean, Token], ViewerClasses USING [Column, Viewer], ViewerEvents USING [EventProc, EventRegistration, RegisterEventProc, UnRegisterEventProc], ViewerOps USING [AddProp, ComputeColumn, CreateViewer, DestroyViewer, FetchProp, GrowViewer, OpenIcon, PaintViewer, SetMenu], ViewerSpecs USING [bwScreenHeight, openLeftWidth, openRightWidth], ViewerTools USING [TiogaContents, TiogaContentsRec, GetSelectedViewer, GetSelectionContents, GetTiogaContents], WalnutParseMsg USING [ParseProc, MsgHeaders, ParseHeadersFromRope], WalnutDefs USING [dontCareMsgSetVersion, Error], WalnutOps USING [MsgSet, ServerInfo, ActiveMsgSetName, DeletedMsgSetName, AcceptNewMail, AddMsg, CreateMsg, EnumerateMsgsInSet, GetDisplayProps, GetNewMail, MoveMsg, MsgSetExists, RemoveMsg, SetHasBeenRead], WalnutDisplayerInternal USING [ DisplayMsgFromMsgSet, MsgCategories], WalnutMsgSetDisplayerPrivate USING [ MsgSetInfo, MsgSetInfoRec, MsgSetFieldHandle, MsgSetFieldHandleRec, MsgInfo, MsgInfoRec, activeMenu, buildingMenu, deletedMenu, displayerMenu, readOnlyMenu], WalnutPrintOps USING [PrintMsgList], WalnutWindowInternal USING [MsgSetButton, activeMsgSetButton, deletedMsgSetButton, initialActiveIconic, initialActiveOpen, initialActiveRight, msgSetIcon, msgSetNamePrefix, personalMailDB, readOnlyAccess, msgSetsVersion, walnutQueue, GetButton, GetSelectedMsgSets, Report, ReportRope, RetrieveNewMail]; WalnutMsgSetDisplayerImpl: CEDAR PROGRAM IMPORTS IO, Nut, Process, Rope, UserProfile, WalnutParseMsg, WalnutDefs, WalnutOps, WalnutDisplayerInternal, WalnutMsgSetDisplayerPrivate, WalnutPrintOps, WalnutWindowInternal, Containers, TBQueue, ThisMachine, TiogaButtons, TiogaOps, ViewerEvents, ViewerOps, ViewerSpecs, ViewerTools EXPORTS WalnutDisplayerInternal, WalnutMsgSetDisplayerPrivate, WalnutPrintOps = BEGIN OPEN WalnutMsgSetDisplayerPrivate; <> TiogaButton: TYPE = TiogaButtons.TiogaButton; Viewer: TYPE = ViewerClasses.Viewer; ROPE: TYPE = Rope.ROPE; MsgSet: TYPE = WalnutOps.MsgSet; MsgSetButton: TYPE = WalnutWindowInternal.MsgSetButton; MsgSetInfo: TYPE = WalnutMsgSetDisplayerPrivate.MsgSetInfo; MsgSetFieldHandle: TYPE = WalnutMsgSetDisplayerPrivate.MsgSetFieldHandle; MsgInfo: TYPE = WalnutMsgSetDisplayerPrivate.MsgInfo; tocDefaultLooks: PUBLIC ROPE _ UserProfile.Token[key: "Walnut.TOCDefaultLooks", default: ""]; tocSelectedLooks: PUBLIC ROPE _ UserProfile.Token[key: "Walnut.TOCSelectedLooks", default: "sb"]; tocUnreadLooks: PUBLIC ROPE _ UserProfile.Token[key: "Walnut.TOCUnreadLooks", default: "i"]; userWantsQMs: PUBLIC BOOL _ UserProfile.Boolean[key: "Walnut.ShowUnreadWithQMs", default: TRUE]; scrollWaitTime: CARDINAL _ 500; -- Milliseconds <> destroyedMsg: ROPE = "Selected msg viewer has been destroyed"; GetMsgSetName: PUBLIC PROC[v: Viewer] RETURNS[msName: ROPE] = { <> msName _ NARROW[ViewerOps.FetchProp[v, $WalnutMsgSetName]]; IF msName = NIL THEN WalnutWindowInternal.Report[" Not a Walnut Message Set viewer"]; }; QDisplayMsgSet: PUBLIC PROC[msb: MsgSetButton, oldV: Viewer, shift: BOOL, 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[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; msI: MsgSetInfo; NewMsgProc: PROC[msg, TOCentry: ROPE, startOfSubject: INT] = { numNew _ numNew + 1; BuildMsgLineViewer[msI, CreateMsgInfo[msg, FALSE, TOCentry, startOfSubject]]; }; numNew _ 0; IF activeV = NIL OR activeV.destroyed THEN { numNew _ -1; -- don't know, won't count [responses, complete] _ WalnutOps.GetNewMail[WalnutDefs.dontCareMsgSetVersion, NIL]; IF ~complete THEN RETURN; WalnutOps.AcceptNewMail[WalnutDefs.dontCareMsgSetVersion]; RETURN }; msI _ NARROW[ViewerOps.FetchProp[activeV, $MsgSetInfo]]; [responses, complete] _ WalnutOps.GetNewMail[active.msgSet.version, NewMsgProc]; IF ~complete THEN RETURN; WalnutOps.AcceptNewMail[active.msgSet.version]; active.msgSet.version _ active.msgSet.version+1 }; RemoveMsgFromMsgSetDisplayer: PUBLIC PROC[msg: ROPE, msgSet: MsgSet] = { msb: MsgSetButton = WalnutWindowInternal.GetButton[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, msb = WalnutWindowInternal.deletedMsgSetButton]; }; MsgSetNamePrefix: PUBLIC PROC RETURNS[ROPE] = { RETURN[WalnutWindowInternal.msgSetNamePrefix] }; TryToDisplayMsg: PROC[mfH: MsgSetFieldHandle] = { DisplayOneMsg[mfH, FALSE ! WalnutDefs.Error => CONTINUE] }; MoveToProc: PUBLIC Menus.MenuProc = { msViewer: Viewer = NARROW[parent]; msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; selected: MsgSetFieldHandle; displayProcess: PROCESS; IF msI = NIL THEN RETURN; selected _ msI.selected; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be moved"]; RETURN}; IF selected.tocButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; BEGIN thisMsgSetB: MsgSetButton = msI.button; thisName: ROPE = thisMsgSetB.msgSet.name; msgSetList: LIST OF MsgSetButton = WalnutWindowInternal.GetSelectedMsgSets[]; first: BOOL _ TRUE; moveToSelf: BOOL _ FALSE; msg: ROPE = selected.msgInfo.msg; fromDeleted: BOOL = msI.button = WalnutWindowInternal.deletedMsgSetButton; IF msgSetList = NIL THEN { WalnutWindowInternal.Report[" No selected MsgSets to Move msg to"]; RETURN}; <> IF mouseButton#red THEN displayProcess _ FORK TryToDisplayMsg[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[msg, thisMsgSetB.msgSet, msL.first.msgSet].exists THEN { IF msL.first.msViewer # NIL THEN AddToDisplayedMsgSet[msL.first, selected.msgInfo, NIL]; IF fromDeleted AND first THEN { <> first _ FALSE; thisMsgSetB.msgSet.version _ thisMsgSetB.msgSet.version + 1; }; }; ENDLOOP; IF ~fromDeleted THEN IF NOT moveToSelf THEN [] _ WalnutOps.RemoveMsg[ msg, thisMsgSetB.msgSet, MsgSetVersion[WalnutWindowInternal.deletedMsgSetButton] ]; first _ TRUE; FOR msL: LIST OF MsgSetButton _ msgSetList, msL.rest UNTIL msL=NIL DO IF first THEN { ReportDisposition[selected.msgInfo, "Moved to:"]; first _ FALSE} ELSE WalnutWindowInternal.ReportRope[","]; WalnutWindowInternal.ReportRope[" "]; WalnutWindowInternal. ReportRope[msL.first.msgSet.name]; ENDLOOP; WalnutWindowInternal.ReportRope["\n"]; IF ~moveToSelf OR fromDeleted THEN RemoveFromDisplayedMsgSet[msI, msViewer, selected, fromDeleted] ELSE [] _ AdvanceSelection[selected]; <> IF displayProcess # NIL THEN TRUSTED { [] _ JOIN displayProcess } END; }; 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] }; NewMailProc: PUBLIC PROC[viewer: Viewer] = { oldM: Menus.Menu = viewer.menu; ViewerOps.SetMenu[viewer, buildingMenu]; BEGIN ENABLE UNWIND => ViewerOps.SetMenu[viewer, oldM]; [] _ WalnutWindowInternal.RetrieveNewMail[]; ViewerOps.SetMenu[viewer, oldM]; END; }; CategoriesProc: PUBLIC PROC[viewer: Viewer] = { msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[viewer, $MsgSetInfo]]; selected: MsgSetFieldHandle; IF msI = NIL THEN RETURN; selected _ msI.selected; IF selected = NIL THEN {WalnutWindowInternal.Report[" No selected msg"]; RETURN}; IF selected.tocButton = NIL THEN {WalnutWindowInternal.Report[destroyedMsg]; RETURN}; WalnutDisplayerInternal.MsgCategories[selected.msgInfo.msg]; }; DeleteProc: PUBLIC Menus.MenuProc = { msViewer: Viewer = NARROW[parent]; msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; displayProcess: PROCESS; selected: MsgSetFieldHandle; IF msI = NIL THEN RETURN; selected _ msI.selected; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be deleted"]; RETURN}; IF selected.tocButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; BEGIN thisMsgSet: MsgSet = msI.button.msgSet; nowInDeleted: BOOL; IF thisMsgSet.name.Equal[WalnutOps.DeletedMsgSetName] THEN RETURN; -- already deleted IF mouseButton#red THEN displayProcess _ FORK TryToDisplayMsg[selected.next]; nowInDeleted _ WalnutOps.RemoveMsg[ selected.msgInfo.msg, thisMsgSet, MsgSetVersion[WalnutWindowInternal.deletedMsgSetButton] ]; RemoveFromDisplayedMsgSet[msI, msViewer, selected, FALSE]; ReportDisposition[selected.msgInfo, "Deleted from "]; WalnutWindowInternal.Report[thisMsgSet.name]; IF nowInDeleted THEN AddToDisplayedMsgSet[WalnutWindowInternal.deletedMsgSetButton, selected.msgInfo, NIL]; IF displayProcess # NIL THEN TRUSTED { [] _ JOIN displayProcess } END; }; AddProc: PUBLIC Menus.MenuProc = { msViewer: Viewer = NARROW[parent]; msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; selected: MsgSetFieldHandle; displayProcess: PROCESS; IF msI = NIL THEN RETURN; selected _ msI.selected; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be moved"]; RETURN}; IF selected.tocButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; BEGIN thisMsgSet: MsgSet = msI.button.msgSet; msgSetList: LIST OF MsgSetButton = WalnutWindowInternal.GetSelectedMsgSets[]; msg: ROPE = selected.msgInfo.msg; isDeleted: BOOL = thisMsgSet.name.Equal[WalnutOps.DeletedMsgSetName, FALSE]; noLongerInDeleted: BOOL _ FALSE; first: BOOL _ TRUE; addedTo: LIST OF MsgSetButton; IF msgSetList = NIL THEN { WalnutWindowInternal.Report[" No selected MsgSets to Add msg to"]; RETURN}; IF mouseButton#red THEN displayProcess _ FORK TryToDisplayMsg[selected.next]; FOR msL: LIST OF MsgSetButton _ msgSetList, msL.rest UNTIL msL=NIL DO exists, undeleted: BOOL; msgSet: MsgSet; exists _ WalnutOps.AddMsg[msg, thisMsgSet, msgSet _ msL.first.msgSet]; noLongerInDeleted _ noLongerInDeleted OR undeleted; IF ~exists THEN BEGIN IF first THEN { ReportDisposition[selected.msgInfo, "Added to: "]; first _ FALSE} ELSE WalnutWindowInternal.ReportRope[", "]; WalnutWindowInternal.ReportRope[msgSet.name]; addedTo _ CONS[msL.first, addedTo]; END; ENDLOOP; WalnutWindowInternal.ReportRope["\n"]; <<>> <> FOR msL: LIST OF MsgSetButton _ addedTo, msL.rest UNTIL msL=NIL DO AddToDisplayedMsgSet[msL.first, selected.msgInfo, NIL]; ENDLOOP; IF mouseButton # red THEN [] _ AdvanceSelection[selected]; IF noLongerInDeleted THEN RemoveMsgFromMsgSetDisplayer[msg, WalnutWindowInternal.deletedMsgSetButton.msgSet]; IF displayProcess # NIL THEN TRUSTED { [] _ JOIN displayProcess } END; }; AdvanceSelection: PROC[msfH: MsgSetFieldHandle] RETURNS[next: MsgSetFieldHandle] = { IF msfH = NIL THEN RETURN[NIL]; next _ msfH.next; IF next = NIL THEN { WalnutWindowInternal.Report[" No Next message"]; RETURN }; [] _ SelectMsgInMsgSet[next] }; DisplayProc: PUBLIC Menus.MenuProc = { msViewer: Viewer = NARROW[parent]; msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; selected: MsgSetFieldHandle; IF msI = NIL THEN RETURN; selected _ msI.selected; IF mouseButton # red THEN selected _ AdvanceSelection[selected]; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be displayed"]; RETURN}; IF selected.tocButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; DisplayOneMsg[selected, shift] }; PrintSelectedProc: PUBLIC PROC[viewer: Viewer, usePress: BOOL] = { PrintSel[msViewer: viewer, usePress: usePress] }; PrintSel: PROC[ msViewer: Viewer, server: ROPE _ NIL, usePress: BOOL _ TRUE , copies: INT _ 1] = { msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; selected: MsgSetFieldHandle; IF msI = NIL THEN RETURN; selected _ msI.selected; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be printed"]; RETURN}; IF selected.tocButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; [] _ WalnutPrintOps.PrintMsgList[ LIST[selected.msgInfo.msg], msViewer, server, usePress, copies]; }; PrintSelCmd: PUBLIC PROC[msgSet: ROPE, server: ROPE, usePress: BOOL, copies: INT] = { msb: MsgSetButton = WalnutWindowInternal.GetButton[msgSet]; IF msb = NIL THEN RETURN; IF msb.msViewer = NIL THEN RETURN; PrintSel[msb.msViewer, server, usePress, copies]; }; AppendMsgProc: PUBLIC PROC[msViewer: Viewer] = { curSel: ROPE = ViewerTools.GetSelectionContents[]; curV: Viewer = ViewerTools.GetSelectedViewer[]; msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; tc: ViewerTools.TiogaContents; from, sender, gvID, me, TOCentry: ROPE; date: ROPE = IO.PutFR[NIL, IO.time[]]; startOfSubject: INT; hasBeenRead: BOOL; msgInfo: MsgInfo; mh: WalnutParseMsg.MsgHeaders; active: MsgSetButton _ WalnutWindowInternal.activeMsgSetButton; WantThisField: WalnutParseMsg.ParseProc = { SELECT TRUE FROM fieldName.Equal["From", FALSE] => RETURN[TRUE, TRUE]; fieldName.Equal["Sender", FALSE] => RETURN[TRUE, TRUE]; ENDCASE => RETURN[FALSE, TRUE]; }; IF msI = NIL THEN RETURN; IF curSel.Length[] < 2 THEN tc _ ViewerTools.GetTiogaContents[curV] ELSE { tc _ NEW[ViewerTools.TiogaContentsRec]; tc.contents _ curSel; }; mh _ WalnutParseMsg.ParseHeadersFromRope[tc.contents, WantThisField]; FOR mhL: WalnutParseMsg.MsgHeaders _ mh, mhL.rest UNTIL mhL=NIL DO fieldName: ROPE = mhL.first.fieldName; SELECT TRUE FROM fieldName.Equal["Sender", FALSE] => sender _ mhL.first.value; fieldName.Equal["From", FALSE] => from _ mhL.first.value; ENDCASE => NULL; ENDLOOP; IF sender = NIL THEN sender _ from; IF sender = NIL THEN sender _ "UnknownSender"; me _ ThisMachine.Address[]; gvID _ IO.PutFR["%g $ %g@%g", IO.rope[sender], IO.rope[me.Substr[len: me.Length[] - 1] ], IO.rope[date] ]; WalnutOps.CreateMsg[gvID, tc]; [hasBeenRead, TOCentry, startOfSubject] _ WalnutOps.GetDisplayProps[gvID]; msgInfo _ CreateMsgInfo[gvID, hasBeenRead, TOCentry, startOfSubject]; IF msI.button # active THEN { active.msgSet.version _ active.msgSet.version + 1; [] _ WalnutOps.MoveMsg[msg: gvID, from: active.msgSet, to: msI.button.msgSet]; active.msgSet.version _ active.msgSet.version + 1; }; AddToDisplayedMsgSet[msI.button, msgInfo, msI] }; ReportDisposition: PROC[msgInfo: MsgInfo, r: ROPE] = { msgSubject: ROPE _ Rope.Substr[msgInfo.tocName, msgInfo.startOfSubject]; IF msgSubject.Length[] > 24 THEN msgSubject _ Rope.Concat[msgSubject.Substr[0, 21], " ..."]; WalnutWindowInternal.ReportRope["Msg: ", msgSubject]; WalnutWindowInternal.ReportRope[": has been ", r]; }; <<* * * * * * * * * * * * * * * * * * * * * *>> MSViewer: PROC[msb: MsgSetButton, oldV: Viewer, shift: BOOL] RETURNS[msV: Viewer] = { OPEN WalnutWindowInternal; iconic: BOOL _ FALSE; whichSide: ViewerClasses.Column _ left; msgSet: MsgSet = msb.msgSet; caption: ROPE = Rope.Concat[msgSet.name, " Messages"]; msI: MsgSetInfo; IF msgSet.name.Equal[WalnutOps.ActiveMsgSetName, FALSE] THEN { iconic _ initialActiveIconic AND initialActiveOpen; IF initialActiveRight THEN whichSide _ right }; IF oldV # NIL THEN IF oldV.destroyed THEN oldV _ NIL; IF oldV # NIL THEN { oldMsgSetName: ROPE = NARROW[ViewerOps.FetchProp[oldV, $WalnutMsgSetName]]; msI _ NARROW[ViewerOps.FetchProp[oldV, $MsgSetInfo]]; oldV.inhibitDestroy _ TRUE; IF ~Rope.Equal[msgSet.name, oldMsgSetName, FALSE] THEN { oldV.name _ caption; WalnutWindowInternal.GetButton[oldMsgSetName].msViewer _ NIL; WalnutWindowInternal.GetButton[msgSet.name].msViewer _ oldV } ELSE { -- check the version to see if all's well IF msI.button.msgSet.version = WalnutOps.MsgSetExists[ msgSet.name, WalnutWindowInternal.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]]; msI _ NEW[MsgSetInfoRec]; msI.button _ msb; msI.container _ msV; msI.destroyER _ ViewerEvents.RegisterEventProc[DestroyMSViewer, destroy, msV, TRUE]; }; 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]; Nut.SetNutInfo[msV, $Walnut, "MsgSet", msgSet.name]; ViewerOps.AddProp[msV, $WalnutMsgSetName, msgSet.name]; ViewerOps.AddProp[msV, $IconLabel, msgSet.name]; MsgSetInViewer[msgSet, msI, shift]; }; 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[msgSet: MsgSet, msI: MsgSetInfo, shift: BOOL] = { OPEN ViewerOps; firstUnread, last: INT; autoScroll: BOOL = UserProfile.Boolean[key: "Walnut.AutoScrollMsgSets", default: TRUE]; v: Viewer = msI.tiogaViewer; parent: Viewer = v.parent; menu: Menus.Menu _ IF WalnutWindowInternal.readOnlyAccess THEN readOnlyMenu ELSE IF msgSet.name.Equal[WalnutOps.ActiveMsgSetName, FALSE] THEN IF WalnutWindowInternal.personalMailDB THEN activeMenu ELSE displayerMenu ELSE IF msgSet.name.Equal[WalnutOps.DeletedMsgSetName, FALSE] THEN deletedMenu ELSE 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]; SetMenu[parent, buildingMenu]; [firstUnread, last] _ FillInMsgSetWindow[msI, msgSet.name]; 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]; }; 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, fromDeleted: BOOL] = { prev, next: MsgSetFieldHandle; bt: TiogaButton; selected: MsgSetFieldHandle _ msI.selected; bt _ mfH.tocButton; IF selected = mfH THEN TiogaButtons.ChangeButtonLooks[ button: bt, addLooks: tocDefaultLooks, removeLooks: tocSelectedLooks]; prev _ mfH.prev; next _ mfH.next; <> IF prev # NIL THEN prev.next _ next; IF next # NIL THEN next.prev _ prev; TiogaButtons.DeleteButton[bt]; IF selected = mfH THEN { msI.selected _ next; IF next # NIL THEN TiogaButtons.ChangeButtonLooks[ button: next.tocButton, addLooks: tocSelectedLooks, removeLooks: tocDefaultLooks]; }; IF msI.lastMFH = mfH THEN msI.lastMFH _ prev; IF ~fromDeleted THEN msI.button.msgSet.version _ msI.button.msgSet.version+1; }; FillInMsgSetWindow: PROC[msI: MsgSetInfo, msgSet: ROPE] RETURNS[firstUnread, last: INT] = { MsgInfoProc: PROC[msg, TOCentry: ROPE, hasBeenRead: BOOL, startOfSubject: INT] = { BuildMsgLineViewer[msI, CreateMsgInfo[msg, hasBeenRead, TOCentry, startOfSubject]]; last _ last + 1; IF firstUnread = -1 AND ~msI.lastMFH.msgInfo.hasBeenRead THEN firstUnread _ last; }; firstUnread _ -1; last _ 0; msI.button.msgSet.version _ WalnutOps.EnumerateMsgsInSet[msgSet, TRUE, MsgInfoProc]; }; DoMsgInfo: PROC[msg: ROPE] RETURNS[msgInfo: MsgInfo] = { tocEntry: ROPE; hasBeenRead: BOOL; startOfSubject: INT; [hasBeenRead, tocEntry, startOfSubject] _ WalnutOps.GetDisplayProps[msg]; RETURN[CreateMsgInfo[msg, hasBeenRead, tocEntry, startOfSubject]]; }; CreateMsgInfo: PROC[msg: ROPE, hasBeenRead: BOOL, tocEntry: ROPE, startOfSubject: INT] RETURNS[msgInfo: MsgInfo] = { needsQ: BOOL _ userWantsQMs OR (tocUnreadLooks = NIL); sos: INT _ IF needsQ THEN 1 ELSE 0; msgInfo _ NEW[MsgInfoRec _ [msg, NIL, startOfSubject+sos, hasBeenRead]]; msgInfo.tocName _ IF needsQ THEN Rope.Concat["\t", tocEntry] ELSE tocEntry; }; BuildMsgLineViewer: PROC[msI: MsgSetInfo, msgInfo: MsgInfo] = { tocLooks: ROPE _ IF msgInfo.hasBeenRead THEN tocDefaultLooks ELSE tocUnreadLooks; needsQ: BOOL _ userWantsQMs OR (tocUnreadLooks = NIL); mfh: MsgSetFieldHandle _ NEW[MsgSetFieldHandleRec _ [ prev: msI.lastMFH, <> container: msI.container, msgInfo: msgInfo]]; mfh.tocButton _ TBQueue.CreateTiogaButton[ q: WalnutWindowInternal.walnutQueue, viewer: msI.tiogaViewer, rope: IF ~msgInfo.hasBeenRead AND needsQ THEN Rope.Concat["?", msgInfo.tocName] ELSE msgInfo.tocName, format: "header", looks: tocLooks, proc: MsgSetSelectionProc, clientData: mfh]; IF msI.lastMFH # NIL THEN msI.lastMFH.next _ mfh; msI.lastMFH _ mfh; }; noMsgRope: ROPE = "Msg is no longer in the MsgSet"; MsgSetSelectionProc: TiogaButtons.TiogaButtonProc = { button: TiogaButton = NARROW[parent]; IF button = NIL THEN WalnutWindowInternal.Report[noMsgRope] ELSE { mfH: MsgSetFieldHandle = NARROW[clientData]; IF (mouseButton = red) AND shift THEN { TiogaOps.InsertRope[mfH.msgInfo.tocName]; RETURN }; [] _ SelectMsgInMsgSet[mfH]; IF control THEN DeleteProc[parent: mfH.container, mouseButton: mouseButton] ELSE IF mouseButton#red THEN DisplayOneMsg[mfH, shift] }; }; DisplayOneMsg: PROC[mfH: MsgSetFieldHandle, shift: BOOL] = { IF mfH # NIL THEN { WalnutDisplayerInternal.DisplayMsgFromMsgSet[mfH.msgInfo.msg, mfH.container, shift]; IF ~mfH.msgInfo.hasBeenRead THEN MarkMsgAsRead[mfH]; }; }; SelectMsgInMsgSet: PROC[mfH: MsgSetFieldHandle] RETURNS[sameAsBefore: BOOL] = { IF mfH.tocButton = NIL THEN WalnutWindowInternal.Report[noMsgRope] ELSE { msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[mfH.container, $MsgSetInfo]]; prevSelected: MsgSetFieldHandle = msI.selected; IF prevSelected = mfH THEN RETURN[TRUE]; <<>> <> 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 _ mfH; }; RETURN[FALSE]; }; MarkMsgAsRead: PROC[mfH: MsgSetFieldHandle] = { <> msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[mfH.container, $MsgSetInfo]]; WalnutOps.SetHasBeenRead[mfH.msgInfo.msg]; mfH.msgInfo.hasBeenRead _ TRUE; IF userWantsQMs THEN { viewer: Viewer = TiogaOps.GetSelection[].viewer; IF viewer # NIL THEN TiogaOps.SaveSelA[]; 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.ChangeButtonLooks[ button: mfH.tocButton, addLooks: tocDefaultLooks, removeLooks: tocUnreadLooks]; }; END.