<<>> <> <> <> <> <> <> <> <<>> <> <> <> <> <> <> <<(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"]; <> <> <> <> <> <<>> <> <> <> <> <> <> 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]; <> <> <> <> <<~Rope.Equal[ms, WalnutOps.DeletedName, FALSE] THEN>> <> <> }; 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.