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 =
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;
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;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
OtherIDFormats:
PROC[msg:
ROPE]
RETURNS[gID, other:
ROPE] = {
OPEN IO;
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: 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]];
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
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]
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
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]];
};
Change Log.