By Mike Craven
Creating Custom Popup Menus in Word 97
The most notable new feature of Word 97 is the incorporation of the Visual Basic for Applications programming language, better known as VBA, replacing the aging WordBasic macro language. This article shows you how easy it can be to customize Word in powerful ways; it demonstrates how to build two types of custom popup menus in VBA: a popup style menu, and a right-click popup thesaurus. More importantly, it demonstrates how Word lends itself to the creation of such popup menus by providing all the necessary commands and sample code in the VBA help file that ships with Word 97.
It doesn't take a lot of programming experience to work with Word macros. I'm a biochemist by trade and have never taken a computer programming class. I happen to do a lot of work with Word and wanted to make it work "my way" instead of the way it works by default. You will need some experience in writing macros to follow along with the examples provided here, but you don't have to be a certified computer programmer.
Microsoft has really outdone themselves when it comes to user customization of Word 97. Customizing toolbars and menus — now called CommandBars — is one of Word's strengths. Microsoft has made it easy to create powerful fly-out menus, and even right-click popup menus. In this article, we'll create popup style and thesaurus menus, but you can easily imagine other uses for the popups, such as displaying a list of recently-used fonts, word counts, or even document statistics. The list of possibilities goes on and on.
Popup menus.
FIGURES 1 and 2 show typical popup style and right-click popup thesaurus menus. The popup style menu can be used as a powerful replacement or addition to Word's own style list on the Formatting toolbar. The problem with Word's default style list, is that it can take quite a bit of time to assemble and present all the styles while working in a complex document. Also, the list isn't in alphabetical order, so the styles can be disorganized.
FIGURE 1: Example of a popup style menu.
FIGURE 2: Example of a right-click popup thesaurus menu.
The custom popup style menu you're about to create can make managing styles a lot easier and faster by displaying them in an organized, non-formatted, alphabetical list. You can use the custom popup style menu to list the styles in your documents by many different categories, including: In-Use, User-Defined, Built-In, and/or All-Styles. You can even organize the list by the family the styles are based on, e.g. Heading styles or List styles.
Our second popup, the right-click thesaurus, is a nice example of a popup menu you can build; it will select a word, look it up in Word's proofing dictionary, and return a display of synonyms, antonyms, alternative word meanings, and related words.
While it's not within the scope of this article to walk you step-by-step through building an exact replica of the menus shown, I will point you in the direction you need to get started building your own custom popups in Word and give you some example code where necessary. Microsoft has most graciously provided us with all the commands, objects, and properties required to build the menus.
Microsoft was even good enough to provide sample code that can be used as a starting point to build your own custom popup menus. All you need to do is take a look in the Microsoft Word Visual Basic help file (VBAWRD8.HLP); everything you need to build the popup menus is right at your fingertips.
Creating a Popup Style Menu
The place to start is in online VBA help. Two help topics — "Styles collection object" and "Style object" — list all the properties and methods that accompany the Style object in Word. Here you will find almost everything you need to build the popup style menu. You'll even find well written examples to guide you. Let's take a look at a few of the Style object properties and their examples.
The BuiltIn property. This property returns True if the specified style in the Styles collection is a built-in style in Word. Here is an example straight from the VBA help file that shows you how easy it is to obtain a list of built-in styles:
For Each sty in ActiveDocument.Styles
If sty.BuiltIn = True Then
Msgbox sty.NameLocal
End If
Next
The InUse property. This property returns True if the specified style in the Styles collection is a built-in style that has been modified or applied in the document or a new style that has been created in the document. See FIGURE 3 for an example straight from the VBA help file that shows you how to get a list of styles currently "In-Use" from the active document.
Set mydoc = ActiveDocument
msg = "Styles in use:" & vbCr
For Each sty In mydoc.Styles
If sty.InUse = True Then
With mydoc.Content.find
.ClearFormatting
.Text = ""
.Style = sty
.Execute Format:=True
If .Found = True Then
msg = msg & sty & vbCr
End If
End With
End If
Next sty
MsgBox msg
FIGURE 3: The code to get a list of styles currently "In-Use" from the active document.
Getting all styles. From the BuiltIn property example shown previously, you can get the names of all styles in Word using:
For Each sty In ActiveDocument.Styles
Msgbox sty.NameLocal
Next
Getting user-defined styles
With what you have learned so far, you can now quite easily get User-Defined styles by grabbing all styles and then checking that the BuiltIn property is False for the indicated style. Simple indeed!
For Each sty In ActiveDocument.Styles
If sty.BuiltIn = False Then
Msgbox sty.NameLocal
End If
Next
Getting help.
The Word 97 help files walk you through most of the "dirty work," providing you with the commands and examples you need to put the style-retrieval code mentioned previously onto a popup menu. The help topics I used to build the example popup menus shown in FIGURES 1 and 2 are "Adding and displaying shortcut menus," "Adding and modifying toolbars," "Adding and managing menu bars and menu items," and "Overview of command bars." Just open the Word/VBA help file from the Visual Basic Editor, search for these topics, and you'll be well on your way to completing your own custom popup style menu.
Getting Started
A popup menu is basically a floating CommandBar that the user displays by right-clicking (or, in our case, by clicking on a toolbar button). It can contain the same control types as a CommandBar, and the controls behave in the same way as on a CommandBar. The only difference between popup menus and other toolbars is that when you create a popup menu with the Add method, you must specify msoBarPopUp as the Position argument for the CommandBar. FIGURE 4 (again, derived from Word 97 help) will create a popup menu that displays, when clicking on a toolbar button, that the OnAction property has been set to run the AccessStyleMenu macro.
Public Sub AccessStyleMenu()
Dim StyleMenu As CommandBar
Dim StyleMenuBtn As CommandBarButton
Set StyleMenu = CommandBars.Add (Name:="StyleMenu", _
Position:=msoBarPopup)
' Build your custom style menu here.
With StyleMenu.Controls.Add (Type:=msoControlPopup)
.Caption = "&All Styles"
.OnAction = "AccessAllStylesMenu"
End With
Set StyleMenuBtn = CommandBars.ActionControl
StyleMenu.ShowPopup StyleMenuBtn.Left, _
StyleMenuBtn.Top + StyleMenuBtn.Height
End Sub
FIGURE 4: The code to create a popup menu which shows that the OnAction property has been set to run the AccessStyleMenu macro.
Following through the macro, you first create the popup style menu itself using the Add method of the CommandBars collection. Once the style menu CommandBar has been created, you can build the actual menu by adding any items you wish to the popup menu. For the sake of simplicity, I have only shown the addition of a single fly-out control here for listing all styles. The OnAction property for the new fly-out control is set to run a separate macro titled AccessAllStylesMenu whenever the fly-out control is activated by moving the mouse pointer over it. FIGURE 5 shows AccessAllStylesMenu.
Public Sub AccessAllStylesMenu()
Dim sty As Style
Dim Ctrl As CommandBarControl
Dim StylePopupMenu As CommandBarPopup
System.Cursor = wdCursorWait
' Get handle to popup style menu.
Set StylePopupMenu = CommandBars.ActionControl
' Remove any existing styles from the popup menu.
For Each Ctrl In StylePopupMenu.Controls
Ctrl.Delete
Next Ctrl
' Add all styles to the popup menu.
For Each sty In ActiveDocument.Styles
With StylePopupMenu.Controls.Add _
(Type:=msoControlButton, ID:=1)
.Caption = sty.NameLocal
End With
Next
System.Cursor = wdCursorNormal
End Sub
FIGURE 5: An example of how the code for the AccessAllStylesMenu macro is derived from the help file.
Now, you'll need to display the popup menu/CommandBar you've just built. This is done using the ShowPopup method of the CommandBars collection as shown in the AccessAllStylesMenu macro example. A little-known trick is used here to ensure the popup menu displays in the proper position; you can return the object of the CommandBar button that was clicked (i.e. the button you clicked to produce the popup menu) by using the ActionControl property of the CommandBars collection. From here, you can use the Left, Top, and Height properties as shown, to properly position the popup style menu over the top of the CommandBar button which was used to activate the menu.
Following the logic of the code, you click on a toolbar button whose OnAction property is set to run the AccessStyleMenu macro shown previously. This will, in turn, display the popup style menu you've just built, showing a single fly-out menu titled All Styles. When you move your mouse pointer over the All Styles fly-out control, this will cause the AccessAllStylesMenu macro to execute and a second menu will pop up listing all styles available to Word through the active document.
You may be saying to yourself, "Okay, but how do I create the initial button which activates my new popup style menu that I have just built?" Taking a look through the "Adding and modifying toolbars" help topic in the Word/VBA help file, we can easily derive a routine that adds a button for your popup menu to Word's Formatting toolbar. The macro in FIGURE 6 will create a button using Word's built-in Format/Style button image and place the button in the first position of Word's built-in Formatting toolbar. When clicked, the button will run our AccessStyleMenu macro, shown previously.
Public Sub AddPopupStyleMenuBtn()
Dim FormatTb As CommandBar
Dim PopupStyleMenuBtn As CommandBarButton
Set FormatTb = CommandBars("Formatting")
Set PopupStyleMenuBtn = _
FormatTb.Controls.Add(Type:=msoControlButton, _
Before:=1)
With PopupStyleMenuBtn
.Caption = "&Style Menu"
.OnAction = "AccessStyleMenu"
.FaceId = 254 ' Style button image.
End With
End Sub
FIGURE 6: A macro to create a button to run the AccessStyleMenu macro.
The Style.Description property. The Description property returns the description of any given Style object. For example, a typical description for the Heading 2 style might be "Normal + Font: Arial, 12 pt, Bold, Italic, Space Before 12 pt After 3 pt, KeepWithNext, Level 2." Here's an example straight from the help file:
Dim sty As Style
Set myDoc = ActiveDocument
Set newDoc = Documents.Add
For Each sty In myDoc.Styles
With newDoc.Range
.InsertAfter Text:=sty.NameLocal & Chr(9) & _
sty.Description
.InsertParagraphAfter
End With
Next sty
This example can be easily adapted to work with your custom popup style menu by modifying the Caption property of the AccessAllStylesMenu macro as follows:
.Caption = sty.NameLocal & " - " & sty.Description
The Style.Type property. The Type property returns an indicated Style object's type. The type can be either of two constants: wdStyleTypeParagraph or wdStyleTypeCharacter. This property can be used to indicate the type of style listed on your popup style menu. You can use the built-in button face ID numbers 948 and 947 to graphically show on your popup menu the type of style being listed, just as Microsoft does in their own Style dialog box (accessed by selecting Format | Style).
In the AccessAllStylesMenu example macro, add the following line to the macro just below the line that starts with .Caption = but before End With:
.FaceId = IIf(sty.Type = wdStyleTypeParagraph, 948, 947)
This code is just a way of saying: "If the style being added to the fly-out menu is a paragraph-based style, then use button face ID number 948 on the menu. Otherwise, it's a character-based style, so use button face ID number 947 on the menu."
Indicating the current style. You can also indicate the style currently in use at the insertion point (or in the current text selection) on your popup menu by setting the CommandBarButton object's State property to msoButtonDown. This will depress the button for the style in use and make it stand out from the others listed on your popup style menu (again, see FIGURE 1). The current style can be easily obtained using the Selection property.
In the AccessAllStylesMenu sample macro, add the following lines to the macro just below the line that starts with .Caption = but before End With:
On Error Resume Next
If sty.NameLocal = Selection.Style Then
.State = msoButtonDown
On Error Goto 0
...
The On Error statements are used to trap generated error and bypass the line of code should multiple styles exist in the current text selection.
Inserting a selected style. Now that you have your style menu built, how do we convert it from a popup menu that just lists various types of styles by their names into one that will actually change the selected text in our document to the style that you've just selected from the popup menu's style list?
Just add the following lines of code to the AccessAllStylesMenu macro anywhere after the line that starts with .Caption = but before End With:
.Tag = sty.NameLocal
.OnAction = "SetStyleToTagName"
This will set the Tag property for each style added to the All Styles fly-out menu to the name of the style. At the same time, it will tell the indicated styles button to run the following macro when it's clicked upon with a mouse:
Public Sub SetStyleToTagName()
Dim SelectedStyle As CommandBarButton
Set SelectedStyle = CommandBars.ActionControl
Selection.Style = _
ActiveDocument.Styles(SelectedStyle.Tag)
End Sub
This macro will take the current selection for the active document and format it with the selected style name that it grabs from the clicked control's Tag property.
Adding a Fly-out MenuThe Manage Styles fly-out menu shown on the popup style menu example in FIGURE 1 is really just a bunch of commands already built into Word. I simply added them to the menu so they're more readily accessible to the user. There are no secrets or tricks; all the commands are built into Word. All you have to do is assemble them on the popup menu as you desire.
As an example, here's some code straight from the VBA help file for deleting styles that aren't built into Word:
Dim sty As Style
For Each sty In ActiveDocument.Styles
If sty.BuiltIn = False Then
sty.Delete
End If
Next sty
The example can be easily modified to delete unused styles in the active document:
Dim sty As Style
For Each sty In ActiveDocument.Styles
If sty.InUse = False Then
sty.Delete
End If
Next sty
So there you have it. We've managed to build a usable popup style menu using only four small macros.
Building a Popup Thesaurus MenuThe idea behind having a right-click popup thesaurus menu in Word is not a unique one. For example, Alki Software had a right-click popup thesaurus menu in their Comprehensive Thesaurus add-in package way back in Word 6.0. For some strange reason, Alki dropped the popup thesaurus feature from their Word 95 version of the package. Fortunately for us however, Microsoft has made it easy to build our own in Word 97.
The concepts behind creating a custom popup thesaurus in Word 97 are basically the same as they were for the popup style menu example we just went through, with only minor differences. Microsoft provides all the commands, objects, and properties required to build a really nice popup thesaurus, just like the one shown in FIGURE 2.
Take a look at the SynonymInfo property in Word/VBA online help. This property returns a SynonymInfo object that contains information from the thesaurus on synonyms, antonyms, or related words and expressions for the specified word or phrase. There are even dozens of examples that will walk you through building your own popup thesaurus menu, such as this one which allows you to get all of the antonyms for the word "big:"
Alist = SynonymInfo(Word:="big", _
LanguageID:=wdEnglishUS).AntonymList
For i = 1 To UBound(Alist)
Msgbox Alist(i)
Next i
And this one under the SynonymList property that shows you how to get a list of synonyms for the word "big," using the meaning "generous:"
Slist = SynonymInfo(Word:="big", _
LanguageID:=wdEnglishUS).SynonymList(Meaning:="generous")
For i = 1 To UBound(Slist)
Msgbox Slist(i)
Next i
Getting Started
The only major difference between building the custom popup thesaurus menu and the custom popup style menu that you have already built is that there's no need to create the main popup menu as a stand-alone CommandBar which displays at the click of a button. This is because you can easily create the thesaurus menu as a custom fly-out off of the right-click Text shortcut menu already built into Word 97. FIGURE 7 demonstrates how you would do it using a macro.
Public Sub AddPopupThesaurusMenu()
Dim ShortCutMenu As CommandBar
Dim ThesaurusMenu As CommandBarPopup
' Get handle to "Text" shortcut menu.
Set ShortCutMenu = CommandBars("Text")
' Add thesaurus popup to the shortcut menu.
Set ThesaurusMenu = ShortCutMenu.Controls.Add( _
Type:=msoControlPopup)
With ThesaurusMenu
.BeginGroup = True
.Caption = "&Thesaurus"
.OnAction = "AccessThesaurusMenu"
End With
End Sub
FIGURE 7: How to create the thesaurus menu as a custom fly-out using a macro.
From here, you would set up your AccessThesaurusMenu and SetSynonymToTagName macros just as you did for the popup style menu example, following the examples given in Word/VBA help (see Listing One on page XX).
Using the same rationale, you can also create a list of antonyms using the AntonymList property, create a list of word meanings using the MeaningList property, and create a list of related words using the RelatedWordList property. Heck, you can even create a parts of speech, or a related expressions list for the selected word using the PartOfSpeechList and RelatedExpressionList properties. Search on "SynonymInfo object" in Word/VBA help for more information.
ConclusionAs you can see, the possibilities for creating popup menus in Microsoft Word 97 are nearly limitless. I have presented you with only two small examples which were derived just from following the example code given in Word/VBA online help. With this knowledge, you can become a master of customizing Word in no time at all.
Download source code for this article here.
Mike Craven is a graduate student at the University of California, Irvine in the field of Molecular Biology and Biochemistry. He creates software for and writes about Word for Windows, and is a leading contributor to the world famous WOPR (Woody's Office POWER Pack) add-in for Microsoft Word. You can reach Mike at mike@wopr.com or download an evaluation version of the WOPR add-in program from http://www.wopr.com.
Begin Listing One — AccessThesaurusMenu and SetSynonymToTagName
Private Sub AccessThesaurusMenu()
Dim i As Long
Dim Slist As Variant
Dim selWord As String
Dim ThesLangID As Long
Dim ThesDict As Variant
Dim LookupWord As SynonymInfo
Dim Ctrl As CommandBarControl
Dim ThesaurusMenu As CommandBarPopup
System.Cursor = wdCursorWait
' Get handle to popup thesaurus menu.
Set ThesaurusMenu = CommandBars.ActionControl
' Remove any existing synonyms from the popup menu.
For Each Ctrl In ThesaurusMenu.Controls
Ctrl.Delete
Next Ctrl
With Selection
.Expand wdWord ' Select insertion point word.
' Deselect any trailing spaces.
Do While Right(.Text, 1) = Chr(32)
.MoveLeft Unit:=wdCharacter, _
Count:=1, Extend:=wdExtend
Loop
selWord$ = Trim(.Text) ' Store selection.
End With
If selWord$ = "" Or selWord$ = vbCr Then ' No selection.
With ThesaurusMenu.Controls.Add( _
Type:=msoControlButton, Temporary:=True)
.FaceId = 463 ' Exclamation icon.
.Caption = "(no selection!)"
End With
Exit Sub
End If
' Get language ID for active thesaurus dictionary.
Set ThesDict = Languages( _
Selection.LanguageID).ActiveThesaurusDictionary
If ThesDict Is Nothing Then
With ThesaurusMenu.Controls.Add( _
Type:=msoControlButton, Temporary:=True)
.FaceId = 1019 ' Cancel icon.
.Caption = "(thesaurus not installed!)"
End With
Exit Sub
End If
ThesLangID = ThesDict.LanguageID
' Lookup the selected word in Word's thesaurus.
Set LookupWord = SynonymInfo(Word:=selWord$, _
LanguageID:=ThesLangID)
' Add synonyms to the popup thesaurus menu if found.
' For simplicity, look up first meaning only.
If LookupWord.Found = True Then ' Synonyms exist.
On Error GoTo NoSynonyms
Slist = LookupWord.SynonymList(1) ' First word.
On Error GoTo 0
For i = 1 To UBound(Slist)
With ThesaurusMenu.Controls.Add( _
Type:=msoControlButton, Temporary:=True)
.Tag = Slist(i)
.Caption = Slist(i)
.OnAction = "SetSynonymToTagName"
End With
Next i
Else
' No synonyms found.
NoSynonyms:
With ThesaurusMenu.Controls.Add( _
Type:=msoControlButton, Temporary:=True)
.FaceId = 487 ' Information icon
.Caption = "(no synonyms found!)"
End With
End If
System.Cursor = wdCursorNormal
End Sub
Private Sub SetSynonymToTagName()
Dim CaseType As Long
Dim ReplaceSelSetting As Boolean
Dim SelectedCtrl As CommandBarButton
' Get handel to selected control.
Set SelectedCtrl = CommandBars.ActionControl
' Store current ReplaceSelection setting.
ReplaceSelSetting = Options.ReplaceSelection
' Force ReplaceSelection to ON.
Options.ReplaceSelection = True
' Store current capitalization state for selection.
CaseType = Selection.Range.Case
With Selection
' Replace selection with accessed control's tag.
.TypeText Text:=SelectedCtrl.Tag
' Find newely inserted text and select it.
With Selection.Find
.ClearFormatting
.Text = SelectedCtrl.Tag
.Wrap = wdFindStop
.Execute Forward:=False
End With
' Restore capitalization state.
.Range.Case = CaseType
' Deselect text.
.EndOf Unit:=wdWord, Extend:=wdMove
End With
' Restore original ReplaceSelection setting.
Options.ReplaceSelection = ReplaceSelSetting
End Sub
End Listing One