-- File: WalnutMsgDisplayerImpl.mesa
-- Contents: Implementation of the Walnut Msg Display windows.
-- Last Edited by: Willie-Sue, December 8, 1982 10:25 am
-- Status: complete.
-- Created by: Rick Cattell & Willie-Sue Haugeland, 17-Feb-82
-- Last Edited by: Donahue, August 26, 1983 4:29 pm
-- Rick on: April 13, 1982 11:50 am
-- Willie-Sue on: November 22, 1983 5:10 pm
DIRECTORY
Ascii USING [ Letter ],
DB USING [Aborted, Failure, GetP, GetName, GetPList, V2S],
IO,
Menus,
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],
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, DB, IO, 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;
----------------------------
-- BuildMsgDisplayer and MsgInViewer are used by "outside callers"
-- they traffic mostly in ropes
BuildMsgViewer: PUBLIC PROC[mName: ROPE, msg: Msg, shift, paint: BOOL← FALSE]
RETURNS[v: 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]]];
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];
END;
MsgInViewer: PUBLIC PROC[mName: ROPE, msg: Msg, v: Viewer, shift: BOOL← FALSE] =
BEGIN
ShowMsgInMsgViewer[v, TiogaMsgFromLog[msg].contents];
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]]];
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];
-- Note how we cleverly put in a "." here to get the date to appear on a new line
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;
--***********************************************************
-- these two procs are backdoors into Walnut, so they need to be guarded
GetMsgName: PUBLIC PROC[v: Viewer] RETURNS[mName: ROPE] =
-- of v is a Viewer for a Walnut Entity, then return its name else NIL
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] =
-- if mName is the name of a msg, then display that msg in v else return found← FALSE
BEGIN
ENABLE
DB.Aborted, 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;
-- ***********************************************************
-- menu command procs:
MsgForwardProc: Menus.MenuProc =
{self: Viewer← NARROW[parent];
[]← WalnutSendOps.Forward[self, self];
};
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];
Report["gvID for msg is: ", GetName[msg]];
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];
-- now freeze everyone linked to you
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;
-- only need to do this once
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];
-- now find the newest link in the chain to copy properties
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;
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * *
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;
-- bug in SetTiogaContents necessitates this
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 ] = {
-- first remove any "< . . .>" in the name
start: INT;
dot: INT;
suffixLength: NAT;
name ← Rope.Concat[base: RemoveComments[name], rest: " "];
-- now see if there are any grapevine registries in the names
-- this is a real hack: look for two to four (for ".ARPA") alphabetics following a "."
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: " "];
-- first remove any "< . . .>" 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] };
-- then do the same for any ( . . . ) 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] };
shortName ← Rope.Substr[name, 0, Rope.Length[name]-1] };
-- * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-- initialize menues:
{ 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