WalnutMsgSetButtonsImpl.mesa
Copyright Ó 1985, 1988, 1992 by Xerox Corporation. All rights reserved.
Willie-Sue, July 5, 1990 1:00:44 pm PDT
Pavel, February 7, 1986 11:43:05 am PST
Jack Kent April 7, 1987 5:14:42 pm PST
Doug Terry, October 16, 1990 9:29 am PDT
Swinehart, June 19, 1991 8:16 am PDT
Contents: co-ordinates MsgSet button handling for WalnutControlWindow
Willie-s, April 27, 1992 7:52 pm PDT
created October, 1982 by Willie-Sue
Last edit by:
Willie-Sue on: January 3, 1985 4:49:23 pm PST
Donahue on: January 31, 1985 3:13:33 pm PST
(To be consistent with new WalnutDisplayerOps, WalnutDisplayerOpsInternal)
(Restart code calls proc to redisplay all message set viewers, since check for version is done there)
(Again, this got much smaller and simpler through changes to WalnutWindow, WalnutWindowInternal)
DIRECTORY
Containers USING [ChildXBound],
EditSpan USING [ChangeLooks, noLooks, Replace],
FileNames USING [StripVersionNumber],
FS USING [Error, FileInfo],
PFS,
Rope,
RopeList USING [DRemove, Length],
TBQueue USING [ AppendToTiogaButton, CreateTiogaButtonFromNode ],
TEditDocument USING [TEditDocumentData],
TextLooks USING [RopeToLooks],
TextNode USING [LastWithin, NodeItself, Span],
Tioga USING [Node],
TiogaButtons USING [ AppendToButton, ChangeButtonLooks, CreateViewer, DeleteButton, GetRope, LoadViewer, MarkViewerNotEdited, RegisterModifiedProc, SetState, TiogaButton, TiogaButtonProc, WasModifiedProc ],
TiogaIO USING [FromRope, ToFile],
TiogaOps USING [GetRope, InsertRope, IsComment, Ref, StepForward],
UserProfile USING [Number, Token],
VFonts USING [StringWidth],
ViewerOps USING [ AddProp, ComputeColumn, DestroyViewer, FetchProp, GrowViewer, MoveViewer, OpenIcon, PaintViewer, SetOpenHeight ],
ViewerClasses USING [Viewer],
ViewerSpecs USING [openLeftWidth, openRightWidth, scrollBarW],
WalnutOps,
WalnutInternal USING [alternateWalnutIcons, ComeFrom, displayMsgSetInIcon, labelledWalnutIcons, QDisplayMsgSet, unLabelledWalnutIcons],
WalnutWindow USING [],
WalnutWindowSidedoor,
WalnutWindowPrivate USING[MsgSetButton, MsgSetButtonObject, MsgSetInfo, WalnutHandle, WalnutHandleRec];
WalnutMsgSetButtonsImpl: CEDAR PROGRAM
IMPORTS
FileNames, FS, PFS, Rope, RopeList, UserProfile,
Containers, EditSpan, TBQueue, TextNode, TextLooks, TiogaButtons, TiogaIO, TiogaOps, VFonts, ViewerOps, ViewerSpecs,
WalnutOps, WalnutInternal
EXPORTS WalnutWindow, WalnutInternal, WalnutWindowSidedoor =
BEGIN OPEN WalnutInternal;
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
TiogaButton: TYPE = TiogaButtons.TiogaButton;
MsgSet: TYPE = WalnutOps.MsgSet;
WalnutHandle: TYPE = WalnutWindowPrivate.WalnutHandle;
WalnutHandleRec: PUBLIC TYPE = WalnutWindowPrivate.WalnutHandleRec;
MsgSetButton: TYPE = WalnutWindowPrivate.MsgSetButton;
MsgSetButtonObject: TYPE = WalnutWindowPrivate.MsgSetButtonObject;
MsgSetInfo: TYPE = WalnutWindowPrivate.MsgSetInfo;
msbDefaultLooks: PUBLIC ROPE ¬
UserProfile.Token[key: "Walnut.MsgSetButtonDefaultLooks", default: ""];
msbSelectedLooks: PUBLIC ROPE ¬
UserProfile.Token[key: "Walnut.MsgSetButtonSelectedLooks", default: "bi"];
lineHeight: INTEGER = 12;  -- different for TiogaButtons
maxLines: INT ¬ 10; 
spaceWidth: INT ¬ VFonts.StringWidth["P"];
nonExistentMsgSetLooks: ROPE ¬
UserProfile.Token[key: "Walnut.NonExistentMsgSetLooks", default: "y"];
Invariant:
IF displayMsgSetInIcon AND selectedMsgSetButtons # NIL THEN
Current icon is labelled and the label = selectedMsgSetButtons.msgSet.name
ELSE
Current icon is unlabelled
The invariant is established at the beginning by initialising as follows:
walnutIcon ← unLabelledWalnutIcon
newMailIcon ← unLabelledNewMailIcon
walnut.icon ← walnutIcon
selectedMsgSetButtons ← NIL
The maintenance of the invariant is split between this procedure and DeselectMsgSet, below.
SelectMsgSetProc: TiogaButtons.TiogaButtonProc = {
IF red & cntl & shift THEN msgs in selected msgsets ComeFrom those msgsets to this msgSet
IF red THEN deselect all & select this one; IF yellow then display;
IF blue THEN toggle whether selected or not
msbH: MsgSetButton = NARROW[clientData];
msb, msb2: MsgSetButton;
wH: WalnutHandle;
IF msbH = NIL THEN RETURN; -- no MsgSetButton for this tioga button
wH ¬ msbH.wH;
IF (mouseButton = red) AND shift THEN {
IF control THEN WalnutInternal.ComeFrom[msbH]
ELSE TiogaOps.InsertRope[msbH.msgSet.name];
RETURN;
};
IF mouseButton = yellow THEN {
MSDisplay[msbH, shift];
RETURN;
};
IF mouseButton = red THEN {  -- deselect any selected
IF (msb ¬ wH.selectedMsgSetButtons) # NIL THEN {
wH.selectedMsgSetButtons ¬ NIL;
DO
FOR tbL: LIST OF TiogaButton ¬ msb.allButtons, tbL.rest UNTIL tbL = NIL DO
TiogaButtons.ChangeButtonLooks[
button: tbL.first,
addLooks: msbDefaultLooks,
removeLooks: msbSelectedLooks
];
ENDLOOP;
IF (msb2 ¬ msb.selected) = NIL THEN
EXIT;
msb.selected ¬ NIL;
msb ¬ msb2;
ENDLOOP;
}
}
ELSE { -- blue button, toggle the state of this msgSet
FOR msb ¬ wH.selectedMsgSetButtons, msb.selected UNTIL msb=NIL DO
IF msb = msbH THEN { -- This one was already selected; deselect it and quit
DeselectMsgSet[msbH];
RETURN;
};
ENDLOOP;
msbH.selected ¬ wH.selectedMsgSetButtons;
};
Here we actually select the tioga buttons for the clicked button
wH.selectedMsgSetButtons ¬ msbH;
FOR tbL: LIST OF TiogaButton ¬ msbH.allButtons, tbL.rest UNTIL tbL = NIL DO
TiogaButtons.ChangeButtonLooks[
button: tbL.first,
addLooks: msbSelectedLooks,
removeLooks: msbDefaultLooks
];
ENDLOOP;
Fix up the icon, if necessary
FixIconAndLabel[wH];
};
FixIconAndLabel: PROC[wH: WalnutHandle] = {
-- This procedure does not repaint the icon, since most callers won't want that
IF displayMsgSetInIcon AND wH.selectedMsgSetButtons # NIL THEN {
label: ROPE ¬ Rope.Concat[wH.identifierPrefix, wH.selectedMsgSetButtons.msgSet.name];
FOR msb: MsgSetButton ¬ wH.selectedMsgSetButtons.selected, msb.selected
UNTIL msb = NIL DO
label ¬ Rope.Cat[label, ", ", msb.msgSet.name];
ENDLOOP;
wH.walnut.label ¬ label;
wH.iconSet ¬ labelledWalnutIcons;
}
ELSE
IF (wH.walnut.label ¬ wH.iconIdentifier) = NIL THEN
wH.iconSet ¬ unLabelledWalnutIcons ELSE wH.iconSet ¬ alternateWalnutIcons;
wH.walnut.icon ¬ wH.iconSet[wH.whichIcon];
};
ShowMsgSetButtons: PUBLIC PROC[wH: WalnutHandle] = {
msL: LIST OF ROPE;
first: BOOL ¬ TRUE;
visibleSpace: INT;
version, numMsgSets: INT;
The following code is commented out since having a local and remote MsgSetButtonsFile is not required. Plus, the "sneaky" trick doesn't work. ... DBT
remoteName: ROPE = wH.localMsgSetButtonsFile.Substr[2]; -- sneaky
What DOES work is for local and remote files to be one and the same! DCS February 26, 1991
End of commented out code. ...DBT
remoteName: ROPE = wH.localMsgSetButtonsFile;
wH.msgSetButtonsFile ¬ FS.FileInfo[remoteName !
FS.Error => IF error.code = $unknownFile THEN CONTINUE ].fullFName;
IF wH.msgSetButtonsFile # NIL THEN wH.msgSetButtonsFile ¬ FileNames.StripVersionNumber[wH.msgSetButtonsFile];
[version, numMsgSets] ¬ WalnutOps.MsgSetsInfo[wH.opsH];
IF wH.firstMsgSetButton # NIL THEN
IF version = wH.msgSetsVersion THEN {
-- check for changed, displayed msgSets
FOR msb: MsgSetButton ¬ wH.firstMsgSetButton, msb.next UNTIL msb=NIL DO
IF msb.msViewer # NIL THEN {
IF msb.msViewer.destroyed THEN {
msb.msViewer ¬ NIL;
msb.msgSet.version ¬ -1;
}
ELSE [] ¬ WalnutInternal.QDisplayMsgSet[wH, msb]
}
ELSE msb.msgSet.version ¬ -1; -- might have been displayed once
ENDLOOP;
RETURN;
};
[msL, wH.msgSetsVersion] ¬ WalnutOps.MsgSetNames[opsHandle: wH.opsH, alphaOrder: TRUE];
IF RopeList.Length[msL] = 2 THEN
msL ¬ LIST[WalnutOps.ActiveName, WalnutOps.DeletedName]
ELSE { -- more than just active & deleted
change the list so active and deleted are first
msL ¬ RopeList.DRemove[msL, WalnutOps.ActiveName, FALSE];
msL ¬ RopeList.DRemove[msL, WalnutOps.DeletedName, FALSE];
msL ¬ CONS[WalnutOps.ActiveName, CONS[WalnutOps.DeletedName, msL]];
};
calculate how big the area for msgSet buttons should be
BEGIN
numLines: INT ¬ UserProfile.Number["Walnut.NumMsgSetButtonsLines", -1];
IF numLines = -1 THEN {
colWidth: INT ¬ (IF wH.walnut.column = right THEN ViewerSpecs.openRightWidth
ELSE ViewerSpecs.openLeftWidth) - ViewerSpecs.scrollBarW;
numChars, totalCharWidth: INT ¬ 0;
FOR nL: LIST OF ROPE ¬ msL, nL.rest UNTIL nL=NIL DO
numChars ¬ numChars + nL.first.Length[] + 2; -- two spaces between
ENDLOOP;
totalCharWidth ¬ numChars*spaceWidth;
numLines ¬ MAX[4, (totalCharWidth/colWidth) + 1];
IF numLines <= 0 THEN numLines ¬ 1
ELSE IF numLines > maxLines THEN numLines ¬ maxLines;
};
visibleSpace ¬ numLines * lineHeight + 1;
END;
IF wH.msgSetsTViewer # NIL THEN DestroyAllMsgSetButtons[wH]; -- start from scratch; it's easier
wH.msgSetsTViewer ¬ TiogaButtons.CreateViewer[
info: [wy: wH.walnutRulerBefore.wy + wH.walnutRulerBefore.wh, ww: wH.walnut.ww,
wh: visibleSpace, parent: wH.walnut, border: FALSE] ];
Containers.ChildXBound[wH.walnut, wH.msgSetsTViewer];
BEGIN
allMsgSetsNode: Tioga.Node;
allMsgSets: ROPE ¬ msL.first;
allSpan: TextNode.Span;
FOR mL: LIST OF ROPE ¬ msL.rest, mL.rest UNTIL mL=NIL DO
allMsgSets ¬ Rope.Cat[allMsgSets, " ", mL.first];
ENDLOOP;
allMsgSetsNode ¬ TiogaIO.FromRope[allMsgSets];
allSpan.start ¬ [TextNode.LastWithin[allMsgSetsNode], TextNode.NodeItself];
allSpan.end ¬ allSpan.start;
EditSpan.ChangeLooks[ allMsgSetsNode, allSpan, EditSpan.noLooks, TextLooks.RopeToLooks[msbDefaultLooks] ];
IF wH.msgSetButtonsFile # NIL THEN {
root: Tioga.Node;
lastChild: Tioga.Node;
TiogaButtons.LoadViewer[viewer: wH.msgSetsTViewer, fileName: wH.msgSetButtonsFile];
find and replace last node with allMsgSetsNode - such fun. Assumes at least two nodes
TiogaButtons.SetState[wH.msgSetsTViewer, editing];
root ¬ NARROW[wH.msgSetsTViewer.data, TEditDocument.TEditDocumentData].text;
lastChild ¬ TextNode.LastWithin[root];
[] ¬ EditSpan.Replace[destRoot: root, sourceRoot: allMsgSetsNode,
dest: [[lastChild, TextNode.NodeItself], [lastChild, TextNode.NodeItself]],
source: allSpan,
saveForPaste: FALSE
];
TiogaButtons.MarkViewerNotEdited[root];
TiogaButtons.SetState[wH.msgSetsTViewer, buttoning];
}
ELSE { -- generate a local file with all the msg sets in it
[] ¬ TiogaIO.ToFile[fileName: PFS.PathFromRope[wH.localMsgSetButtonsFile], root: allMsgSetsNode];
TiogaButtons.LoadViewer[viewer: wH.msgSetsTViewer, fileName: wH.localMsgSetButtonsFile];
};
END;
ViewerOps.AddProp[wH.msgSetsTViewer, $WalnutHandle, wH];
TiogaButtons.RegisterModifiedProc[v: wH.msgSetsTViewer, proc: BuildMsgSetButtons];
BEGIN
newY: INTEGER ¬ wH.walnutRulerBefore.wy + wH.walnutRulerBefore.wh + visibleSpace;
v: Viewer = CheckOptions[];
CheckOptions: PROC RETURNS[v: Viewer] = {
IF wH.oldMailReader # NIL THEN
IF NOT (v ¬ wH.oldMailReader.container).destroyed THEN RETURN[v]
ELSE { wH.oldMailReader ¬ NIL; RETURN[NIL]};
IF wH.printingOptions # NIL THEN
IF NOT (v ¬ wH.printingOptions.container).destroyed THEN RETURN[v]
ELSE { wH.printingOptions ¬ NIL; RETURN[NIL]};
};
IF wH.walnutRulerAfter.wy # newY THEN {
ViewerOps.MoveViewer[wH.walnutRulerAfter, wH.walnutRulerAfter.wx,
newY, wH.walnutRulerAfter.ww, wH.walnutRulerAfter.wh, FALSE];
newY ¬ wH.walnutRulerAfter.wy+wH.walnutRulerAfter.wh+1;
IF v # NIL AND NOT v.destroyed THEN {
ViewerOps.MoveViewer[v, v.wx, newY, v.ww, v.wh, FALSE];
newY ¬ v.wy+v.wh+1;
};
ViewerOps.MoveViewer[wH.walnutTS, wH.walnutTS.wx,
newY, wH.walnutTS.ww, wH.walnutTS.wh, FALSE];
ViewerOps.SetOpenHeight[wH.msgSetsTViewer, visibleSpace];
ViewerOps.ComputeColumn[wH.walnut.column];
ViewerOps.PaintViewer[wH.walnutRulerAfter, all];
};
END;
DoBuildMsgSetButtons[wH, msL];
msb ← wH.activeMsgSetButton ← AddMSButton[wH, msb, WalnutOps.ActiveName, FALSE];
msb ← wH.deletedMsgSetButton← AddMSButton[wH, msb, WalnutOps.DeletedName];
FOR msL2: LIST OF ROPE ← msL, msL2.rest UNTIL msL2 = NIL DO
IF ~Rope.Equal[(ms ← msL2.first), WalnutOps.ActiveName, FALSE] AND
~Rope.Equal[ms, WalnutOps.DeletedName, FALSE] THEN
msb ← AddMSButton[wH, msb, ms];
ENDLOOP;
};
GetSelectedMsgSets: PUBLIC PROC[wH: WalnutHandle]
RETURNS[msL: LIST OF MsgSetButton] = {
FOR msB: MsgSetButton ¬ wH.selectedMsgSetButtons, msB.selected UNTIL msB=NIL DO
msL ¬ CONS[msB, msL];
ENDLOOP;
};
GetSelectedMsgSetNames: PUBLIC PROC[wH: WalnutHandle]
RETURNS[msgSetNames: LIST OF ROPE] = {
FOR msB: MsgSetButton ¬ wH.selectedMsgSetButtons, msB.selected UNTIL msB=NIL DO
msgSetNames ¬ CONS[msB.msgSet.name, msgSetNames];
ENDLOOP;
};
AddMsgSetButton: PUBLIC PROC[wH: WalnutHandle, msgSet: Rope.ROPE, select: BOOL] = {
thisMsb: MsgSetButton;
prevMsb: MsgSetButton ¬ wH.deletedMsgSetButton;
DO
nextMsb: MsgSetButton ¬ prevMsb.next;
IF nextMsb = NIL THEN
{ thisMsb ¬ AddMSButton[wH, prevMsb, msgSet]; EXIT };
IF Rope.Compare[msgSet, nextMsb.msgSet.name] = less THEN
{ thisMsb ¬ AddMSButton[wH, prevMsb, msgSet]; EXIT };
prevMsb ¬ nextMsb;
ENDLOOP;
IF select THEN SelectMsgSetProc[button: NIL, clientData: thisMsb]; -- red-select new MsgSet
};
DeleteMsgSetButton: PUBLIC PROC[wH: WalnutHandle, msgSet: ROPE] = {
if msgSet is Active, just destroy viewer, but not button
prevMsb: MsgSetButton ¬ wH.firstMsgSetButton;
FOR msb: MsgSetButton ¬ wH.firstMsgSetButton, msb.next UNTIL msb = NIL DO
IF Rope.Equal[msb.msgSet.name, msgSet, FALSE] THEN
{ msV: Viewer ¬ msb.msViewer;
IF msV # NIL AND ~msV.destroyed THEN ViewerOps.DestroyViewer[msV];
IF Rope.Equal[msgSet, WalnutOps.ActiveName] THEN
{ msb.msViewer ¬ NIL; RETURN};
DeselectMsgSet[msb];
prevMsb.next ¬ msb.next;
TiogaButtons.DeleteButton[msb.mainButton];
FOR tbL: LIST OF TiogaButton ¬ msb.allButtons, tbL.rest UNTIL tbL = NIL DO
IF tbL.first = msb.mainButton THEN LOOP;
TiogaButtons.ChangeButtonLooks[button: tbL.first, addLooks: nonExistentMsgSetLooks];
ENDLOOP;
RETURN
};
prevMsb ¬ msb;
ENDLOOP;
};
DestroyAllMsgSetButtons: PUBLIC PROC[wH: WalnutHandle] = {
wH.selectedMsgSetButtons ¬ NIL;
FOR msb: MsgSetButton ¬ wH.firstMsgSetButton, msb.next UNTIL msb=NIL DO
IF msb.msViewer # NIL THEN ViewerOps.DestroyViewer[msb.msViewer];
msb.msViewer ¬ NIL;
ENDLOOP;
wH.firstMsgSetButton ¬ NIL;
ViewerOps.MoveViewer[wH.walnutRulerAfter, wH.walnutRulerAfter.wx,
wH.walnutRulerBefore.wy+wH.walnutRulerBefore.wh+14+2, wH.walnutRulerAfter.ww,
wH.walnutRulerAfter.wh, FALSE];
ViewerOps.MoveViewer[wH.walnutTS, wH.walnutTS.wx,
wH.walnutRulerAfter.wy+wH.walnutRulerAfter.wh+1, wH.walnutTS.ww,
wH.walnutTS.wh, FALSE];
ViewerOps.DestroyViewer[wH.msgSetsTViewer, FALSE];
wH.msgSetsTViewer ¬ NIL;
ViewerOps.ComputeColumn[wH.walnut.column];
ViewerOps.PaintViewer[wH.walnut, client];
};
SelectMsgSetsFromMSNames: PUBLIC PROC [wH: WalnutHandle, msNames: LIST OF ROPE]
RETURNS[notFound: LIST OF ROPE] = {
Selects the message set buttons in the Walnut Control viewer which corresponds to the list of msNames given.
first: BOOLEAN ¬ TRUE;
IF wH.walnut = NIL THEN RETURN[msNames];
FOR msNameList: LIST OF ROPE ¬ msNames, msNameList.rest UNTIL msNameList = NIL DO
msb: MsgSetButton ¬ GetButton[wH, msNameList.first];
IF msb = NIL THEN {
notFound ¬ CONS[msNameList.first, notFound];
LOOP
};
SelectMsgSetProc[
button: NIL,
mouseButton: IF first THEN red ELSE blue,
clientData: msb];
first ¬ FALSE;
ENDLOOP;
IF displayMsgSetInIcon AND wH.walnut.iconic THEN ViewerOps.PaintViewer[wH.walnut, all];
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
AddMSButton: PROC[wH: WalnutHandle, prevMsb: MsgSetButton, msgSet: ROPE]
 RETURNS[msbH: MsgSetButton] = {
assumes msgSet does not already have a button; adds it after prevMsb
spaceBt, newBt: TiogaButtons.TiogaButton;
prevBtL: LIST OF TiogaButton;
msbH ¬ NEW[MsgSetButtonObject ¬ [wH: wH, msgSet: [name: msgSet], next: prevMsb.next] ];
spaceBt ¬ TiogaButtons.AppendToButton[
button: prevMsb.mainButton, rope: " ", looks: msbDefaultLooks];
newBt ¬ TBQueue.AppendToTiogaButton[
q: wH.walnutQueue, button: spaceBt, rope: msgSet,
looks: msbDefaultLooks, proc: SelectMsgSetProc, clientData: msbH];
msbH.spaceButton ¬ spaceBt;
msbH.mainButton ¬ newBt;
msbH.allButtons ¬ LIST[newBt];
prevMsb.next ¬ msbH;
FOR tbL: LIST OF TiogaButton ¬ wH.orphanButtons, tbL.rest UNTIL tbL = NIL DO
bt: TiogaButton;
IF NOT msgSet.Equal[TiogaButtons.GetRope[tbL.first], FALSE] THEN
{ prevBtL ¬ tbL; LOOP };
take this one off the orphan list
bt ¬ tbL.first;
IF prevBtL = NIL THEN wH.orphanButtons ¬ tbL.rest
ELSE prevBtL.rest ¬ tbL.rest;
msbH.allButtons ¬ CONS[bt, msbH.allButtons];
TiogaButtons.ChangeButtonLooks[button: bt, removeLooks: nonExistentMsgSetLooks];
prevBtL ¬ tbL;
ENDLOOP;
};
DeselectMsgSet: PROC[msbH: MsgSetButton] = {
wH: WalnutHandle = msbH.wH;
msb: MsgSetButton ¬ wH.selectedMsgSetButtons;
FOR msb2: MsgSetButton ¬ msb, msb2.selected UNTIL msb2=NIL DO
IF msb2 = msbH THEN {  -- found on list so take off
msb.selected ¬ msb2.selected;
IF msbH = wH.selectedMsgSetButtons THEN
wH.selectedMsgSetButtons ¬ msbH.selected;
msbH.selected ¬ NIL;
FOR tbL: LIST OF TiogaButton ¬ msb2.allButtons, tbL.rest UNTIL tbL = NIL DO
TiogaButtons.ChangeButtonLooks[
button: tbL.first,
addLooks: msbDefaultLooks,
removeLooks: msbSelectedLooks
];
ENDLOOP;
Fix up the icon, if necessary
FixIconAndLabel[wH];
RETURN
};
msb ¬ msb2;
ENDLOOP;
};
MSDisplay: PROC[msb: MsgSetButton, shift: BOOL] = {
msV: Viewer = msb.msViewer;
IF (msV # NIL) AND ~msV.destroyed THEN
{ IF msV.iconic THEN ViewerOps.OpenIcon[msV, shift]
ELSE IF shift THEN ViewerOps.GrowViewer[msV]
ELSE ViewerOps.PaintViewer[msV, all] }
ELSE [] ¬ QDisplayMsgSet[msb.wH, msb, msV, shift];
};
GetButton: PUBLIC PROC[wH: WalnutHandle, msgSet: ROPE] RETURNS[MsgSetButton] = {
FOR msb: MsgSetButton ¬ wH.firstMsgSetButton, msb.next UNTIL msb = NIL DO
IF Rope.Equal[msb.msgSet.name, msgSet, FALSE] THEN RETURN[msb];
ENDLOOP;
RETURN[NIL];
};
PrepareToEditMsgSetButtons: PUBLIC PROC[wH: WalnutHandle] = {
FOR msb: MsgSetButton ¬ wH.selectedMsgSetButtons, msb.next UNTIL msb = NIL DO
FOR tb: LIST OF TiogaButton ¬ msb.allButtons, tb.rest UNTIL tb = NIL DO
TiogaButtons.ChangeButtonLooks[button: tb.first, removeLooks: msbSelectedLooks];
ENDLOOP;
ENDLOOP;
FOR tbL: LIST OF TiogaButton ¬ wH.orphanButtons, tbL.rest UNTIL tbL = NIL DO
TiogaButtons.ChangeButtonLooks[button: tbL.first, removeLooks: nonExistentMsgSetLooks];
ENDLOOP;
TiogaButtons.SetState[wH.msgSetsTViewer, editing];
};
whitespace: ROPE = " \t\n\r\l";
BuildMsgSetButtons: TiogaButtons.WasModifiedProc ~ {
PROC [v: ViewerClasses.Viewer]
wH: WalnutHandle = NARROW[ViewerOps.FetchProp[v, $WalnutHandle]];
DoBuildMsgSetButtons[wH, WalnutOps.MsgSetNames[wH.opsH].mL ];
};
DoBuildMsgSetButtons: PROC[wH: WalnutHandle, msList: LIST OF ROPE] = {
root: Tioga.Node = NARROW[wH.msgSetsTViewer.data, TEditDocument.TEditDocumentData].text;
rope, name: ROPE;
start, end: INT;
length: INT;
prevMsb: MsgSetButton ¬ NIL;
msbH: MsgSetButton;
wH.orphanButtons ¬ NIL;
wH.firstMsgSetButton ¬ NIL;
create the MsgSetButtons from the list of msgSets
FOR mL: LIST OF ROPE ¬ msList, mL.rest UNTIL mL = NIL DO
name: ROPE = mL.first;
msb: MsgSetButton = NEW[MsgSetButtonObject ¬ [wH: wH, msgSet: [name: name]] ];
IF prevMsb # NIL THEN prevMsb.next ¬ msb
ELSE wH.firstMsgSetButton ¬ msb;
prevMsb ¬ msb;
SELECT TRUE FROM
Rope.Equal[name, WalnutOps.ActiveName, FALSE] => wH.activeMsgSetButton ¬ msb;
Rope.Equal[name, WalnutOps.DeletedName, FALSE] => wH.deletedMsgSetButton ¬ msb;
ENDCASE => NULL;
ENDLOOP;
FOR vL: LIST OF Viewer ¬ wH.msgSetViewerList, vL.rest UNTIL vL = NIL DO
msI: MsgSetInfo;
name: ROPE;
msb: MsgSetButton;
IF vL.first.destroyed THEN LOOP;
msI ¬ NARROW[ViewerOps.FetchProp[vL.first, $MsgSetInfo]];
name ¬ msI.button.msgSet.name;
msb ¬ GetButton[wH, name];
msb.msViewer ¬ vL.first;
msI.button ¬ msb;
ENDLOOP;
FOR node: TiogaOps.Ref ¬ TiogaOps.StepForward[root], TiogaOps.StepForward[node] UNTIL node=NIL DO
for each non-comment node, find text strings and turn them into buttons.
IF TiogaOps.IsComment[node] THEN LOOP;
rope ¬ TiogaOps.GetRope[node];
length ¬ Rope.Length[rope];
start ¬ Rope.SkipOver[rope, 0, whitespace];
WHILE start < length DO
tb: TiogaButton;
end ¬ Rope.SkipTo[rope, start+1, whitespace];
name ¬ Rope.Substr[rope, start, end-start];
msbH ¬ GetButton[wH, name];
tb ¬ TBQueue.CreateTiogaButtonFromNode[q: wH.walnutQueue, node: node, start: start, end: end, proc: SelectMsgSetProc, clientData: msbH];
IF msbH # NIL THEN {
msbH.allButtons ¬ CONS[tb, msbH.allButtons];
msbH.mainButton ¬ tb;  -- always the last one
}
ELSE {
TiogaButtons.ChangeButtonLooks[button: tb, addLooks: nonExistentMsgSetLooks];
wH.orphanButtons ¬ CONS[tb, wH.orphanButtons];
};
start ¬ Rope.SkipOver[rope, end, whitespace];
ENDLOOP;
ENDLOOP;
};
MakeMSButton: PROC[wH: WalnutHandle, prevMsb: MsgSetButton, msgSet: ROPE, node: TiogaOps.Ref, start: INT ¬ 0, end: INT ¬ INT.LAST]
RETURNS[msbH: MsgSetButton] = {
assumes msgSet does not already have a button; adds it after prevMsb
newBt: TiogaButtons.TiogaButton;
msbH ¬ NEW[MsgSetButtonObject ¬ [wH: wH, msgSet: [name: msgSet], next: prevMsb.next] ];
newBt ¬ TBQueue.CreateTiogaButtonFromNode[q: wH.walnutQueue, node: node, start: start, end: end, proc: SelectMsgSetProc, clientData: msbH];
msbH.mainButton ¬ newBt;
msbH.allButtons ¬ LIST[newBt];
prevMsb.next ¬ msbH;
};
END.