Output of dynapix.c
The beginning of the program is pretty much as expected. After the toolkit is initialized, the MainWindow and the
MenuBar are created the same way as in the previous examples. Just after the MenuBar is created, however, we make
the following calls:
if (widget = XtNameToWidget (menubar, "button_2"))
XtVaSetValues(menubar, XmNmenuHelpWidget, widget, NULL);
The purpose of these statements is to inform the MenuBar which of its CascadeButtons contains the Help menu.
Setting the MenuBar's XmNmenuHelpWidget resource to the CascadeButton returned by XtNameToWidget()
causes the MenuBar to position the menu specially. The Help menu is placed at the far right on the MenuBar; this
position is necessary for the application to conform to Motif sytle guidelines. For details on how to support a help
system, see Chapter 7, Custom Dialogs, and Chapter 21, Advanced Dialog Programming.
PulldownMenus are created next in the expected manner. The only variation is for the Edit menu, where each item in
the menu represents a color. Since only one color can be used at a time, the color that is currently being used is
marked with a diamond−shape indicator. In order to get this radio−box behavior, each menu item in the
PulldownMenu is a XmVaRADIOBUTTON and the menu is told to treat the items as a RadioBox. The analogy is that
of an old car radio, where selecting a new station causes the other selectors to pop out. Just as you can only have the
radio tuned to one station at a time, you may only have one color set at a time. The RadioBox functionality is
managed automatically by the RowColumn widget that is used to implement the PulldownMenu. Setting the
XmNradioBehavior and XmN-radioAlwaysOne RowColumn resources to True provides the RadioBox
behavior. See Chapter 11, Labels and Buttons, for a complete description and further examples of this type of
behavior. the figure shows the RadioBox−style Edit menu.
5 The Main Window 5.2.3 A Sample Application
97
The Edit menu for dynapix.c
Although the RowColumn manages the RadioBox automatically, we need to turn the radio on by setting the initial
color. After the PulldownMenu is created, the menu (RadioBox) is initialized so that its first item is selected, since we
know that we are using black as the initial color. XtNameToWidget() is used again to get the appropriate button
from the menu. Since the menu items were created using XmVaRADIOBUTTON, the widget that is returned is a
ToggleButton. The XmNset resource is used to turn the button on. Once the menu has been initialized, the Motif
toolkit handles everything automatically.
Note that when we create the Help menu, there is only one item in the menu. You might think that it is redundant to
have a single Help item in the Help menu, but this design is an element of Motif style. The Motif Style Guide states
that items on the MenuBar should always post PulldownMenu, not perform application actions directly.
It is important to note that XmVaCreateSimplePulldownMenu() returns the RowColumn widget that contains
the items in the menu, even though the routine creates both the RowColumn widget and its MenuShell parent. The
routine does not return the MenuShell widget that is actually popped up and down when the menu posted. To get a
handle to that widget, you need to use XtParent() on the RowColumn widget. This design makes sense, since you
need access to the RowColumn widget much more often than you need access to the MenuShell.
Once all of the items have been installed, the MenuBar is managed using XtManageChild(). The approach to
creating MenuBars, PulldownMenus, menu items, and their associated callback routines that we have described here
is meant to be simple and straightforward. In some cases, you may find that these techniques are too limiting. For
example, you cannot specify different callback routines for different items in the same menu, you cannot pass
different client data for different items, and you cannot name the widgets individually. The most inconvenient aspect
of this method, however, is that it requires so much redundant code in order to build a realistically sized MenuBar.
Our intent here is to introduce the basic concepts of menus and to demonstrate the recommended design approach for
applications. We describe how the menu creation process can be generalized for large menu systems in Chapter 15,
Menus.
The rest of the source code is composed of callback routines that are used by the PulldownMenu items. For example,
when the user selects either of the items in the File menu, the function file_cb() is called. If the Quit item is
selected, the -client_data parameter is 1 and the program exits. If the Open item is selected, client_data is
0 and a FileSelectionDialog is popped up to allow the user to select a new bitmap file. The dialog is created using the
convenience routine XmCreateFileSelectionDialog(), which produces the results shown in the figure. Two
callback routines are installed for the dialog: load_pixmap(), which is called when the user presses the OK
5 The Main Window 5.2.3 A Sample Application
98
button, and -XtUnmanageChild(), which is called when the user selects the Cancel button. For more detailed
information on the FileSelectionDialog, see Chapter 6, Selection Dialogs.
The load_pixmap() function loads a new bitmap from a file and displays it in the Label widget. This function uses
the same method for loading a pixmap as was used earlier in main(). Since the function is invoked as a callback by
the FileSelectionDialog, we need to get the value of the file selection. The value is taken from the value field of the
FileSelectionDialog's callback structure, XmFileSelectionBoxCallbackStruct. Since the filename is
represented as a compound string, it must be converted to a character string. The conversion is done using
XmStringGetLtoR(), which creates a regular C string for use by XmGetPixmap(). The load_pixmap()
routine is also called directly from change_color(), so we need to check the call_data parameter. This
parameter is NULL if the routine is not invoked as a callback.
If XmGetPixmap() succeeds, we get the old pixmap and destroy it using XmDestroyPixmap() before we install
the new pixmap. XmGetPixmap() loads and caches a pixmap. If the function is called more than once for a given
image, it returns the cached image, which saves space because a new version of the pixmap is not allocated for each
call. XmDestroyPixmap() decrements the reference count for the image; if the reference count reaches to zero, the
pixmap is actually destroyed. Otherwise, another reference to it may exist, so nothing is done. It is important to use
these two functions in conjunction with each other. However, if you use other pixmap−loading functions to create
pixmaps, you cannot use XmDestroyPixmap() to free them.
The FileSelectionDialog for dynapix.c
The function change_color() is used as the callback routine for items in the Edit menu. The names of the colors
are stored in the colors array. The index of a color in this array is the same as the index of the corresponding menu
item in the menu. The color name is parsed and loaded using XAllocNamedColor(), provided that the string
exists in the RGB database (usually /usr/lib/X11/rgb.txt). If the routine is successful, it returns a non−zero status and
the XColor structure is filled with the RGB data and pixel value. In this case, load_pixmap() is called to reload
the pixmap with the new color. If XAllocNamedColor() returns zero, or if the returned pixel value is the same as
5 The Main Window 5.2.3 A Sample Application
99
Get Volume 6A: Motif Programming Manual now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.