<> <> <> <> <> <> <> <> DIRECTORY Ascii USING [ DEL, Letter ], Basics USING [BYTE, LongNumber], BasicTime USING [GMT, ToPupTime], DB USING [Aborted, Error, Failure, GetP, GetName, GetPList, V2S], GVBasics USING [Timestamp], IO, Menus, PupDefs USING [PupAddress, GetHostName], Rope, WalnutDocumentRope USING [Create], TEditSplit USING[ Split ], TiogaOps USING [CancelSelection, GetSelection, ViewerDoc], TiogaMenuOps USING[ tiogaMenu ], ViewerClasses USING [Viewer], ViewerEvents USING [ EventRegistration, EventProc, ViewerEvent, RegisterEventProc, UnRegisterEventProc ], ViewerLocks USING[ CallUnderWriteLock ], ViewerOps USING [AddProp, CreateViewer, DestroyViewer, FetchProp, GrowViewer, OpenIcon, PaintViewer, SetMenu], ViewerTools USING [TiogaContents, InhibitUserEdits, SetTiogaContents], WalnutViewer USING [CreateMenuEntry], WalnutSendOps USING [Answer, Forward, RFC822Date], WalnutDB USING [Msg, Value, mCategoryIs, mSubjectIs, mTOCEntryIs, mTOCEntryOf, DeclareMsg, GetName, TiogaMsgFromLog, V2E], WalnutVoice USING [AddNuthatchHandleToViewer], WalnutLog USING [TiogaTextFromLog], WalnutDisplayerOps, WalnutMsgOps USING [MsgSetFieldHandle], WalnutPrintOps USING [MsgPrintProc], WalnutWindow USING [msgIcon, walnutQueue, Report, ReportRope]; WalnutMsgDisplayerImpl: CEDAR MONITOR IMPORTS Ascii, BasicTime, DB, IO, PupDefs, Rope, WalnutDocumentRope, TiogaMenuOps, TiogaOps, TEditSplit, Menus, ViewerEvents, ViewerLocks, ViewerOps, ViewerTools, WalnutSendOps, WalnutDB, WalnutLog, WalnutPrintOps, WalnutViewer, WalnutVoice, WalnutWindow EXPORTS WalnutDisplayerOps, WalnutMsgOps SHARES Menus = BEGIN OPEN WalnutDB, WalnutWindow; ROPE: TYPE = Rope.ROPE; TiogaContents: TYPE = ViewerTools.TiogaContents; Viewer: TYPE = ViewerClasses.Viewer; msgMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; nilMenu: Menus.Menu _ Menus.CreateMenu[]; msgName: PUBLIC ROPE _ NIL; -- set by WalnutWindowImpl when DB changes msgMenuAvailable: PUBLIC BOOL_ FALSE; <<-------------------------->> <> <> BuildMsgViewer: PUBLIC PROC[mName: ROPE, msg: Msg, shift, paint: BOOL_ FALSE] RETURNS[v: Viewer] = { RETURN[SetUpViewer[mName: mName, msg: msg, shift: shift, paint: paint, v: NIL]]}; MsgInViewer: PUBLIC PROC[mName: ROPE, msg: Msg, v: Viewer, shift: BOOL_ FALSE] = BEGIN v_ SetUpViewer[mName: mName, msg: msg, shift: shift, paint: TRUE, v: v]; ShowMsgInMsgViewer[v, TiogaMsgFromLog[msg].contents]; END; SetUpViewer: PROC[mName: ROPE, msg: Msg, shift, paint: BOOL_ FALSE, v: Viewer] RETURNS[Viewer]= BEGIN shortName, name, date: ROPE; LockedSetMenu: PROC = {ViewerOps.SetMenu[v, msgMenu]}; [shortName, date] _ ShortMsgName[msg]; name _ Rope.Cat[ shortName, " ", date, " ", DB.V2S[DB.GetP[msg, WalnutDB.mSubjectIs]]]; name_ CheckName[name]; IF v = NIL THEN { v _ ViewerOps.CreateViewer[flavor: $Text, paint: paint, info: [name: name, icon: msgIcon, scrollable: TRUE, iconic: FALSE]]; ViewerLocks.CallUnderWriteLock[LockedSetMenu, v]; }; IF shift AND paint THEN ViewerOps.GrowViewer[v]; ViewerOps.AddProp[v, $Entity, msgName.Concat[mName]]; ViewerOps.AddProp[v, $IconLabel, Rope.Cat[StripForIcon[shortName], " .", date]]; WalnutVoice.AddNuthatchHandleToViewer[v]; RETURN[v]; END; onlyInTextViewer: ROPE = "Can only display msgs in Text viewers"; noSplitsAllowed: ROPE = "Can't display msg in a split viewer"; <<-------------------------->> DisplayMsgFromMsgSet: PUBLIC PROC[mfh: WalnutMsgOps.MsgSetFieldHandle, msViewer: Viewer, shift: BOOL_ FALSE] RETURNS[v: Viewer] = BEGIN contents: TiogaContents; shortName, name, date: ROPE; LockedSetMenu: PROC = {ViewerOps.SetMenu[v, msgMenu]}; [shortName, date] _ ShortMsgName[mfh.msg]; name _ Rope.Cat[ shortName, " ", date, " ", DB.V2S[DB.GetP[mfh.msg, WalnutDB.mSubjectIs]]]; name_ CheckName[name]; v _ NARROW[ViewerOps.FetchProp[msViewer, $LastSpawned]]; IF v # NIL AND ~v.destroyed THEN { v.name_ name; ViewerOps.AddProp[v, $IconLabel, StripForIcon[shortName]]; ViewerOps.PaintViewer[v, caption]} ELSE { v _ ViewerOps.CreateViewer[flavor: $Text, paint: ~shift, info: [name: name, icon: msgIcon, scrollable: TRUE, iconic: FALSE]]; ViewerLocks.CallUnderWriteLock[LockedSetMenu, v]; IF shift THEN ViewerOps.GrowViewer[v]; shift_ FALSE; -- so don't end up toggling the Grow ViewerOps.AddProp[v, $WhoSpawnedMe, msViewer]; ViewerOps.AddProp[msViewer, $LastSpawned, v]; ViewerOps.AddProp[v, $DestroyMsgDisplayer, ViewerEvents.RegisterEventProc[DestroyMsgDisplayer, destroy, v]]; WalnutVoice.AddNuthatchHandleToViewer[v]; }; ViewerOps.AddProp[v, $Entity, msgName.Concat[GetName[mfh.msg]]]; ViewerOps.AddProp[v, $WalnutEntity, mfh.msg]; <> ViewerOps.AddProp[v, $IconLabel, Rope.Cat[StripForIcon[shortName], " .", date]]; IF mfh.posOK THEN contents_ WalnutLog.TiogaTextFromLog[mfh.headersPos, mfh.msgLength] ELSE { hp, len: INT; [contents, hp, len]_ TiogaMsgFromLog[mfh.msg]; mfh.headersPos_ hp; mfh.msgLength_ len; mfh.posOK_ TRUE }; ShowMsgInMsgViewer[v, contents]; IF v.iconic THEN ViewerOps.OpenIcon[v, shift] ELSE IF shift THEN ViewerOps.GrowViewer[v]; END; FixUpMsgViewer: PUBLIC PROC[mName: ROPE, v: Viewer] = BEGIN entityName: ROPE; msg: Msg; IF (mName.Length[] = 0) OR v.destroyed THEN RETURN; entityName_ mName.Substr[msgName.Length[]]; msg_ DeclareMsg[entityName, OldOnly].msg; IF msg = NIL THEN { Report["Msg: ", entityName, " doesn't exist; destroying viewer"]; ViewerOps.DestroyViewer[v]; RETURN }; ShowMsgInMsgViewer[v, TiogaMsgFromLog[msg].contents]; ViewerOps.AddProp[v, $WalnutEntity, msg]; END; <<***********************************************************>> <> GetMsgName: PUBLIC PROC[v: Viewer] RETURNS[mName: ROPE] = <> BEGIN ra: REF ANY = ViewerOps.FetchProp[v, $Entity]; IF ra # NIL THEN { fullName: ROPE _ NARROW[ra]; IF fullName.Find[msgName, 0] = 0 THEN RETURN[fullName.Replace[0, msgName.Length[]]] }; Report[" Not a Walnut Msg viewer"]; END; StuffMsgContents: PUBLIC PROC[v: Viewer, mName: ROPE] RETURNS[found: BOOLEAN] = <> BEGIN ENABLE DB.Aborted, DB.Error, DB.Failure, IO.Error => GOTO notFound; msg: Msg_ DeclareMsg[mName, OldOnly].msg; IF (found_ msg#NIL) THEN { IF v.class.flavor # $Text THEN { Report[onlyInTextViewer]; RETURN[FALSE]}; IF ViewerOps.FetchProp[v, $WhoSpawnedMe] # NIL THEN { ShowMsgInMsgViewer[v, TiogaMsgFromLog[msg].contents]; RETURN}; IF v.link # NIL THEN { Report[noSplitsAllowed]; RETURN[FALSE]}; ViewerTools.SetTiogaContents[v, WalnutDB.TiogaMsgFromLog[msg].contents, FALSE] }; EXITS notFound => RETURN[FALSE]; END; AddToMsgMenu: PUBLIC PROC[label: ROPE, proc: Menus.MenuProc, onQueue: BOOL_ FALSE] = BEGIN IF onQueue THEN Menus.AppendMenuEntry[ msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, label, proc]] ELSE Menus.AppendMenuEntry[ msgMenu, Menus.CreateEntry[label, proc]]; END; <<***********************************************************>> <> MsgForwardProc: Menus.MenuProc = {self: Viewer_ NARROW[parent]; self.inhibitDestroy_ TRUE; []_ WalnutSendOps.Forward[self, self]; self.inhibitDestroy_ FALSE; }; MsgAnswerProc: Menus.MenuProc = { OPEN TiogaOps; self: Viewer_ NARROW[parent]; msgR: ROPE; self.inhibitDestroy_ TRUE; TRUSTED { msgR_ WalnutDocumentRope.Create[LOOPHOLE[ViewerDoc[self]]]}; []_ WalnutSendOps.Answer[msgR, self]; self.inhibitDestroy_ FALSE; }; MsgCategoriesProc: Menus.MenuProc = BEGIN self: Viewer_ NARROW[parent]; ra: REF ANY_ ViewerOps.FetchProp[self, $WalnutEntity]; msg: Msg_ V2E[ra]; MsgCategories[msg]; END; MsgGvIdProc: Menus.MenuProc = BEGIN self: Viewer_ NARROW[parent]; ra: REF ANY_ ViewerOps.FetchProp[self, $WalnutEntity]; msg: Msg_ V2E[ra]; name: ROPE = GetName[msg]; gID, other: ROPE; Report["gvID for msg is: ", name]; IF mouseButton = red THEN RETURN; [gID, other]_ OtherIDFormats[name]; Report[gID]; Report[other]; END; MsgFreezeProc: Menus.MenuProc = { self: Viewer = NARROW[parent]; msViewer: Viewer = NARROW[ViewerOps.FetchProp[self, $WhoSpawnedMe]]; frozen: Menus.MenuEntry = Menus.FindEntry[self.menu, "Freeze"]; IF frozen # NIL THEN FreezeViewer[self]; <> IF self.link # NIL THEN -- you are part of a split viewer FOR newV: Viewer _ self.link, newV.link UNTIL newV = self DO FreezeViewer[newV] ENDLOOP; <> IF msViewer # NIL THEN ViewerOps.AddProp[msViewer, $LastSpawned, NIL] }; FreezeViewer: PROC[ v: Viewer ] = { freezeButton: Menus.MenuEntry = Menus.FindEntry[v.menu, "Freeze"]; ViewerOps.AddProp[ v, $Frozen, v ]; IF freezeButton # NIL THEN { Menus.ReplaceMenuEntry[v.menu, freezeButton]; ViewerOps.PaintViewer[v, menu] } }; MsgSplitProc: Menus.MenuProc = { self: Viewer = NARROW[parent]; newV: Viewer; frozen: REF ANY = ViewerOps.FetchProp[self, $Frozen]; TEditSplit.Split[self]; <> FOR newV _ self.link, newV.link UNTIL newV.link = self DO ENDLOOP; ViewerOps.AddProp[ newV, $Entity, ViewerOps.FetchProp[ self, $Entity ] ]; ViewerOps.AddProp[ newV, $WalnutEntity, ViewerOps.FetchProp[ self, $WalnutEntity ] ]; ViewerOps.AddProp[ newV, $WhoSpawnedMe, ViewerOps.FetchProp[ self, $WhoSpawnedMe ] ]; IF frozen # NIL THEN FreezeViewer[newV] ELSE ViewerOps.AddProp[ newV, $DestroyMsgDisplayer, ViewerEvents.RegisterEventProc[DestroyMsgDisplayer, destroy, newV] ]; newV.icon _ msgIcon }; DestroyMsgDisplayer: ViewerEvents.EventProc = { eventProc: ViewerEvents.EventRegistration; next: Viewer = viewer.link; spawner: Viewer = NARROW[ ViewerOps.FetchProp[viewer, $WhoSpawnedMe] ]; IF ViewerOps.FetchProp[viewer, $Frozen] # NIL THEN RETURN; -- you're not involved IF spawner # NIL THEN ViewerOps.AddProp[ spawner, $LastSpawned, next ]; eventProc_ NARROW[ ViewerOps.FetchProp[viewer, $DestroyMsgDisplayer]]; ViewerEvents.UnRegisterEventProc[ eventProc, destroy]; }; MsgPlacesProc: Menus.MenuProc = Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Places" ] ].proc; MsgLevelsProc: Menus.MenuProc = Menus.CopyEntry[ Menus.FindEntry[ TiogaMenuOps.tiogaMenu, "Levels" ] ].proc; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *>> MsgCategories: PUBLIC PROC[msg: Msg] = BEGIN msL: LIST OF Value_ DB.GetPList[msg, mCategoryIs]; first: BOOL_ TRUE; name, date: ROPE; [name, date] _ ShortMsgName[msg]; ReportRope[IO.PutFR[" %g is in: ", IO.rope[Rope.Cat[name, " ", date]]]]; IF msL = NIL THEN {Report["no MsgSets! This is a bug."]; RETURN}; FOR mL: LIST OF Value_ msL, mL.rest UNTIL mL=NIL DO name: ROPE_ DB.GetName[V2E[mL.first]]; IF first THEN first_ FALSE ELSE ReportRope[", "]; ReportRope[name]; ENDLOOP; ReportRope["\n"]; END; ShortMsgName: PROC[msg: Msg] RETURNS[toEntry: ROPE, date: ROPE] = BEGIN TOCEntry: ROPE = DB.V2S[DB.GetP[e: msg, aIs: WalnutDB.mTOCEntryIs, aOf: WalnutDB.mTOCEntryOf]]; lastNonBlank: INT _ Rope.Length[TOCEntry]-1; date _ Rope.Substr[TOCEntry, 0, 9]; WHILE Rope.Fetch[TOCEntry, lastNonBlank] = '\n DO lastNonBlank _ lastNonBlank-1 ENDLOOP; toEntry _ RemoveComments[Rope.Substr[TOCEntry, 11, lastNonBlank-10]]; END; CheckName: PROC[name: ROPE] RETURNS[checked: ROPE] = BEGIN transProc: Rope.TranslatorType = { RETURN[IF old > Ascii.DEL THEN Ascii.DEL ELSE old] }; IF name.Length[] > 60 THEN name_ Rope.Concat[name.Substr[0, 57], " ..."]; checked_ Rope.Translate[base: name, translator: transProc]; END; OtherIDFormats: PROC[name: ROPE] RETURNS[gID, other: ROPE] = BEGIN OPEN IO; pupAddr: PupDefs.PupAddress; ts: GVBasics.Timestamp; h: IO.STREAM_ IO.RIS[name]; netAsInt, hostAsInt: INT; tyme: BasicTime.GMT; net, host: Basics.BYTE; sender, gvTimeStamp, pupHost: ROPE; sender_ h.GetTokenRope[IO.IDProc].token; UNTIL ([]_ h.GetChar[]) = '$ DO ENDLOOP; []_ h.GetChar[]; -- space netAsInt_ h.GetInt[]; []_ h.GetChar[]; -- # hostAsInt_ h.GetInt[]; []_ h.GetChar[]; -- @ tyme_ h.GetTime[]; net_ LOOPHOLE[netAsInt, Basics.LongNumber].ll; host_ LOOPHOLE[hostAsInt, Basics.LongNumber].ll; pupAddr_ [net: [net], host: [host], socket: [0, 0]]; ts_ [net: net, host: host, time: BasicTime.ToPupTime[tyme]]; gvTimeStamp_ RopeFromTimestamp[ts]; pupHost_ PupDefs.GetHostName[pupAddr]; gID_ PutFR["[sender: %g, gvTimeStamp: %g]", rope[sender], rope[gvTimeStamp]]; other_ PutFR["[sender: %g, fromHost: %g, time: %g", rope[sender], rope[pupHost], rope[WalnutSendOps.RFC822Date[tyme]]]; END; <> RopeFromTimestamp: PROC[stamp: GVBasics.Timestamp] RETURNS[Rope.ROPE] = { RETURN[ IO.PutFR["%b#%b@%g", [integer[stamp.net]], [integer[stamp.host]], [cardinal[stamp.time]] ] ] }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * *>> ShowMsgInMsgViewer: PROC[v: Viewer, contents: TiogaContents] = BEGIN isFormated: BOOL; KillFeedbackSel[v]; IF (isFormated_ contents.formatting.Length[] # 0) THEN {v.newVersion_ TRUE; ViewerOps.PaintViewer[v, caption]}; IF v.link # NIL THEN DestroySplitMsgDisplayers[v]; ViewerTools.SetTiogaContents[v, contents, FALSE]; IF isFormated THEN v.newVersion_ FALSE; ViewerTools.InhibitUserEdits[v]; ViewerOps.PaintViewer[v, all] END; <> KillFeedbackSel: PROC[v: Viewer] = BEGIN OPEN TiogaOps; who: Viewer_ GetSelection[feedback].viewer; IF who = v THEN CancelSelection[feedback]; END; DestroySplitMsgDisplayers: ENTRY PROC[keepThisOne: Viewer] = BEGIN ENABLE UNWIND => NULL; next: Viewer_ keepThisOne.link; next2: Viewer; event: ViewerEvents.EventRegistration; DO IF next = keepThisOne THEN EXIT; IF (event_ NARROW[ViewerOps.FetchProp[next, $DestroyMsgDisplayer]]) = NIL THEN LOOP; ViewerEvents.UnRegisterEventProc[event, destroy]; next2_ next.link; ViewerOps.DestroyViewer[next]; -- DON'T FORK here next_ next2; ENDLOOP; END; StripForIcon: PROC[ name: ROPE ] RETURNS[ iconLabel: ROPE ] = { <" in the name>> start: INT; dot: INT; suffixLength: NAT; name _ Rope.Concat[base: RemoveComments[name], rest: " "]; <> <> start _ 0; WHILE (dot _ Rope.Find[Rope.Substr[name, start], "."]) # -1 DO suffixLength _ 0; dot _ dot+start; WHILE Ascii.Letter[Rope.Fetch[name, dot+1+suffixLength]] DO suffixLength _ suffixLength+1; IF suffixLength > 4 THEN EXIT ENDLOOP; IF suffixLength <= 4 THEN { name _ Rope.Replace[name, dot, suffixLength+1]; start _ dot } ELSE start _ dot+suffixLength ENDLOOP; iconLabel _ Rope.Substr[name, 0, Rope.Length[name]-1] }; RemoveComments: PROC[ name: ROPE ] RETURNS[ shortName: ROPE ] = { start, end: INT; name _ Rope.Concat[base: name, rest: " "]; <" in the name>> start _ Rope.Find[name, "<"]; IF start > 0 THEN { end _ Rope.Find[s1: name, s2: ">", pos1: start+1]; IF end > 0 THEN name _ Rope.Replace[name, start, end-start+1] }; <> start _ Rope.Find[name, "("]; IF start > 0 THEN { end _ Rope.Find[s1: name, s2: ")", pos1: start+1]; IF end > 0 THEN name _ Rope.Replace[name, start, end-start+1] }; shortName _ Rope.Substr[name, 0, Rope.Length[name]-1] }; <<* * * * * * * * * * * * * * * * * * * * * * * * * * * *>> <> { OPEN Menus; AppendMenuEntry[ msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Freeze", MsgFreezeProc]]; AppendMenuEntry[ msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Categories", MsgCategoriesProc]]; AppendMenuEntry[ msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Answer", MsgAnswerProc]]; AppendMenuEntry[msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Forward", MsgForwardProc]]; AppendMenuEntry[msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Print", WalnutPrintOps.MsgPrintProc]]; AppendMenuEntry[msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "gvID", MsgGvIdProc]]; AppendMenuEntry[ msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Split", MsgSplitProc]]; AppendMenuEntry[ msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Places", MsgPlacesProc]]; AppendMenuEntry[ msgMenu, WalnutViewer.CreateMenuEntry[walnutQueue, "Levels", MsgLevelsProc]]; msgMenuAvailable_ TRUE; }; END. Change Log. WSH on March 4, 1983: take out all DBText stuff