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. €WalnutMsgSetDisplayerImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Willie-Sue, October 14, 1986 6:30:51 pm PDT Donahue, May 9, 1986 10:39:11 am PDT Contents: Implementation of the WalnutMsgSet displayers. Last edit by: Donahue, December 13, 1984 11:42:00 am PST (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) Types 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 msg has been automatically removed from Deleted, so bump its version number Finally, make sure the displaying process is done now update display * * * * * * * * * * * * * * * * * * * * * * after 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 used by selection Κ˜šΟn™Icodešœ Οmœ1™˜>—šœŸœŸœ˜JšœA˜A—šœŸœŸœ˜Jšœ>˜>—šœŸœŸœ˜Jšœ>Ÿœ˜D—J˜JšœŸœ Οc˜0—™ JšœŸœ,˜>—˜š  œŸœŸœ Ÿœ Ÿœ˜?JšœC™CJšœ Ÿœ,˜;JšŸœ ŸœŸœA˜UJšœ˜J˜—š œŸœŸœ)Ÿœ Ÿœ˜XJšœŸœ˜šŸœŸœŸœ˜Jš ŸœŸœŸœŸœŸœ‘˜0Jšœ˜—šŸœŸœŸ˜JšŸœŸœŸœ˜1—Jšœ1˜1Jšœ˜Jšœ˜—J˜šœŸœŸœ˜5Jš œŸœ ŸœŸœ!Ÿœ Ÿœ˜RJ˜"Jšœ˜š œŸœŸœŸœ˜>J˜Jšœ+Ÿœ˜MJšœ˜—J˜ šŸœ ŸœŸœŸœ˜,Jšœ‘˜(JšœOŸœ˜TJšŸœ ŸœŸœ˜Jšœ:˜:JšŸ˜Jšœ˜—JšœŸœ,˜8JšœP˜PJšŸœ ŸœŸœ˜Jšœ/˜/Jšœ/˜/Jšœ˜——˜šœŸœŸœŸœ˜HJšœ@˜@Jšœ˜J˜Jš ŸœŸœŸœŸœŸœ˜JšœŸœ˜3JšŸœŸœ˜,Jšœ˜—J˜Jš ŸœŸœŸœŸœŸœŸœ˜5šŸœŸœŸ˜šœŸœŸœ/˜MJšœŸœ)˜5JšœŸœ˜šŸœ)ŸœŸœ˜8Jšœ˜Jšœ9Ÿœ˜=Jšœ=˜=—šŸœ‘)˜0šŸœ4˜6šœ:Ÿœ˜@JšœŸœ˜JšŸœ˜ Jšœ˜—Jšœ˜——Jšœ)˜)JšœŸœ˜JšœŸœ˜JšœŸœ˜Jšœ ˜ —J˜šŸœ˜šœ˜J˜šœ/Ÿœ˜3Jšœ.ŸœŸœ˜M——JšœŸœ˜Jšœ˜J˜šœ˜Jšœ>Ÿœ˜D—˜J˜———šŸœŸœŸœ˜šœŸœŸœŸœŸ˜GJšœ˜—šœ,˜,JšœŸœ.˜O—JšœJ˜JJšœ-˜-Jšœ-˜-J˜J˜—J˜Jšœ)˜)Jšœ4˜4Jšœ7˜7Jšœ0˜0Jšœ#˜#Jšœ˜—J˜šœ Ÿœ˜J˜˜J˜J˜J˜ —˜ J™——šœ˜+JšœŸœ+˜CJšœ=˜=JšœŸœ˜Jšœ9˜9Jšœ˜——˜JšœŸœŸœ˜+šœŸœŸ˜JšœJŸœ˜P——˜šœŸœ)Ÿœ˜FJšŸœ ˜JšœŸœ˜Jšœ ŸœAŸœ˜WJ˜J˜˜šŸœ%ŸœŸ˜=šŸœ/ŸœŸ˜<šŸœ%Ÿœ ŸœŸ˜NJšŸœ0ŸœŸœ Ÿœ˜]————J˜š œŸœ˜šŸœ Ÿœ Ÿ˜šœŸœŸ˜˜*Jšœ;Ÿœ˜B—šœ™˜(JšŸœ5˜8——J˜JšŸœ ˜$—J˜—J˜Jšœ˜—J˜Jšœ#‘˜‘ ˜JJ˜@Jšœ˜——J˜J™+˜Jšœ+™+—˜šœŸœ:˜TšŸœŸœŸœ˜JšŸœŸœŸœŸœ˜>JšŸœŸœŸœŸœ1˜OJšœ!˜!JšœM˜MJ˜—Jšœ˜——˜JšœE™EšœŸœ˜ JšœHŸœ˜QJ˜Jšœ˜Jšœ+˜+J˜Jšœ˜šŸœŸ˜šœ˜JšœF˜F——J˜Jšœ˜Jšœ ™ JšŸœŸœŸœ˜$JšŸœŸœŸœ˜$Jšœ˜J˜šŸœŸœ˜Jšœ˜šŸœŸœŸ˜šœ˜JšœR˜R——J˜—JšŸœŸœ˜-JšŸœŸœ9˜MJšœ˜——˜š œŸœŸœŸœŸœ˜[š  œŸœŸœŸœŸœ˜RJšœS˜SJšœ˜šŸœŸœ"Ÿ˜=Jšœ˜—Jšœ˜—J˜Jšœ˜J˜ JšœAŸœ˜TJšœ˜——˜š œŸœŸœŸœ˜8Jšœ Ÿœ˜Jšœ Ÿœ˜JšœŸœ˜JšœI˜IJšŸœ<˜BJ˜——˜š œŸœŸœŸœ ŸœŸœŸœ˜tJšœŸœŸœŸœ˜6Jš œŸœŸœŸœŸœ˜#Jšœ ŸœŸœ$˜HJšœŸœŸœŸœ ˜KJ˜——˜šœŸœ'˜?Jš œ ŸœŸœŸœŸœ˜QJšœŸœŸœŸœ˜6šœŸœ˜5Jšœ˜Jšœ Ÿœ™Jšœ˜Jšœ˜—J˜šœ*˜*Jšœ$˜$Jšœ˜Kš œŸœŸœŸœ#Ÿœ˜eJšœ˜Jšœ˜JšœŸ˜Jšœ˜—J˜JšŸœŸœŸœ˜1Jšœ˜Jšœ˜——J˜Jšœ Ÿœ$˜3˜šœ"˜5JšœŸœ ˜%JšŸœ ŸœŸœ'˜;šŸ˜šœŸœ ˜.šŸœŸœŸ˜%Jšœ,Ÿœ˜5—Jšœ˜šŸœ Ÿœ;˜KJšŸœŸœŸœ˜6——J˜—J˜——˜š œŸœ Ÿœ˜<šŸœŸœŸœ˜JšœT˜TJšŸœŸœ˜4J˜—Jšœ˜——˜šœŸœŸœŸœ˜OJšŸœŸœŸœ'˜BšŸ˜šœŸœ2˜LJšœ/˜/JšŸœŸœŸœŸœ˜(—J™šœ™š ŸœŸœŸœŸœŸ˜9šœ˜Jšœ˜Jšœ˜Jšœ˜——J˜JšœR˜RJšœ˜—J˜—JšŸœŸœ˜Jšœ˜——˜š œŸœ˜/Jšœ™JšœŸœ2˜JJšœ*˜*JšœŸœ˜šŸœŸœ˜J˜0KšŸœ ŸœŸœ˜)Kšœ}˜}K˜K˜KšŸœ ŸœŸœŸœ˜N—˜JšœO˜O—Jšœ˜——J˜J˜JšŸœ˜J˜—…—bŠƒΎ