DIRECTORY Containers USING [ChildXBound, ChildYBound], Menus USING [MouseButton, Menu, MenuProc], Nut USING[SetNutInfo], Rope, TBQueue USING [AppendToTiogaButton, CreateTiogaButton], TiogaButtons USING [ TiogaButton, TiogaButtonProc, CreateViewer, ChangeButtonLooks, DeleteButton, SetStyleFromRope], TiogaOps USING [InsertRope], 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], WalnutDefs USING [dontCareMsgSetVersion, Error], WalnutOps USING [MsgSet, ServerInfo, ActiveMsgSetName, DeletedMsgSetName, AcceptNewMail, AddMsg, EnumerateMsgsInSet, GetDisplayProps, GetNewMail, 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, deletedMsgSetButton, initialActiveIconic, initialActiveOpen, initialActiveRight, msgSetIcon, msgSetNamePrefix, personalMailDB, readOnlyAccess, msgSetsVersion, walnutQueue, GetButton, GetSelectedMsgSets, Report, ReportRope, RetrieveNewMail]; WalnutMsgSetDisplayerImpl: CEDAR PROGRAM IMPORTS Nut, Rope, UserProfile, WalnutDefs, WalnutOps, WalnutDisplayerInternal, WalnutMsgSetDisplayerPrivate, WalnutPrintOps, WalnutWindowInternal, Containers, TBQueue, TiogaButtons, TiogaOps, ViewerEvents, ViewerOps, ViewerSpecs EXPORTS WalnutDisplayerInternal, WalnutMsgSetDisplayerPrivate = 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]; 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 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]; }; 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 = msI.selected; displayProcess: PROCESS; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be moved"]; RETURN}; IF selected.iButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; BEGIN thisMsgSet: MsgSet = msI.button.msgSet; thisName: ROPE = thisMsgSet.name; msgSetList: LIST OF MsgSetButton = WalnutWindowInternal.GetSelectedMsgSets[]; first: BOOL _ TRUE; moveToSelf: BOOL _ FALSE; msg: ROPE = selected.msgInfo.msg; 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, thisMsgSet, msL.first.msgSet].exists THEN IF msL.first.msViewer # NIL THEN AddToDisplayedMsgSet[msL.first, selected.msgInfo, NIL] ENDLOOP; IF ~msI.button = WalnutWindowInternal.deletedMsgSetButton THEN IF NOT moveToSelf THEN [] _ WalnutOps.RemoveMsg[ msg, thisMsgSet, MsgSetVersion[WalnutWindowInternal.deletedMsgSetButton] ]; 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 THEN RemoveFromDisplayedMsgSet[msI, msViewer, selected] 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 Menus.MenuProc = { v: Viewer = NARROW[parent]; oldM: Menus.Menu = v.menu; ViewerOps.SetMenu[v, buildingMenu]; BEGIN ENABLE UNWIND => {v.menu _ oldM; ViewerOps.PaintViewer[v, menu]}; [] _ WalnutWindowInternal.RetrieveNewMail[]; v.menu _ oldM; ViewerOps.PaintViewer[v, menu]; END; }; CategoriesProc: PUBLIC Menus.MenuProc = { msViewer: Viewer = NARROW[parent]; msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; selected: MsgSetFieldHandle = msI.selected; IF selected = NIL THEN {WalnutWindowInternal.Report[" No selected msg"]; RETURN}; IF selected.iButton = 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]]; selected: MsgSetFieldHandle = msI.selected; displayProcess: PROCESS; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be deleted"]; RETURN}; IF selected.iButton = 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]; 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 = msI.selected; displayProcess: PROCESS; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be moved"]; RETURN}; IF selected.iButton = 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 _ msI.selected; IF mouseButton # red THEN selected _ AdvanceSelection[selected]; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be displayed"]; RETURN}; IF selected.iButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; DisplayOneMsg[selected, shift] }; PrintSelectedProc: PUBLIC Menus.MenuProc = { msViewer: Viewer = NARROW[parent]; msI: MsgSetInfo = NARROW[ViewerOps.FetchProp[msViewer, $MsgSetInfo]]; selected: MsgSetFieldHandle = msI.selected; IF selected = NIL THEN { WalnutWindowInternal.Report[" No selected msg to be printed"]; RETURN}; IF selected.iButton = NIL THEN { WalnutWindowInternal.Report[destroyedMsg]; RETURN}; [] _ WalnutPrintOps.PrintMsgList[LIST[selected.msgInfo.msg], msViewer]; }; 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; [top, bottom] _ viewer.class.scroll[viewer, query, 0]; IF bottom >= 95 OR last <=0 THEN RETURN; howMuch _ IF firstUnread <= 0 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] = { prev, next: MsgSetFieldHandle; bt: TiogaButton; selected: MsgSetFieldHandle _ msI.selected; bt _ mfH.iButton; 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.iButton, addLooks: tocSelectedLooks, removeLooks: tocDefaultLooks]; }; IF msI.lastMFH = mfH THEN msI.lastMFH _ prev; 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.iButton _ TBQueue.CreateTiogaButton[ q: WalnutWindowInternal.walnutQueue, viewer: msI.tiogaViewer, rope: "", format: "header", proc: NIL]; IF ~msgInfo.hasBeenRead AND needsQ THEN mfh.hbrButton _ TBQueue.AppendToTiogaButton[ q: WalnutWindowInternal.walnutQueue, button: mfh.iButton, looks: tocLooks, rope: "?", proc: MsgSetSelectionProc, clientData: mfh]; mfh.tocButton _ TBQueue.AppendToTiogaButton[ q: WalnutWindowInternal.walnutQueue, button: IF mfh.hbrButton#NIL THEN mfh.hbrButton ELSE mfh.iButton, looks: tocLooks, rope: msgInfo.tocName, 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.iButton = 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.iButton#NIL THEN TiogaButtons.ChangeButtonLooks[ button: prevSelected.iButton, addLooks: tocDefaultLooks, removeLooks: tocSelectedLooks]; TiogaButtons.ChangeButtonLooks[button: mfH.iButton, addLooks: tocSelectedLooks]; msI.selected _ mfH; }; RETURN[FALSE]; }; MarkMsgAsRead: PROC[mfH: MsgSetFieldHandle] = { WalnutOps.SetHasBeenRead[mfH.msgInfo.msg]; mfH.msgInfo.hasBeenRead _ TRUE; IF mfH.hbrButton # NIL THEN TiogaButtons.DeleteButton[button: mfH.hbrButton]; TiogaButtons.ChangeButtonLooks[ button: mfH.tocButton, addLooks: tocDefaultLooks, removeLooks: tocUnreadLooks]; }; END. lWalnutMsgSetDisplayerImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Willie-Sue, July 17, 1985 7:39:38 pm PDT Donahue, August 6, 1985 4:54:58 pm 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 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 iButton, hbrButton, tocButton: NIL, turn off previous selection used by selection ΚΧ˜šΟn™Icodešœ Οmœ1™˜>—šœŸœŸœ˜JšœA˜A—šœŸœŸœ˜Jšœ>˜>—šœŸœŸœ˜Jšœ>Ÿœ˜D——™ JšœŸœ,˜>—˜š  œŸœŸœ Ÿœ Ÿœ˜?JšœC™CJšœ Ÿœ,˜;JšŸœ ŸœŸœA˜UJšœ˜J˜—š œŸœŸœ)Ÿœ Ÿœ˜XJšœŸœ˜šŸœŸœŸœ˜Jš ŸœŸœŸœŸœŸœΟc˜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šœŸœ1˜=Jšœ˜šŸ˜Jš ŸœŸœŸœŸœ  ˜'JšŸœŸœŸœŸœ˜/Jšœ˜JšŸœ˜—Jšœ2˜2Jšœ˜——˜šœŸ œŸœŸœ˜-JšœŸœ*˜2——˜šœŸœ˜1JšœŸœŸœ˜;—J˜š œŸœ˜%JšœŸœ ˜"JšœŸœ-˜EJšœ+˜+JšœŸœ˜šŸœ ŸœŸ˜Jšœ?Ÿœ˜G—šŸœŸœŸ˜Jšœ-Ÿœ˜5—šŸ˜Jšœ'˜'Jšœ Ÿœ˜!Jšœ ŸœŸœ:˜MJšœŸœŸœ˜Jšœ ŸœŸœ˜JšœŸœ˜!J˜šŸœŸœŸ˜JšœFŸœ˜NJ˜—Jšœ4™4JšŸœŸœŸœ ˜MJ˜š ŸœŸœŸœ%ŸœŸœŸ˜EJšŸœ'ŸœŸœŸ˜Fš ŸœŸœ=ŸœŸœŸœŸ˜iJšœ2Ÿœ˜6——JšŸœ˜J˜šŸœ8Ÿ˜>šŸœŸœ Ÿœ˜0JšœK˜KJ˜——š ŸœŸœŸœ%ŸœŸœŸ˜EšŸœŸ˜ Jšœ<Ÿœ˜BJšŸœ&˜*—J˜%J˜9JšŸœ˜—J˜&šŸœ Ÿœ3˜FJšŸœ!˜%—Jšœ1™1Jš ŸœŸœŸœŸœŸœ˜A—JšŸœ˜Jšœ˜——˜š œŸœŸœŸœ˜Fš ŸœŸœŸœŸœŸœŸ˜AJšŸœ"˜(—JšŸœŸœ˜"—J˜š œŸœ˜&Jšœ Ÿœ ˜J˜J˜#šŸœŸœŸœ4˜GJ˜,J˜J˜—JšŸœ˜Jšœ˜——˜šœŸœ˜)JšœŸœ ˜"JšœŸœ-˜EJšœ+˜+JšŸœ ŸœŸœ3Ÿœ˜QJšŸœŸœŸœ-Ÿœ˜SJ˜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˜———šŸœŸœŸœ˜šœŸœŸœ8˜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˜Jšœ˜Jšœ+˜+J˜Jšœ˜šŸœŸ˜šœ˜JšœF˜F——J˜Jšœ˜Jšœ ™ JšŸœŸœŸœ˜$JšŸœŸœŸœ˜$Jšœ˜J˜šŸœŸœ˜Jšœ˜šŸœŸœŸ˜šœ˜JšœP˜P——J˜—JšŸœŸœ˜-J˜8Jšœ˜——˜š œŸœŸœŸœŸœ˜[š  œŸœŸœŸœŸœ˜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šœ;Ÿœ˜@—šŸœŸœŸ˜'šœ,˜,Jšœ9˜9JšœH˜H——šœ,˜,Jšœ$˜$Jš œŸœŸœŸœŸœ ˜AJšœT˜T—J˜JšŸœŸœŸœ˜1Jšœ˜Jšœ˜——J˜Jšœ Ÿœ$˜3˜šœ"˜5JšœŸœ ˜%JšŸœ ŸœŸœ'˜;šŸ˜šœŸœ ˜.šŸœŸœŸ˜%Jšœ,Ÿœ˜5—Jšœ˜šŸœ Ÿœ;˜KJšŸœŸœŸœ˜6——J˜—J˜——˜š œŸœ Ÿœ˜<šŸœŸœŸœ˜JšœT˜TJšŸœŸœ˜4J˜—Jšœ˜——˜šœŸœŸœŸœ˜OJšŸœŸœŸœ'˜@šŸ˜šœŸœ2˜LJšœ/˜/JšŸœŸœŸœŸœ˜(—J™šœ™š ŸœŸœŸœŸœŸ˜7šœ˜Jšœ˜Jšœ˜Jšœ˜——J˜JšœP˜PJšœ˜—J˜—JšŸœŸœ˜Jšœ˜——˜š œŸœ˜/Jšœ™J˜*JšœŸœ˜JšŸœŸœŸœ2˜M˜JšœO˜O—Jšœ˜——J˜J˜JšŸœ˜J˜J˜—…—TΊpύ