DIRECTORY
Ascii USING [ Letter ],
Basics USING [BYTE, LongNumber],
BasicTime USING [GMT, ToPupTime],
Convert USING [CardFromRope],
GVBasics USING [Timestamp],
IO,
Menus,
Nut USING [SetNutInfo, CopyNutInfo],
PupDefs USING [PupAddress, GetHostName],
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],
WalnutViewer USING [CreateMenuEntry],
WalnutSendOps USING [Answer, Forward, RFC822Date],
WalnutDisplayerInternal,
WalnutPrintOps USING [MsgPrintProc],
WalnutWindowInternal USING [msgIcon, walnutQueue, Report, ReportRope],
WalnutWindow USING [EnumWalnutViewers];
WalnutMsgDisplayerImpl:
CEDAR
MONITOR
IMPORTS
Ascii, BasicTime, Convert, IO, Nut, PupDefs, RefText, Rope,
WalnutDocumentRope, TiogaMenuOps, TiogaOps, TEditSplit,
Menus, UserProfile, ViewerEvents, ViewerLocks, ViewerOps, ViewerTools,
WalnutOps,
WalnutSendOps, WalnutPrintOps,
WalnutViewer, WalnutWindow, WalnutWindowInternal
EXPORTS
WalnutDisplayerInternal, WalnutWindow
SHARES Menus =
QDisplayMsg:
PUBLIC
PROC[msg:
ROPE, oldV: Viewer, shift:
BOOL, openIfIconic:
BOOL ←
TRUE]
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];
};
***********************************************************
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 = {
self: Viewer = NARROW[parent];
msg: ROPE = NARROW[ViewerOps.FetchProp[self, $WalnutMsgName]];
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];
};
MsgGvIdProc: Menus.MenuProc = {
self: Viewer = NARROW[parent];
msg: ROPE = NARROW[ViewerOps.FetchProp[self, $WalnutMsgName]];
MsgGvId[msg, mouseButton # red];
};
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;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
OtherIDFormats:
PROC[msg:
ROPE]
RETURNS[gID, other:
ROPE] = {
OPEN IO;
pupAddr: PupDefs.PupAddress;
ts: GVBasics.Timestamp;
h: IO.STREAM ← IO.RIS[msg];
netAsRope, hostAsRope: ROPE;
netAsLC, hostAsLC: LONG CARDINAL;
tyme: BasicTime.GMT;
net, host: Basics.BYTE;
sender, gvTimeStamp, pupHost: 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;
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]]];
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
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:
BOOL ←
TRUE] = {
painted: BOOL ← FALSE;
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 {
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]
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Change Log.