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"]; SelectMsgSetProc: TiogaButtons.TiogaButtonProc = { 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; }; 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; 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; 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 msL ¬ RopeList.DRemove[msL, WalnutOps.ActiveName, FALSE]; msL ¬ RopeList.DRemove[msL, WalnutOps.DeletedName, FALSE]; msL ¬ CONS[WalnutOps.ActiveName, CONS[WalnutOps.DeletedName, msL]]; }; 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]; 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]; }; 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] = { 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] = { 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] = { 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 }; 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; 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 ~ { 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; 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 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] = { 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.  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) 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. 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 Here we actually select the tioga buttons for the clicked button Fix up the icon, if necessary 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 change the list so active and deleted are first calculate how big the area for msgSet buttons should be find and replace last node with allMsgSetsNode - such fun. Assumes at least two nodes 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; if msgSet is Active, just destroy viewer, but not button Selects the message set buttons in the Walnut Control viewer which corresponds to the list of msNames given. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * assumes msgSet does not already have a button; adds it after prevMsb take this one off the orphan list Fix up the icon, if necessary PROC [v: ViewerClasses.Viewer] create the MsgSetButtons from the list of msgSets for each non-comment node, find text strings and turn them into buttons. assumes msgSet does not already have a button; adds it after prevMsb Κ0•NewlineDelimiter –(cedarcode) style™codešΟb™Kšœ Οeœ=™HK™'K™(KšΟy&™&K™(K™$K™KšœE™EK™$—K˜Kšœ#™#šœ ™ Kšœ-™-K™+K™JK™eK™`K˜—šΟk ˜ Kšœ  œ˜Kšœ  œ!˜/Kšœ  œ˜%Kš œ œ˜Kš œ˜K˜Kšœ  œ˜!Kšœ œ4˜AKšœ œ˜(Kšœ  œ˜Kšœ  œ ˜.Kšœ œ˜Kšœ  œΌ˜ΞKšœ œ˜!Kšœ  œ4˜BKšœ  œ˜"Kšœ œ˜Kšœ  œt˜ƒKšœ œ ˜Kšœ  œ-˜>Kšœ ˜ Kšœ œs˜‡Kšœ  œ˜Kšœ˜šœ œN˜gK˜——šΠbk‘˜&š ˜Kšœ  œ œ˜0K˜tKšœ˜—Kš œ5˜