WalnutMsgDisplayerImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by: Willie-Sue, September 29, 1986 11:07:08 am PDT
Pavel, February 6, 1986 3:33:33 pm PST
Donahue, June 17, 1985 4:53:34 pm PDT
Contents: Implementation of the Walnut Msg Display windows.
Status: complete.
Created by: Rick Cattell & Willie-Sue Haugeland, 17-Feb-82
Last Edited by: Donahue, December 27, 1984 11:41:54 am PST
(Added Nut connection)
(Changed StripForIcon to remove all registry information from icon labels)
Rick on: April 13, 1982 11:50 am
Willie-Sue on: January 3, 1985 4:27:53 pm PST
DIRECTORY
Ascii USING [ Letter ],
Basics USING [BYTE, LongNumber],
BasicTime USING [GMT, ToPupTime],
Convert USING [CardFromRope],
GVBasics USING [Timestamp],
IO,
Menus,
Nut USING [SetNutInfo, CopyNutInfo],
PopUpSelection USING [Request],
RefText USING[line, AppendChar, ObtainScratch, ReleaseScratch],
Rope,
WalnutDocumentRope USING [Create],
TEditSplit USING[ Split ],
TEditDocument USING [TEditDocumentData, maxClip],
TiogaOps USING [CancelSelection, GetSelection, RestoreSelB, SaveSelB, SelectDocument, SetStyle, ViewerDoc],
TiogaMenuOps USING[ tiogaMenu, AllLevels ],
UserProfile USING [Token],
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],
WalnutOps USING [MsgSet, GetCategories, GetMsgShortName, GetMsg, MsgExists],
WalnutOpsExtras USING [GetMsgSize],
WalnutViewer USING [CreateMenuEntry],
WalnutSendOps USING [Answer, Forward, WalnutSendProc],
WalnutSendOpsExtras USING [ReSend],
WalnutControlInternal USING [DoWaitCall],
WalnutDisplayerInternal,
WalnutPrintOps USING [MsgPrintProc],
WalnutWindowInternal USING [msgIcon, walnutQueue, Report, ReportRope],
WalnutWindow USING [EnumWalnutViewers];
WalnutMsgDisplayerImpl: CEDAR MONITOR
IMPORTS
Ascii, BasicTime, Convert, IO, Nut, PopUpSelection, RefText, Rope,
WalnutDocumentRope, TiogaMenuOps, TiogaOps, TEditSplit,
Menus, UserProfile, ViewerEvents, ViewerLocks, ViewerOps, ViewerTools,
WalnutOps, WalnutOpsExtras,
WalnutSendOps, WalnutSendOpsExtras, WalnutControlInternal, WalnutPrintOps,
WalnutViewer, WalnutWindow, WalnutWindowInternal
EXPORTS
WalnutDisplayerInternal, WalnutWindow
SHARES Menus =
BEGIN
ROPE: TYPE = Rope.ROPE;
TiogaContents: TYPE = ViewerTools.TiogaContents;
Viewer: TYPE = ViewerClasses.Viewer;
MsgSet: TYPE = WalnutOps.MsgSet;
msgMenu: PUBLIC Menus.Menu ← Menus.CreateMenu[];
frozenMsgMenu: Menus.Menu ← Menus.CreateMenu[];
nilMenu: Menus.Menu ← Menus.CreateMenu[];
--------------------------
exported to WalnutWindow
GetMsgName: PUBLIC PROC[v: Viewer] RETURNS[mName: ROPE] = {
of v is a Viewer for a Walnut Entity, then return its name else NIL
mName ← NARROW[ViewerOps.FetchProp[v, $WalnutMsgName]];
IF mName = NIL THEN WalnutWindowInternal.Report[" Not a Walnut Msg viewer"];
};
InternalAddToMsgMenu: PUBLIC PROC[label: ROPE, proc: Menus.MenuProc,
 clientData: REF ANYNIL, onQueue: BOOL] = {
frozenList: LIST OF Viewer;
new: Menus.MenuEntry = IF onQueue THEN
WalnutViewer.CreateMenuEntry[
 WalnutWindowInternal.walnutQueue, label, proc, clientData]
ELSE Menus.CreateEntry[label, proc, clientData];
Menus.AppendMenuEntry[msgMenu, new];
frozenList ← ChangeAllMsgMenus[];
FOR fL: LIST OF Viewer ← frozenList, fL.rest UNTIL fL = NIL DO
v: Viewer = fL.first;
Menus.AppendMenuEntry[v.menu, new];
ViewerOps.PaintViewer[v, menu];
ENDLOOP;
};
RemoveFromMsgMenu: PUBLIC PROC[name: ROPE] = {
old: Menus.MenuEntry = Menus.FindEntry[msgMenu, name];
notFound: BOOLFALSE;
frozenList: LIST OF Viewer;
IF old = NIL THEN RETURN;
Menus.ReplaceMenuEntry[msgMenu, old ! Menus.targetNotFound =>
{ notFound ← TRUE; CONTINUE}];
IF notFound THEN RETURN;
frozenList ← ChangeAllMsgMenus[];
FOR fL: LIST OF Viewer ← frozenList, fL.rest UNTIL fL = NIL DO
v: Viewer = fL.first;
Menus.ReplaceMenuEntry[v.menu, old ! Menus.targetNotFound => CONTINUE];
ViewerOps.PaintViewer[v, menu];
ENDLOOP;
};
onlyInTextViewer: ROPE = "Can only display msgs in Text viewers";
noSplitsAllowed: ROPE = "Can't display msg in a split viewer";
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
QDisplayMsg: PUBLIC PROC[msg: ROPE, oldV: Viewer, shift: BOOL, openIfIconic: BOOLTRUE]
RETURNS[v: Viewer] = {
contents: TiogaContents;
herald, shortName: ROPE;
iconName, date: ROPE;
LockedSetMenu: PROC = {ViewerOps.SetMenu[v, msgMenu]};
IF ~WalnutOps.MsgExists[msg] THEN RETURN[NIL];
[contents, herald, shortName] ← WalnutOps.GetMsg[msg];
iconName ← Rope.Substr[shortName, 0, shortName.Length[] - 9];
date ← Rope.Substr[shortName, shortName.Length[] - 9];
IF oldV # NIL AND ~oldV.destroyed THEN
{ oldV.name ← herald;
ViewerOps.PaintViewer[oldV, caption];
v ← oldV;
}
ELSE
{ v ← ViewerOps.CreateViewer[flavor: $Text, paint: ~shift,
 info: [name: herald, icon: WalnutWindowInternal.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, $DestroyMsgDisplayer,
ViewerEvents.RegisterEventProc[DestroyMsgDisplayer, destroy, v]];
};
Nut.SetNutInfo[v, $Walnut, "Msg", msg];
ViewerOps.AddProp[v, $WalnutMsgName, 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[iconName], " .", date]];
ShowMsgInMsgViewer[v, contents, shift, openIfIconic];
};
FixUpMsgViewer: PUBLIC PROC[msg: ROPE, v: Viewer] = {
IF (msg.Length[] = 0) OR v.destroyed THEN RETURN;
IF QDisplayMsg[msg, v, FALSE].v = NIL THEN {
WalnutWindowInternal.Report["Msg: ", msg, " doesn't exist; destroying viewer"];
ViewerOps.DestroyViewer[v];
};
};
DisplayMsgFromMsgSet: PUBLIC PROC[msg: ROPE, msViewer: Viewer, shift: BOOL] = {
v: Viewer = NARROW[ViewerOps.FetchProp[msViewer, $LastSpawned]];
newV: Viewer;
newV ← QDisplayMsg[msg, v, shift];
IF newV = v THEN RETURN;
ViewerOps.AddProp[newV, $WhoSpawnedMe, msViewer];
ViewerOps.AddProp[msViewer, $LastSpawned, newV];
};
***********************************************************
menu command procs:
MsgForwardProc: Menus.MenuProc = {
self: Viewer ← NARROW[parent];
self.inhibitDestroy← TRUE;
[]← WalnutSendOps.Forward[self, self];
self.inhibitDestroy← FALSE;
};
MsgReSendProc: Menus.MenuProc = {
self: Viewer ← NARROW[parent];
self.inhibitDestroy← TRUE;
[]← WalnutSendOpsExtras.ReSend[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;
};
MsgOpsProc: Menus.MenuProc = {
self: Viewer = NARROW[parent];
msg: ROPE = NARROW[ViewerOps.FetchProp[self, $WalnutMsgName]];
which: INT = PopUpSelection.Request["MsgOps",
LIST["Sender", "Categories", "SizeOf", "gvID", "PressPrint", "Interpress3.0Print"]
];
SELECT which FROM
1 => WalnutSendOps.WalnutSendProc[fromExec: FALSE];
2 => MsgCategoriesProc[self, msg];
3 => {
textLen, formatLen: INT;
[textLen, formatLen] ← WalnutOpsExtras.GetMsgSize[msg];
IF formatLen # 0 THEN
WalnutWindowInternal.Report[IO.PutFR["Formatted Msg \" %g\" has %g bytes of text",
IO.rope[msg], IO.int[textLen] ]]
ELSE WalnutWindowInternal.Report[IO.PutFR["Msg \"%g\" has %g bytes of text",
IO.rope[msg], IO.int[textLen] ]];
};
4 => MsgGvId[msg, mouseButton # red];
5 => MsgPrint[self: self, usePress: TRUE];
6 => MsgPrint[self: self, usePress: FALSE];
ENDCASE => NULL;
};
MsgCategoriesProc: PROC[self: Viewer, msg: ROPE] = {
IF ViewerOps.FetchProp[self, $Frozen] # NIL AND ~WalnutOps.MsgExists[msg] THEN
WalnutWindowInternal.Report[
"Msg ", msg, " in a frozen viewer doesn't exist in this database"]
ELSE MsgCategories[msg];
};
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 ] = {
ViewerOps.AddProp[ v, $Frozen, v ];
ViewerOps.SetMenu[v, frozenMsgMenu];
};
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;
Nut.CopyNutInfo[from: self, to: newV];
ViewerOps.AddProp[
newV, $WalnutMsgName, ViewerOps.FetchProp[self, $WalnutMsgName] ];
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 ← WalnutWindowInternal.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: ROPE] = {
Mc: PROC = {
msL: LIST OF ROPE ← WalnutOps.GetCategories[msg];
first: BOOLTRUE;
shortName: ROPE = WalnutOps.GetMsgShortName[msg];
WalnutWindowInternal.ReportRope[IO.PutFR[" %g is in: ", IO.rope[shortName]]];
IF msL = NIL THEN
{WalnutWindowInternal.Report["no MsgSets! This is a bug."]; RETURN};
FOR mL: LIST OF ROPE ← msL, mL.rest UNTIL mL=NIL DO
IF first THEN first ← FALSE ELSE WalnutWindowInternal.ReportRope[", "];
WalnutWindowInternal.ReportRope[mL.first];
ENDLOOP;
WalnutWindowInternal.ReportRope["\n"];
};
[] ← WalnutControlInternal.DoWaitCall[Mc];
};
MsgGvId: PUBLIC PROC[msg: ROPE, all: BOOL] = {
Mg: PROC = {
gID, other: ROPE;
WalnutWindowInternal.Report["gvID for msg is: ", msg];
IF ~all THEN RETURN;
[gID, other] ← OtherIDFormats[msg];
WalnutWindowInternal.Report[gID];
WalnutWindowInternal.Report[other];
};
[] ← WalnutControlInternal.DoWaitCall[Mg];
};
MsgPrint: PROC[self: Viewer, usePress: BOOL] = {
Mp: PROC =
{ WalnutPrintOps.MsgPrintProc[self, usePress] };
[] ← WalnutControlInternal.DoWaitCall[Mp];
};
OtherIDFormats: PROC[msg: ROPE] RETURNS[gID, other: ROPE] = {
OPEN IO;
ts: GVBasics.Timestamp;
h: IO.STREAMIO.RIS[msg];
netAsRope, hostAsRope: ROPE;
netAsLC, hostAsLC: LONG CARDINAL;
tyme: BasicTime.GMT;
net, host: Basics.BYTE;
sender, gvTimeStamp: ROPE;
BEGIN
temp: REF TEXT = RefText.ObtainScratch[RefText.line];
ch: CHAR;
temp.length ← 0;
UNTIL (ch ← h.GetChar[]) = '$ DO [] ← RefText.AppendChar[temp, ch]; ENDLOOP;
temp.length ← temp.length - 1;  -- delete trailing SP
sender ← Rope.FromRefText[temp];
RefText.ReleaseScratch[temp];
END;
[] ← h.GetChar[]; -- space
netAsRope ← h.GetCedarTokenRope[].token;
[] ← h.GetChar[];  -- #
hostAsRope ← h.GetCedarTokenRope[].token;
[] ← h.GetChar[];  -- @
tyme ← h.GetTime[];
netAsLC ← Convert.CardFromRope[netAsRope, 8];
hostAsLC ← Convert.CardFromRope[hostAsRope, 8];
net ← LOOPHOLE[netAsLC, Basics.LongNumber].ll;
host ← LOOPHOLE[hostAsLC, Basics.LongNumber].ll;
ts ← [net: net, host: host, time: BasicTime.ToPupTime[tyme]];
gvTimeStamp ← RopeFromTimestamp[ts];
gID ← PutFR["[sender: %g, gvTimeStamp: %g]", rope[sender], rope[gvTimeStamp]];
};
the one from GVBasics gets a bound fault
RopeFromTimestamp: PROC[stamp: GVBasics.Timestamp] RETURNS[Rope.ROPE] =
{ RETURN[ IO.PutFR["%b#%b@%g",
[integer[stamp.net]], [integer[stamp.host]], [cardinal[stamp.time]] ] ]
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
ChangeAllMsgMenus: PROC RETURNS[fL: LIST OF Viewer] = {
-- a menu entry was added or removed - update all msg viewers
vL: LIST OF Viewer ←
WalnutWindow.EnumWalnutViewers[keepSeparate: TRUE].msgList;
FOR vL2: LIST OF Viewer ← vL, vL2.rest UNTIL vL2 = NIL DO
IF ViewerOps.FetchProp[vL2.first, $Frozen] # NIL THEN
fL ← CONS[vL2.first, fL] ELSE ViewerOps.SetMenu[vL2.first, msgMenu];
ENDLOOP;
};
plainTextStyle: PUBLIC ROPE ← UserProfile.Token[key: "Walnut.PlainTextStyle", default: "cedar"];
ShowMsgInMsgViewer: PROC[v: Viewer, contents: TiogaContents, shift: BOOL, open: BOOLTRUE] = {
painted: BOOLFALSE;
KillFeedbackSel[v];
IF v.link # NIL THEN DestroySplitMsgDisplayers[v];
check to see whether AllLevels will cause a paint
BEGIN
WITH v.data SELECT FROM
tdd: TEditDocument.TEditDocumentData =>
IF tdd.clipLevel # TEditDocument.maxClip THEN painted ← TRUE
ENDCASE;
END;
IF contents.formatting.IsEmpty[]
AND NOT Rope.Equal[plainTextStyle, "cedar"] THEN { -- Plain Text
ViewerTools.SetTiogaContents[v, contents, FALSE];
TiogaOps.SaveSelB[];
TiogaOps.SelectDocument[viewer: v, level: point];
TiogaOps.SetStyle[plainTextStyle, root];
v.newVersion ← FALSE; -- Is there a ``better'' way to do this?
ViewerOps.PaintViewer[v, all];
TiogaOps.RestoreSelB[];
}
ELSE { -- Tioga formatted; needn't reset the style
ViewerTools.SetTiogaContents[v, contents, NOT painted];
TiogaMenuOps.AllLevels[v];
};
ViewerTools.InhibitUserEdits[v];
IF v.iconic THEN {
IF ~open THEN
RETURN;
ViewerOps.OpenIcon[v, shift]
}
ELSE IF shift THEN
ViewerOps.GrowViewer[v]
};
bug in SetTiogaContents necessitates this
KillFeedbackSel: PROC[v: Viewer] = {
OPEN TiogaOps;
who: Viewer ← GetSelection[feedback].viewer;
IF who = v THEN CancelSelection[feedback];
};
DestroySplitMsgDisplayers: ENTRY PROC[keepThisOne: Viewer] = {
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;
};
StripForIcon: PROC[name: ROPE] RETURNS[ iconLabel: ROPE ] = {
start: INT;
dot: INT;
suffixLength: NAT;
Remove all registry information from the icon label -- where registry information is taken to be any sequence of letters following a period
start ← 0;
WHILE (dot ← Rope.Find[Rope.Substr[name, start], "."]) # -1 DO
length: INT = Rope.Length[name];
dot ← dot+start;
IF length - dot < 2 THEN EXIT;
suffixLength ← 0;
WHILE dot+1+suffixLength < length AND
Ascii.Letter[Rope.Fetch[name, dot+1+suffixLength]] DO
suffixLength ← suffixLength+1
ENDLOOP;
IF suffixLength >= 0 THEN
{ name ← Rope.Replace[name, dot, suffixLength+1]; start ← dot }
ELSE start ← dot+suffixLength+1
ENDLOOP;
iconLabel ← Rope.Substr[name, 0, Rope.Length[name]-1]
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
initialize menues:
{ OPEN Menus, WalnutWindowInternal;
AppendMenuEntry[ msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Freeze", MsgFreezeProc]];
AppendMenuEntry[ msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Answer", MsgAnswerProc]];
AppendMenuEntry[msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Forward", MsgForwardProc]];
AppendMenuEntry[msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "ReSend", MsgReSendProc]];
AppendMenuEntry[msgMenu, Menus.CreateEntry["MsgOps", MsgOpsProc]];
AppendMenuEntry[msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Print", WalnutPrintOps.MsgPrintProc]];
AppendMenuEntry[ msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Split", MsgSplitProc]];
AppendMenuEntry[ msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Places", MsgPlacesProc]];
AppendMenuEntry[ msgMenu,
WalnutViewer.CreateMenuEntry[walnutQueue, "Levels", MsgLevelsProc]];
AppendMenuEntry[ frozenMsgMenu, Menus.CreateEntry["Answer", MsgAnswerProc]];
AppendMenuEntry[ frozenMsgMenu, Menus.CreateEntry["Forward", MsgForwardProc]];
AppendMenuEntry[ frozenMsgMenu, Menus.CreateEntry["ReSend", MsgReSendProc]];
AppendMenuEntry[ frozenMsgMenu, Menus.CreateEntry["MsgOps", MsgOpsProc]];
AppendMenuEntry[ frozenMsgMenu, Menus.CreateEntry["Split", MsgSplitProc]];
AppendMenuEntry[ frozenMsgMenu, Menus.CreateEntry["Places", MsgPlacesProc]];
AppendMenuEntry[ frozenMsgMenu, Menus.CreateEntry["Levels", MsgLevelsProc]];
};
END.
Change Log.
WSH on March 4, 1983: take out all DBText stuff