WalnutMsgSetButtonsImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Willie-Sue, September 18, 1985 10:04:07 am PDT
Pavel, February 7, 1986 11:43:05 am PST
Contents: co-ordinates MsgSet button handling for WalnutControlWindow
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],
Rope USING [ROPE, Cat, Compare, Equal, Length],
TBQueue USING [AppendToTiogaButton],
TiogaButtons USING [ CreateButton,
TiogaButtonProc, TiogaButton,
AppendToButton, ChangeButtonLooks, CreateViewer, DeleteButton],
TiogaOps USING [InsertRope],
UserProfile USING [Number, Token],
VFonts USING [StringWidth],
ViewerOps USING [ComputeColumn, DestroyViewer, GrowViewer, MoveViewer,
OpenIcon, PaintViewer, SetOpenHeight],
ViewerClasses USING [Viewer],
ViewerSpecs USING [openLeftWidth, openRightWidth, scrollBarW],
WalnutOps,
WalnutDisplayerInternal USING [QDisplayMsgSet, displayMsgSetInIcon],
WalnutWindow USING [],
WalnutWindowInternal USING[walnut, walnutTS, walnutRulerBefore, walnutRulerAfter, MsgSetButton, MsgSetButtonObject, walnutQueue,
walnutIcon, newMailIcon,
labelledWalnutIcon, labelledNewMailIcon,
unLabelledWalnutIcon, unLabelledNewMailIcon];
WalnutMsgSetButtonsImpl: CEDAR PROGRAM
IMPORTS
Rope, UserProfile,
Containers, TBQueue, TiogaButtons, TiogaOps, VFonts, ViewerOps, ViewerSpecs,
WalnutOps, WalnutDisplayerInternal, WalnutWindowInternal
EXPORTS WalnutWindow, WalnutWindowInternal =
BEGIN OPEN WalnutDisplayerInternal, WalnutWindowInternal;
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
TiogaButton: TYPE = TiogaButtons.TiogaButton;
MsgSet: TYPE = WalnutOps.MsgSet;
msgSetsVersion: PUBLIC INT ← -1;
msgSetsTViewer: PUBLIC Viewer ← NIL;
msbDefaultLooks: PUBLIC ROPE
UserProfile.Token[key: "Walnut.MsgSetButtonDefaultLooks", default: ""];
msbSelectedLooks: PUBLIC ROPE
UserProfile.Token[key: "Walnut.MsgSetButtonSelectedLooks", default: "bi"];
lineHeight: INT ← 12;
maxLines: INT ← 10;
spaceWidth: INT ← VFonts.StringWidth["P"];
selectedMsgSetButtons: PUBLIC MsgSetButton ← NIL; -- List of selected MsgSets (may be empty)
firstMsgSetButton: PUBLIC MsgSetButton ← NIL;   -- head of List of MsgSets (never empty
deletedMsgSetButton: PUBLIC MsgSetButton ← NIL; -- The message set button for Deleted
activeMsgSetButton: PUBLIC MsgSetButton ← NIL; -- The message set button for Active
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 THEN deselect all & select this one; IF yellow then display;
IF blue THEN toggle whether selected or not
button: TiogaButtons.TiogaButton ← NARROW[parent];
msbH: MsgSetButton = NARROW[clientData];
msb, msb2: MsgSetButton;
IF (mouseButton = red) AND shift THEN {
TiogaOps.InsertRope[msbH.msgSet.name];
RETURN;
};
IF mouseButton = yellow THEN {
MSDisplay[msbH, shift];
RETURN;
};
IF mouseButton = red THEN {  -- deselect any selected
IF (msb ← selectedMsgSetButtons) # NIL THEN {
selectedMsgSetButtons ← NIL;
DO
TiogaButtons.ChangeButtonLooks[
button: msb.button,
addLooks: msbDefaultLooks,
removeLooks: msbSelectedLooks
];
IF (msb2 ← msb.selected) = NIL THEN
EXIT;
msb.selected ← NIL;
msb ← msb2;
ENDLOOP;
}
}
ELSE { -- blue button, toggle the state of this msgSet
FOR msb ← 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 ← selectedMsgSetButtons;
};
Here we actually select the given button
selectedMsgSetButtons ← msbH;
TiogaButtons.ChangeButtonLooks[
button: button,
addLooks: msbSelectedLooks,
removeLooks: msbDefaultLooks
];
Fix up the icon, if necessary
FixIconAndLabel[];
};
FixIconAndLabel: PROC = {
-- This procedure does not repaint the icon, since most callers won't want that
IF displayMsgSetInIcon THEN {
IF selectedMsgSetButtons = NIL THEN {
walnut.label ← NIL;
walnutIcon ← unLabelledWalnutIcon;
newMailIcon ← unLabelledNewMailIcon;
}
ELSE {
label : ROPE ← selectedMsgSetButtons.msgSet.name;
FOR msb: MsgSetButton ← selectedMsgSetButtons.selected, msb.selected
UNTIL msb = NIL DO
label ← Rope.Cat[label, ", ", msb.msgSet.name];
ENDLOOP;
walnut.label ← label;
walnutIcon ← labelledWalnutIcon;
newMailIcon ← labelledNewMailIcon;
};
IF walnut.icon = labelledWalnutIcon OR walnut.icon = unLabelledWalnutIcon THEN
walnut.icon ← walnutIcon
ELSE
walnut.icon ← newMailIcon;
};
};
ShowMsgSetButtons: PUBLIC PROC = {
msL: LIST OF ROPE;
bt: TiogaButtons.TiogaButton;
msb: MsgSetButton;
first: BOOLTRUE;
ms: ROPE;
visibleSpace: INT;
version, numMsgSets: INT;
[version, numMsgSets] ← WalnutOps.MsgSetsInfo[];
IF firstMsgSetButton # NIL THEN
IF version = msgSetsVersion THEN {
-- check for changed, displayed msgSets
FOR msb: MsgSetButton ← 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 [] ← WalnutDisplayerInternal.QDisplayMsgSet[msb: msb]
}
ELSE msb.msgSet.version ← -1;  -- might have been displayed once
ENDLOOP;
RETURN;
};
[msL, msgSetsVersion] ← WalnutOps.MsgSetNames[alphaOrder: TRUE];
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 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 ← (totalCharWidth/colWidth) + 1;
IF numLines <= 0 THEN numLines ← 1
ELSE IF numLines > maxLines THEN numLines ← maxLines;
};
visibleSpace ← numLines * lineHeight + 1;
END;
IF msgSetsTViewer # NIL THEN DestroyAllMsgSetButtons[]; -- start from scratch; it's easier
msgSetsTViewer ← TiogaButtons.CreateViewer[
info: [wy: walnutRulerBefore.wy + 2, ww: walnut.ww,
wh: visibleSpace, parent: walnut, border: FALSE] ];
Containers.ChildXBound[walnut, msgSetsTViewer];
IF walnutRulerAfter.wy # walnutRulerBefore.wy + visibleSpace THEN {
ViewerOps.MoveViewer[walnutRulerAfter, walnutRulerAfter.wx,
walnutRulerBefore.wy+walnutRulerBefore.wh+visibleSpace,
walnutRulerAfter.ww, walnutRulerAfter.wh, FALSE];
ViewerOps.MoveViewer[walnutTS, walnutTS.wx,
walnutRulerAfter.wy+walnutRulerAfter.wh+1, walnutTS.ww,
walnutTS.wh, FALSE];
ViewerOps.SetOpenHeight[msgSetsTViewer, visibleSpace];
ViewerOps.ComputeColumn[walnut.column];
};
msb← firstMsgSetButton ←
NEW[MsgSetButtonObject ← [ msgSet: WalnutOps.MsgSet[name: " "]]];
bt ← TiogaButtons.CreateButton[
viewer: msgSetsTViewer, rope: " ", clientData: firstMsgSetButton];
firstMsgSetButton.button ← bt;
msb ← activeMsgSetButton ← AddMSButton[msb, WalnutOps.ActiveMsgSetName, FALSE];
msb ← deletedMsgSetButton← AddMSButton[msb, WalnutOps.DeletedMsgSetName];
FOR msL2: LIST OF ROPE ← msL, msL2.rest UNTIL msL2 = NIL DO
IF ~Rope.Equal[(ms ← msL2.first), WalnutOps.ActiveMsgSetName, FALSE] AND
~Rope.Equal[ms, WalnutOps.DeletedMsgSetName, FALSE] THEN
msb ← AddMSButton[msb, ms];
ENDLOOP;
};
GetSelectedMsgSets: PUBLIC PROC RETURNS[msL: LIST OF MsgSetButton] = {
FOR msB: MsgSetButton ← selectedMsgSetButtons, msB.selected UNTIL msB=NIL DO
msL ← CONS[msB, msL];
ENDLOOP;
};
AddMsgSetButton: PUBLIC PROC[msgSet: Rope.ROPE, select: BOOL] = {
thisMsb: MsgSetButton;
prevMsb: MsgSetButton ← deletedMsgSetButton;
DO
nextMsb: MsgSetButton ← prevMsb.next;
IF nextMsb = NIL THEN
{ thisMsb ← AddMSButton[prevMsb, msgSet]; EXIT};
IF Rope.Compare[msgSet, nextMsb.msgSet.name] = less THEN
{thisMsb ← AddMSButton[prevMsb, msgSet]; EXIT};
prevMsb ← nextMsb;
ENDLOOP;
IF select THEN SelectMsgSetProc[  -- red-select new MsgSet
parent: thisMsb.button, clientData: thisMsb];
};
DeleteMsgSetButton: PUBLIC PROC[msgSet: ROPE] = {
if msgSet is Active, just destroy viewer, but not button
prevMsb: MsgSetButton ← firstMsgSetButton;
FOR msb: MsgSetButton ← 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.ActiveMsgSetName] THEN
{ msb.msViewer ← NIL; RETURN};
DeselectMsgSet[msb];
prevMsb.next ← msb.next;
TiogaButtons.DeleteButton[msb.spaceButton];
TiogaButtons.DeleteButton[msb.button];
RETURN
};
prevMsb ← msb;
ENDLOOP;
};
DestroyAllMsgSetButtons: PUBLIC PROC = {
selectedMsgSetButtons ← NIL;
FOR msb: MsgSetButton ← firstMsgSetButton, msb.next UNTIL msb=NIL DO
IF msb.msViewer # NIL THEN ViewerOps.DestroyViewer[msb.msViewer];
msb.msViewer ← NIL;
ENDLOOP;
firstMsgSetButton ← NIL;
ViewerOps.MoveViewer[walnutRulerAfter, walnutRulerAfter.wx,
walnutRulerBefore.wy+walnutRulerBefore.wh+14+2, walnutRulerAfter.ww,
walnutRulerAfter.wh, FALSE];
ViewerOps.MoveViewer[walnutTS, walnutTS.wx,
walnutRulerAfter.wy+walnutRulerAfter.wh+1, walnutTS.ww,
walnutTS.wh, FALSE];
ViewerOps.DestroyViewer[msgSetsTViewer, FALSE];
msgSetsTViewer ← NIL;
ViewerOps.ComputeColumn[walnut.column];
ViewerOps.PaintViewer[walnut, client];
};
SelectMsgSetsFromMSNames: PUBLIC PROC [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: BOOLEANTRUE;
IF walnut = NIL THEN RETURN[msNames];
FOR msNameList: LIST OF ROPE ← msNames, msNameList.rest UNTIL msNameList = NIL DO
msb: MsgSetButton ← GetButton[msNameList.first];
IF msb = NIL THEN {
notFound ← CONS[msNameList.first, notFound];
LOOP
};
SelectMsgSetProc[
parent: msb.button,
mouseButton: IF first THEN red ELSE blue,
clientData: msb];
first ← FALSE;
ENDLOOP;
IF displayMsgSetInIcon AND walnut.iconic THEN
ViewerOps.PaintViewer[walnut, all];
};
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
AddMSButton: PROC[prevMsb: MsgSetButton, msgSet: ROPE, needsSpace: BOOLTRUE]
RETURNS[msbH: MsgSetButton] = {
assumes msgSet does not already have a button; adds it after prevMsb
spaceBt, newBt: TiogaButtons.TiogaButton;
IF needsSpace THEN
spaceBt ← TiogaButtons.AppendToButton[button: prevMsb.button, rope: " "]
ELSE spaceBt ← prevMsb.button;
msbH ← NEW[MsgSetButtonObject ← [msgSet: [name: msgSet], next: prevMsb.next] ];
newBt ← TBQueue.AppendToTiogaButton[
q: walnutQueue, button: spaceBt, rope: msgSet,
looks: msbDefaultLooks, proc: SelectMsgSetProc, clientData: msbH];
msbH.spaceButton ← spaceBt;
msbH.button ← newBt;
prevMsb.next ← msbH;
};
DeselectMsgSet: PROC[msbH: MsgSetButton] = {
msb: MsgSetButton ← 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 = selectedMsgSetButtons THEN
selectedMsgSetButtons ← msbH.selected;
msbH.selected ← NIL;
TiogaButtons.ChangeButtonLooks[
button: msbH.button,
addLooks: msbDefaultLooks,
removeLooks: msbSelectedLooks
];
Fix up the icon, if necessary
FixIconAndLabel[];
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 [] ← WalnutDisplayerInternal.QDisplayMsgSet[msb, msV, shift];
};
GetButton: PUBLIC PROC[msgSet: ROPE] RETURNS[MsgSetButton] = {
FOR msb: MsgSetButton ← firstMsgSetButton, msb.next UNTIL msb = NIL DO
IF Rope.Equal[msb.msgSet.name, msgSet] THEN RETURN[msb];
ENDLOOP;
RETURN[NIL];
};
END.