Chapter 5. Creating Menus With ViewKit

This chapter introduces the basic ViewKit classes needed to create and manipulate the menus in a ViewKit application. Figure 5-1 shows the inheritance graph for these classes.

Figure 5-1. Inheritance Graph for the ViewKit Menu Classes

Figure 5-1 Inheritance Graph for the ViewKit Menu Classes

Overview of ViewKit Menu Support

Motif provides the components for building menus (buttons, menu shells, and so on) but does little to make menu construction easy. ViewKit provides a set of classes that facilitate common operations on menus, including creating menu bars, menu panes, popup menus, option menus, and cascading menu panes. The ViewKit menu package also provides an object-oriented interface for activating and deactivating menu items; dynamically adding, removing, or replacing menus items or menu panes; and performing other operations.

The basis for all ViewKit menu classes is the abstract class VkMenuItem, which is derived from VkComponent. There are two types of classes derived from VkMenuItem. The first serve as containers and correspond to the menu types supported by Motif: popup menus, pulldown menu panes, menu bars, and option menus. The second type of derived classes are individual menu items: actions, toggles, labels, and separators.

The classes derived from VkMenuItem correspond closely with Motif widgets and gadgets. For example, an action implemented as a VkMenuAction object represents a XmPushButton gadget along with an associated callback. However, the ViewKit menus offer several advantages over directly using Motif widgets and gadgets. You can manipulate the menu objects more easily than widgets. You can display, activate, and deactivate items with a single function call. You can also easily move or replace items.


Caution: ViewKit implements menu items as gadgets rather than widgets. This causes a problem in callbacks and other situations if you try to use certain Xt functions (such as XtDisplay(3Xt), XtScreen(3Xt), and XtWindow(3Xt)), which expect widgets as arguments. Therefore, use the more general functions (such as XtDisplayofObject(3Xt), XtScreenofObject(3Xt), and XtWindowofObject(3Xt)) when you need this information for ViewKit menu items.

VkMenu, derived from VkMenuItem, is the abstract base class that implements the functionality needed to create and manipulate menus. It provides support for creating menus and adding, removing, replacing, finding, activating, and deactivating menu items.

Separate subclasses of VkMenu implement the various types of menus supported by ViewKit:

VkMenuBar 

Menu bars designed to work with the VkWindow class.

VkPopupMenu 


Popup menus that automatically pop up when the user clicks the right mouse button over a widget.

VkOptionMenu 


Option menus.

VkSubMenu 

Pulldown menu panes that can be used either as pulldown panes in a menu bar or pull-right panes in a popup or other pulldown menu.

VkRadioSubMenu 


A subclass of VkSubMenu used to enforce radio behavior on toggle items that it contains.

VkHelpPane  

A ready-made menu pane that provides an interface to the standard help protocol supported by all ViewKit applications.

Individual menu items are implemented as subclasses derived from VkMenuItem:

VkMenuAction 


A selectable menu item that performs an action, implemented as a PushButtonGadget.

VkMenuConfirmFirstAction 


A selectable menu item that performs an action that the user must confirm before it is executed. When the user chooses this type of menu item, the application posts a question dialog asking the user for confirmation. The application performs the action only if the user confirms it.

VkMenuToggle 


A two-state toggle button gadget. To enforce radio behavior on a group of toggles, you must add them to a VkRadioSubMenu object.

VkMenuLabel 

A non-selectable label.

VkMenuSeparator 


A non-selectable separator.

ViewKit Menu Item Classes

This section describes the features of the ViewKit menu item classes. First it describes the features implemented by VkMenuItem, which are common to all the menu item classes. Then it describes the unique features of each individual menu item class.

Submenus are described in “Submenus” and “Radio Submenus”.


Note: The header file <Vk/VkMenuItem.h> contains the declarations for all menu item classes.


Common Features of Menu Items

Individual menu items are implemented as subclasses derived from VkMenuItem, which provides a standard set of functions for accessing and manipulating menu items.

Unlike with many other ViewKit classes, you should never need to directly instantiate a menu item class. ViewKit automatically instantiates menu item objects as needed when you create menus, as described in “Constructing Menus”. Therefore, this guide does not describe the menu item constructors and destructors.

Keep in mind that ViewKit implements menu items as gadgets rather than widgets. If you need to directly access menu item gadgets, remember to use Xt functions that accept gadgets as well as widgets as arguments.

Displaying and Hiding Menu Items

The VkMenuItem::show() function makes a menu item visible when you display the menu to which it belongs:

void show()

By default, all menu items are visible when they are created (that is, they appear when you display the menu to which they belong). You do not have to explicitly call a menu item's show() function to display it. You can call show() to display a menu item after you have hidden it with hide().

The VkMenuItem::hide() function makes a menu item invisible when you display the menu to which it belongs:

void hide()

hide() does not remove the menu item from the menu, it simply unmanages the widget or gadget associated with a menu item. You can display a hidden menu item by calling its show() function.

If you want to remove a menu item from a menu, you can call VkMenuItem::remove():

void remove()

remove() does not destroy a menu item, it simply removes the item from the menu hierarchy.

Note that instead of retaining pointers to all of your menu items and using VkMenuItem::remove() to remove menu items, you can instead use VkMenu::removeItem(). The effect is the same no matter which function you use, though typically you will find it easier to use the VkMenu function. “Removing Items From a Menu” describes VkMenu::removeItem().

Activating and Deactivating Menu Items

The VkMenuItem::activate() function makes a menu item sensitive so that it accepts user input (that is, a user can choose the item):

void activate()

By default, all menu items are activated (sensitive) when they are created.

The VkMenuItem::deactivate() function makes a menu item insensitive so that it does not accept user input (that is, a user cannot choose the item):

void deactivate()

When it is insensitive, the menu item appears “grayed out” when you display the menu to which it belongs. You can reactivate a menu item by calling its activate() function.

Note that instead of retaining pointers to all of your menu items and using VkMenuItem::activate() and VkMenuItem::deactivate() to activate and deactivate menu items, you can use VkMenu::activateItem() and VkMenu::deactivateItem(), respectively. The effect is the same no matter which functions you use, though typically it is easier to use the VkMenu functions. “Activating and Deactivating Items in a Menu” describes VkMenuItem::activate() and VkMenuItem::deactivate().

Setting Menu Item Labels

Generally, you set the label for a menu item by setting a value in the resource database for that item's XmNlabelString resource. For example, if you have a menu item named “addPage,” you can set the label for that item by including a resource specification such as this:

*addPage.labelString:    Add Page

If you do not set the menu item's XmNlabelString resource, ViewKit uses the item's name.

In some cases, you might need to set the label of an item programmatically. For example, in a page layout system, you might want to change the labels for the items in an Edit menu to reflect the type of object the user has currently chosen. You can change a menu item's label programmatically with the setLabel() function:

virtual void setLabel(const char * str)

The string is treated first as a resource name that setLabel() looks up relative to the menu item's widget. If the resource exists, its value is used as the item's label. If the resource does not exist, or if the string contains spaces or newline characters, setLabel() uses the string itself as the item's label. This allows applications to set and change menu item labels dynamically without hard-coding the exact label strings in the application code.

You can also obtain the current label string by using getLabel():

char *getLabel()

Setting the Position of Menu Items

By default, ViewKit inserts items into a menu in the order you specify them. Therefore, the easiest way to set the positions of menu items is to add them to the menu in the order that you want them to appear.

Occasionally you might need to explicitly set the position of a menu item. To do so, use VkMenuItem::setPosition():

void setPosition(int position)

setPosition() sets the item's position in the menu. You can specify any integer value from zero to the number of items in the menu; a value of zero specifies the first position in the menu. setPosition() ignores invalid values.


Note: setPosition() is effective only before ViewKit realizes the menu to which the menu item belongs. If you call setPosition() after realizing a menu, it has no effect. For example, if you create a menu bar in a window's constructor, you can safely use setPosition() to position menu items; however, after calling the window's show() function, setPosition() has no effect.


Menu Items Utility Functions

You can use MenuItem::menuType() to determine the specific menu item type when given a pointer to a VkMenuItem object:

virtual VkMenuItemType menuType()

menuType() returns one of the following enumerated values of type VkMenuItem::VkMenuItemType:

ACTION 

A VkMenuAction object.

CONFIRMFIRSTACTION 


A VkMenuConfirmFirstAction object.

TOGGLE 

A VkMenuToggle object.

LABEL 

A VkMenuLabel object.

SEPARATOR 

A VkMenuSeparator object.

SUBMENU 

A VkSubMenu object.

RADIOSUBMENU 


A VkRadioSubMenu object.

BAR 

A VkMenuBar object.

OPTION 

A VkOptionMenu object.

POPUP 

A VkPopupMenu object.

OBJECT 

A user-defined subclass of VkMenuActionObject (described in “Command Classes”).

You can also determine when an object pointed to by a VkMenuItem pointer is a menu by calling MenuItem::isContainer():

virtual Boolean isContainer()

isContainer() returns TRUE if the VkMenuItem object is an item that can “contain” other menu items (in other words, a menu).

Menu Actions

The VkMenuAction class provides a selectable menu item that performs an action. A VkMenuAction object is implemented as a PushButtonGadget.

A VkMenuAction object has associated with it a callback function that performs an operation and, optionally, a callback function that “undoes” the operation. You specify these callback functions when you add the item to a menu using one of the methods described in “Constructing Menus”. Consult that section for information on using VkMenuAction objects in a menu.

VkMenuAction provides a couple of public functions in addition to those implemented by VkMenuItem:

  • You can determine whether an action has an undo callback associated with it by calling VkMenuAction::hasUndo():

    Boolean hasUndo()
    

    hasUndo() returns TRUE if the object has an associated undo callback function.

  • If an object has an undo callback function, you can call it programmatically using VkMenuAction::undo():

    virtual void undo()
    

Typically, you won't have any need to call undo() explicitly. ViewKit provides automatic undo handling for your application using the VkUndoManager class, as described in Chapter 6, “ViewKit Undo Management and Command Classes.” All you have to do is provide undo callback functions for your VkMenuAction objects and create an instance of VkUndoManager as described in Chapter 6.

Confirmable Menu Actions

The VkMenuConfirmFirstAction class, which is derived from VkMenuAction, provides a selectable menu item that performs an action. When the user chooses this type of menu item, the application posts a question dialog asking the user for confirmation. The application performs the action only if the user confirms it.

Because the VkMenuConfirmFirstAction class is intended for irrecoverable actions (for example, deleting a file), VkMenuConfirmFirstAction objects do not support undo callback functions.

The VkMenuConfirmFirstAction class uses a XmPushButtonGadget to implement the menu choice and the VkQuestionDialog class to implement the question dialog. (See “Question Dialog” for more information on the VkQuestionDialog class.)

The question displayed in the confirmation dialog is determined by the value of the resource noUndoQuestion, which ViewKit looks up relative to the menu item's widget. For example, if you have a menu item named “quit,” set the question text for that item by including a resource specification such as this:

*quit.noUndoQuestion:    Do you really want to quit?

If you do not provide a value for this resource, ViewKit uses the default question: “This action cannot be undone. Do you want to proceed anyway?”

Menu Toggles

The VkMenuToggle class, which is derived from VkMenuAction, provides a two-state toggle as a menu item. To enforce radio behavior on a group of toggles, you must add them to a VkRadioSubMenu object; otherwise, VkMenuToggle objects exhibit simple checkbox-style behavior. A VkMenuToggle object is implemented as a ToggleButtonGadget.

In addition to the public functions provided by VkMenuItem, VkMenuToggle provides functions for setting and retrieving the toggle state:

  • You can set the visual state of a VkMenuToggle object, without activating its associated callback, using VkMenuToggle::setVisualState():

    void setVisualState(Boolean state)
    

    setVisualState() selects the toggle if state is TRUE, and deselects the toggle if state is FALSE.

  • You can set the visual state of a VkMenuToggle object and activate its associated callback with VkMenuToggle::setStateAndNotify():

    void setStateAndNotify(Boolean state)
    

  • You can retrieve the current value of a VkMenuToggle object using VkMenuToggle::getState():

    Boolean getState()
    

    getState() returns TRUE if the toggle is currently selected, and FALSE if it is currently deselected.

Menu Labels

The VkMenuLabel class provides a non-selectable label as a menu item. A VkMenuLabel object is implemented as a LabelGadget.

The VkMenuLabel class does not provide any public functions other than those implemented by VkMenuItem.

Menu Separators

The VkMenuSeparator class provides a non-selectable separator as a menu item. A VkMenuSeparator object is implemented as a SeparatorGadget.

You can give a menu separator a name if you choose. This allows you to manipulate it like any other menu item.

The VkMenuSeparator class does not provide any public functions other than those implemented by VkMenuItem.

ViewKit Menu Base Class

This section describes the abstract VkMenu class, which provides the basic features of the ViewKit menu classes. It describes how to construct menus, manipulate items contained in the menus, and use the menu access functions. Because all ViewKit menu classes are derived from VkMenu, the functions and techniques described in this section apply to all menu classes.

Constructing Menus

The methods of constructing menus are the same for all types of menus (menu bars, options menus, and so on). The examples in this section use the VkMenuBar class, but the principles are similar for any of the ViewKit menu classes.

You can build menus either by passing a static menu description to the class constructor for a menu, or by adding items dynamically through function calls. You can mix the two approaches, initially defining a static menu structure and then dynamically adding items as needed.

By default, ViewKit menus, are built using work procedures since this shortens application startup times. You should turn this off if there is a conflict with your application's own workproc usage. To do so, use VkMenu::useWorkProcs():

static void useWorkProcs(Boolean flag = TRUE)

Constructing Menus From a Static Description

To construct a menu from a static description, you must create a VkMenuDesc array that describes the contents of the menu and then pass that array as an argument to an appropriate menu constructor. This section describes the format of the VkMenuDesc structure and provides examples of its use.

The VkMenuDesc Structure

The definition for the VkMenuDesc structure is:

struct VkMenuDesc {
    VkMenuItemType   menuType;
    char            *name;
    XtCallbackProc   callback;
    VkMenuDesc      *submenu;
    XtPointer        clientData;
    XtCallbackProc   undoCallback;
};

The purposes of the VkMenuDesc fields are as follows:

menuType 

The type of menu item. The value of this field must be one of the enumerated constants listed below.

name 

The menu item's name, which is also used as the menu item's default label.

callback 

An Xt-style callback procedure that is executed when this menu item is activated.

submenu 

A pointer to an array of a VkMenuDesc structures that describes the contents of a submenu.

clientData 

Data that is passed to the callback procedure when it is executed.

undoCallback 

A callback procedure that can be executed to undo the effects of the actions of the activation callback. Implementation of support for undoing actions is described in Chapter 6, “ViewKit Undo Management and Command Classes.”

The menuType parameter is an enumerated value of type VkMenuItemType. Possible values are as follows:

ACTION 

A selectable menu item, implemented as a VkMenuAction object.

CONFIRMFIRSTACTION 


A selectable menu item, implemented as a VkMenuConfirmFirstAction object, which performs an action that the user must confirm before it is executed.

TOGGLE 

A two-state toggle button gadget, implemented as a VkMenuToggle object.

LABEL 

A label, implemented as a VkMenuLabel object.

SEPARATOR 

A separator, implemented as a VkMenuSeparator object.

SUBMENU 

A cascading submenu, implemented as a VkSubMenu object.

RADIOSUBMENU 


A cascading submenu that acts as a radio-style pane, implemented as a VkRadioSubMenu object.

END 

A constant that must terminate all menu descriptions.

Not all fields are used for each menu item type. Table 5-1 summarizes the optional and required fields for each menu item type.

Table 5-1. Required and Optional Parameters in a Static Menu Description[a]

menuType

name

callback

submenu

clientData[b]

undoCallback

ACTION

R

O[c]

I

O

O

CONFIRMFIRSTACTION

R

Ob

I

O

I

TOGGLE

R

Ob

I

O

I

LABEL

R

I

I

I

I

SEPARATOR

O

I

I

I

I

SUBMENU

R

I

R

O[d]

I

RADIOSUBMENU

R

I

R

Oc

I

END

R

I

I

I

I

[b] If you provide a default client data argument to the menu constructor, that value is used for all menu items for which you do not explicitly provide a client data parameter.

[c] While this parameter is optional, the menu item is useless unless you provide a callback function.

[d] If you provide a client data parameter, that value is used as default client data for all menu items in the
submenu.

For example, consider the following array definition:

class EditWindow: public VkWindow {
  private:
    static VkMenuDesc editMenu[];
    // ...
};
 
VkMenuDesc EditWindow::editMenu[] = {
  { ACTION,   "Cut",   &EditWindow::cutCallback,
                               NULL,   NULL,   &EditWindow::undoCutCallback },
  { ACTION,   "Copy",  &EditWindow::copyCallback,
                               NULL,   NULL,   &EditWindow::undoCopyCallback },
  { ACTION,   "Paste", &EditWindow::pasteCallback,
                               NULL,   NULL,   &EditWindow::undoPasteCallback },
  { ACTION,   "Search" &EditWindow::searchCallback }
  { SEPARATOR },
  { CONFIRMFIRSTACTION, "Revert", &EditWindow::revertCallback },
  { END }
};

The editMenu array describes a simple menu for editing in an application. The menu consists of five actions and a separator. The menu's Cut item calls the cutCallback() function when it is activated with no client data passed to it. Cut also supports an undo action through the undoCutCallback() function. The Copy and Paste items work similarly.

The Search action does not support an undo action. Presumably, the action performed by this item is either too complex to undo or is meaningless to undo.

The Revert item is implemented as a CONFIRMFIRSTACTION. When the user activates this item, the application posts a confirmation dialog to warn the user that the action cannot be undone.

As a more complex example, consider a menu that contains two submenus, each of which contains two selectable items. You could describe this menu with definitions such as:

class TextWindow: public VkWindow {
  
  private:
    static VkMenuDesc menu[];
    static VkMenuDesc applicationPane[];
    static VkMenuDesc editPane[];
    // ...
};
 
VkMenuDesc TextWindow::applicationPane[] = {
    { ACTION, "Open", &TextWindow::openCallback },
    { ACTION, "Save", &TextWindow::saveCallback },
    { END }
};
 
VkMenuDesc TextWindow::editPane[] = {
    { ACTION, "Cut",   &TextWindow::cutCallback },
    { ACTION, "Paste", &TextWindow::pasteCallback },
    { END }
};
 
VkMenuDesc TextWindow::menu[] = {
    { SUBMENU, "Application", NULL, applicationPane },
    { SUBMENU, "Edit",        NULL, editPane },
    { END }
};

After constructing a static menu description, you create it by passing it as an argument to a menu constructor. For example, to implement the menus defined above as a menu bar, you can execute:

VkMenuBar *menubar = new VkMenuBar(menu);

You can implement the same menu as a popup menu simply by passing the definition to a popup menu constructor:

VkPopupMenu *popup = new VkPopupMenu(menu);

Special Considerations for Xt Callback Client Data When Using Static Menu Descriptions

As described in “Using Xt Callbacks With Components”, when using Xt-style callbacks in ViewKit, pass the this pointer as client data to all Xt callback functions. Callback functions then retrieve this pointer, cast it to the expected component type, and call a corresponding member function.

However, you cannot use the this pointer when you define a static data member. To get around this limitation, menu constructors accept a defaultClientData argument. If you provide a value for this argument, any menu item that does not provide a client data argument uses this argument instead. This allows you to specify menus statically while still allowing you to use an instance pointer with Xt callbacks. The code fragment Example 5-1 illustrates this technique.

Example 5-1. Providing Default Client Data When Using Static Menu Descriptions

class SampleWindow: public VkWindow {
 
  private:
    static void oneCallback(Widget,  XtPointer, XtPointer);
    static void twoCallback(Widget,  XtPointer, XtPointer);
    static void cutCallback(Widget,  XtPointer, XtPointer);
    static void pasteCallback(Widget,  XtPointer, XtPointer);
 
    static VkMenuDesc applicationPane[];
    static VkMenuDesc editPane[];
    static VkMenuDesc menu[];
 
  public:
    SampleWindow(const char *name);
 
    // Other members
};
SampleWindow::SampleWindow(char *name) : VkWindow(name)
{
     setMenuBar(new VkMenuBar(menu, (XtPointer) this));
 
     // Other actions
}



Note: VkWindow::addMenuPane(), VkWindow::addRadioMenuPane(), and the form of the VkWindow::setMenuBar() function that accepts a VkMenuDesc array as an argument all automatically use the this pointer as default client data for the menu bars and menu panes that they create.


Creating a Menu Bar Using a Static Description

Example 5-2 illustrates using a static description of a menu tree to create a menu bar. The program creates its main window using MyWindow, a subclass of VkWindow. The menu description and all menu callbacks are contained within the MyWindow subclass definition.

Example 5-2. Creating a Menu Bar Using a Static Description

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkMenu.h>
#include <iostream.h>
#include <Xm/Label.h>
 
class MyWindow: public VkWindow {
  private:
    static void sampleCallback( Widget,  XtPointer , XtPointer);
    static void quitCallback( Widget,  XtPointer , XtPointer);
 
    void quit();
    void sample();
 
    static VkMenuDesc subMenu[];
    static VkMenuDesc sampleMenuPane[];
    static VkMenuDesc appMenuPane[];
    static VkMenuDesc mainMenuPane[];
 
  public:
    MyWindow( const char *name);
    ~MyWindow();
 
    virtual const char* className();
};
 
MyWindow::MyWindow( const char *name) : VkWindow( name)
{
    Widget label =  XmCreateLabel(mainWindowWidget(), "a menu",
                                  NULL, 0);
    setMenuBar(mainMenuPane);
    addView(label);
}
 
MyWindow::~MyWindow()
{
    // Empty
}
 
const char* MyWindow::className() 
{
    return "MyWindow";
}
 
// The menu bar is essentially a set of cascading menu panes, so the
// top level of the menu tree is always defined as a list of submenus
 
VkMenuDesc  MyWindow::mainMenuPane[] = {
  { SUBMENU, "Application",  NULL, MyWindow::appMenuPane },
  { SUBMENU, "Sample",       NULL, MyWindow::sampleMenuPane },
  { END }
};
 
VkMenuDesc MyWindow::appMenuPane[] = {
  { ACTION,      "One",     &MyWindow::sampleCallback },
  { ACTION,      "Two",     &MyWindow::sampleCallback },
  { ACTION,      "Three",   &MyWindow::sampleCallback },
  { SEPARATOR,   “Menu Separator”},
  { ACTION,      "Quit",    &MyWindow::quitCallback },
  { END },
};
 
VkMenuDesc MyWindow::sampleMenuPane[] = {
  { LABEL,       "Test Label" },
  { SEPARATOR,   “Sample Menu Separator”},
  { ACTION,       "An Action",       &MyWindow::sampleCallback },
  { ACTION,       "Another Action",  &MyWindow::sampleCallback },
  { SUBMENU,      "A Submenu",       NULL, MyWindow::subMenu },
  { END },
};
 
VkMenuDesc MyWindow::subMenu[] = {
  { ACTION,  "foo",   &MyWindow::sampleCallback },
  { ACTION,  "bar",   &MyWindow::sampleCallback },
  { ACTION,  "baz",   &MyWindow::sampleCallback },
  { END },
};
 
void MyWindow::sample()
{
    cout << "sample callback" <<  "\n" << flush;
}
 
void MyWindow::sampleCallback(Widget, XtPointer clientData, XtPointer)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->sample();
}
 
void MyWindow::quitCallback ( Widget, XtPointer, XtPointer )
{
    theApplication->quitYourself();
}
 
void main(int argc, char **argv)
{
  VkApp     *myApp    = new VkApp("Menudemo",  &argc,  argv);
  MyWindow  *menuWin  = new MyWindow("MenuWindow");
 
  menuWin->show();
  myApp->run();
}

When you run this program, you see the window shown in Figure 5-2.

Figure 5-2. Main Window With Menu Bar Created by Static Description

Figure 5-2 Main Window With Menu Bar Created by Static Description

The first pane, shown in Figure 5-3, contains three selectable entries (actions), followed by a separator, followed by a fourth action. The first three menu items simply invoke a stub function when chosen. The fourth item calls quitCallback(), which exits the application.

Figure 5-3. Menu Pane Created by a Static Description

Figure 5-3 Menu Pane Created by a Static Description

The second menu pane, shown in Figure 5-4, demonstrates a non-selectable label, a separator, and a cascading submenu.

Figure 5-4. Menu Pane Containing a Label and a Submenu

Figure 5-4 Menu Pane Containing a Label and a Submenu

In addition to implementing these application-defined menu panes, ViewKit can automatically add a Help menu to a menu bar, which provides a user interface to a help system. “ViewKit Help Menu” describes the Help menu. “Using an External Help Library” describes how to add an interface to an external help system to a ViewKit application.

Constructing Menus Dynamically

In addition to the static description approach demonstrated in the previous section, ViewKit allows applications to construct menus and menu items dynamically using functions defined in VkMenu. This section describes the menu-creation functions and provides examples of their use.

Functions for Dynamically Creating Menus

The VkMenu class provides a number of member functions for creating menus. Each function adds a single menu item to a given menu. You can use these functions at any time in your program. Even if you created a menu using a static definition, you can use these functions to add items to the menu.

VkMenu::addAction() adds to a menu a selectable menu action, implemented as a VkMenuAction object:

VkMenuAction *addAction(const char *name,
                        XtCallbackProc actionCallback = NULL,
                        XtPointer clientData = NULL,
                        int position = -1)
 
VkMenuAction *addAction(const char *name,
                        XtCallbackProc actionCallback,
                        XtCallbackProc undoCallback,
                        XtPointer clientData,
                        int position = -1)

addAction() creates a VkMenuAction object named name and adds it to the menu. By default, addAction() adds the item to the end of the menu; if you specify a value for position, addAction() adds the item at that position. actionCallback is the callback function that performs the item's action, and undoCallback is the callback function that undoes the action. If you do not provide an undo callback, the action cannot be undone and does not participate in the ViewKit undo mechanism as described in Chapter 6. clientData is client data passed to the callback functions. Following ViewKit conventions as described in “Using Xt Callbacks With Components”, pass the this pointer as client data so that the callback functions can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.

VkMenu::addConfirmFirstAction() adds to a menu a selectable menu action, implemented as a VkMenuConfirmFirstAction object:

VkMenuConfirmFirstAction *
      addConfirmFirstAction(const char *name,
                            XtCallbackProc actionCallback = NULL,
                            XtPointer clientData = NULL,
                            int position = -1)

addConfirmFirstAction() creates a VkMenuConfirmFirstAction object named name and adds it to the menu. By default, addConfirmFirstAction() adds the item to the end of the menu; if you specify a value for position, addConfirmFirstAction() adds the item at that position. actionCallback is the callback function that performs the item's action, and clientData is client data passed to the callback function. As described above, pass the this pointer as client data.

VkMenu::addToggle() adds to a menu a selectable menu toggle, implemented as a VkMenuToggle object:

VkMenuToggle *addToggle(const char *name,
                        XtCallbackProc actionCallback = NULL,
                        XtPointer clientData = NULL,
                        int state = -1)
                        int position = -1)

addToggle() creates a VkMenuToggle object named name and adds it to the menu. By default, addToggle() adds the item to the end of the menu; if you specify a value for position, addToggle() adds the item at that position. If you provide a state argument, addToggle() sets the initial state of the toggle to that value. actionCallback is the callback function that performs the item's action, and clientData is client data passed to the callback function. As described above, pass the this pointer as client data.

VkMenu::addLabel() adds to a menu a non-selectable menu label, implemented as a VkMenuLabel object:

VkMenuLabel *addLabel(const char *name,
                      int position = -1)

addLabel() creates a VkMenuLabel object named name and adds it to the menu. By default, addLabel() adds the item to the end of the menu; if you specify a value for position, addLabel() adds the item at that position.

VkMenu::addSeparator() adds to a menu a non-selectable menu separator, implemented as a VkMenuSeparator object:

VkMenuSeparator *addSeparator(const char *name,
                              int position = -1)

addSeparator() creates a VkMenuSeparator object named name and adds it to the menu. By default, addSeparator() adds the item to the end of the menu; if you specify a value for position, addSeparator() adds the item at that position.

VkMenu::addSubmenu() adds to a menu a submenu, implemented as a VkSubMenu object:

VkSubMenu *addSubmenu(VkSubMenu *submenu,
                      int position = -1)
 
VkSubMenu *addSubmenu(const char *name,
                      int position = -1)
 
VkSubMenu *addSubmenu(const char *name,
                      VkMenuDesc *menuDesc)
                      XtPointer *defaultClientData = NULL)
                      int position = -1)

addSubmenu() is overloaded so that you can: 1) add an existing VkSubMenu object; 2) create and add a VkSubMenu object containing no items; or 3) create and add a VkSubMenu object from the static menu description, menuDesc. If you create and add the submenu using the static menu description, you can also provide a defaultClientData value that is used as the default client data for all items contained by the submenu. By default, addSubmenu() adds the item to the end of the menu; if you specify a value for position, addSubmenu() adds the item at that position.


Note: The m in addSubmenu() is lowercase, whereas the M in VkSubMenu is uppercase.

VkMenu::addRadioSubmenu() adds to a menu a submenu that enforces radio-style behavior on the toggle items it contains:

VkRadioSubMenu *addRadioSubmenu(VkRadioSubMenu *submenu,
                                int position = -1)
 
VkRadioSubMenu *addRadioSubmenu(const char *name,
                                int position = -1)
 
VkRadioSubMenu *addRadioSubmenu(const char *name,
                              VkMenuDesc *menuDesc)
                              XtPointer *defaultClientData = NULL)
                              int position = -1)

addRadioSubmenu() is overloaded so that you can do one of the following:

  • Add an existing VkRadioSubMenu object.

  • Create and add a VkRadioSubMenu object containing no items.

  • Create and add a VkRadioSubMenu object from the static menu description, menuDesc.

If you create and add the submenu using the static menu description, you can also provide a defaultClientData value that is used as the default client data for all items contained by the submenu. By default, addSubmenu() adds the item to the end of the menu; if you specify a value for position, addSubmenu() adds the item at that position.


Note: The m in addRadioSubmenu() is lowercase, whereas the M in VkRadioSubMenu is uppercase.

VkMenu::add() adds an existing menu item to a menu:

void add(VkMenuItem *item, int position = -1)

By default, add() adds the item to the end of the menu; if you specify a value for position, add() adds the item at that position. Though you can use add() to add any type of menu item to a menu, you typically need it to add only the ViewKit undo manager and VkMenuActionObject objects. “Undo Management” describes the ViewKit undo manager, and “Command Classes” describes the VkMenuActionObject class.

Creating a Menu Bar Dynamically

Example 5-3 is functionally equivalent to Example 5-2. It constructs a menu by adding items one at a time to the window's menu bar and to individual menu panes.

Example 5-3. Creating a Menu Bar Dynamically

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkSubMenu.h>
#include <Vk/VkMenu.h>
#include <Xm/Label.h>
#include <iostream.h>
class MyWindow: public VkWindow {
  private:
    static void sampleCallback( Widget, XtPointer, XtPointer);
    static void quitCallback( Widget, XtPointer, XtPointer);
 
  protected:
    void sample();
  public:
    MyWindow( const char *name);
    ~MyWindow();
 
    virtual const char* className();
};
 
MyWindow::MyWindow( const char *name) : VkWindow( name) 
{
    Widget label =  XmCreateLabel(mainWindowWidget(), "a menu", NULL, 0);
 
    // Add a menu pane
 
    VkSubMenu *appMenuPane = addMenuPane("Application");
    
    appMenuPane->addAction("One",   &MyWindow::sampleCallback,
                           (XtPointer) this);
    appMenuPane->addAction("Two",   &MyWindow::sampleCallback,
                           (XtPointer) this);
    appMenuPane->addAction("Three", &MyWindow::sampleCallback,
                           (XtPointer) this);
    appMenuPane->addSeparator();
    appMenuPane->addAction("Quit", &MyWindow::quitCallback,
                           (XtPointer) this); 
 
    // Add a menu second pane
    
    VkSubMenu *sampleMenuPane = addMenuPane("Sample");
    
    sampleMenuPane->addLabel("Test Label");
    sampleMenuPane->addSeparator();
    sampleMenuPane->addAction("An Action",
                              &MyWindow::sampleCallback,
                              (XtPointer) this);
    sampleMenuPane->addAction("Another Action",
                              &MyWindow::sampleCallback,
                              (XtPointer) this);
 
    // Create a cascading submenu
 
    VkSubMenu *subMenu = sampleMenuPane->addSubmenu("A Submenu"); 
    
    subMenu->addAction("foo", &MyWindow::sampleCallback, (XtPointer) this);
    subMenu->addAction("bar", &MyWindow::sampleCallback, (XtPointer) this);
    subMenu->addAction("baz", &MyWindow::sampleCallback, (XtPointer) this);
    
    addView(label);
}
 
MyWindow::~MyWindow()
{
    // Empty
}
 
const char* MyWindow::className() { return "MyWindow";}
 
void MyWindow::sampleCallback(Widget, XtPointer clientData, XtPointer)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->sample();
}
 
void MyWindow::sample()
{
 
    cout << "sample callback" << "\n" << flush;
}
 
void MyWindow::quitCallback ( Widget, XtPointer, XtPointer )
{
    theApplication->quitYourself();
}
 
void main(int argc, char **argv)
{
    VkApp  *myApp = new VkApp("Menu", &argc, argv);
    MyWindow  *w1  = new MyWindow("menuWindow");
 
    w1->show();
    myApp->run();
}


Manipulating Items in Menu

One of the advantages of the ViewKit menu system is the ability to manipulate the items in a menu after the menu has been created. The ViewKit menu system allows menu items to be manipulated by sending messages to any menu item. Menu items can also be found and manipulated by name.

Finding Items in a Menu

The VkMenu::findNamedItem() function allows you to find an item in a menu given its component name:

VkMenuItem *findNamedItem(const char *name,
                          Boolean caseless = FALSE)

findNamedItem() finds and returns a pointer to a menu item of the specified name belonging to the menu object or any submenus of the menu object. You can also pass an optional Boolean argument specifying whether or not the search is case-sensitive. If findNamedItem() finds no menu item with the given name, it returns NULL. If multiple instances of the same name exist, findNamedItem() returns the first name found in a depth-first search.


Note: Remember that you need to cast the return value if you need to access a member function provided by a VkMenuItem subclass. For example, if you search for a toggle item, remember to cast the return value to VkMenuToggle before calling a member function such as VkMenuToggle::setVisualState().


Activating and Deactivating Items in a Menu

The VkMenu::activateItem() function makes a menu item sensitive so that it accepts user input (that is, a user can choose the item):

VkMenuItem *activateItem(const char *name)

You provide as an argument to activateItem() the name of the menu item to activate. This is the same name that you gave the menu item when you created it. activateItem() returns a VkMenuItem pointer to the item activated (or NULL if you did not provide a valid menu item name). By default, all menu items are activated (sensitive) when they are created.

The VkMenu::deactivateItem() function makes a menu item insensitive so that it does not accept user input (that is, a user cannot choose the item):

VkMenuItem *deactivateItem(const char *name)

You provide as an argument to deactivateItem() the name of the menu item to deactivate. This is the same name that you gave the menu item when you created it. deactivateItem() returns a VkMenuItem pointer to the item deactivated (or NULL if you did not provide a valid menu item name). When it is insensitive, the menu item appears “grayed out” when you display the menu. You can reactivate a menu item by calling deactivateItem() on that item.

Note that instead of using VkMenu::activateItem() and VkMenu::deactivateItem() to activate and deactivate menu items, you could retain pointers to all of your menu items and use VkMenuItem::activate() and VkMenuItem::deactivate(), respectively. The effect is the same no matter which functions you use, though typically it is easier to use the VkMenu functions. “Activating and Deactivating Menu Items” describes VkMenuItem::activate() and VkMenuItem::deactivate().

Removing Items From a Menu

If you want to remove a menu item from a menu, you can call VkMenu::removeItem():

VkMenuItem *removeItem(const char *name)

You provide as an argument to removeItem() the name of the menu item to remove from the menu. This is the same name that you gave the menu item when you created it. removeItem() returns a VkMenuItem pointer to the item removed. removeItem() does not destroy a menu item; it simply removes the item from the menu hierarchy.

Note that instead of using VkMenu::removeItem(), you can retain pointers to all of your menu items and use VkMenuItem::remove(). The effect is the same no matter which functions you use, though typically you it is easier to use the VkMenu functions. “Displaying and Hiding Menu Items” describes VkMenuItem::remove().

Replacing Items in a Menu

You can replace an item in a menu with another menu item using VkMenu::replace():

VkMenuItem *replace(const char *name, VkMenuItem *newItem)

replace() first uses VkMenu::findNamedItem to find the item specified by name. Then it removes that item from the menu and adds the menu item specified by newItem in its place. replace() returns a pointer to the menu item that you replaced.

Manipulating Menu Items

The program in Example 5-4 allows users to dynamically add and remove items from a menu, and also to activate and deactivate items.

Example 5-4. Manipulating Menu Items

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkMenu.h>
#include <Vk/VkSubMenu.h>
#include <Xm/Label.h>
#include <stream.h>
#include <stdlib.h>
 
class MyWindow: public VkWindow {
 
  private:
    static void addOneCallback       (Widget, XtPointer, XtPointer);
    static void removeOneCallback    (Widget, XtPointer, XtPointer);
    static void activateOneCallback  (Widget, XtPointer, XtPointer);
    static void deactivateOneCallback(Widget, XtPointer, XtPointer);
    static void sampleCallback       (Widget, XtPointer, XtPointer);
    static void quitCallback         (Widget, XtPointer, XtPointer);
 
  protected:
    VkSubMenu *_appMenuPane;
    VkSubMenu *_menuPaneTwo;
 
    void addOne();
    void removeOne();
    void activateOne();
    void deactivateOne();
    void sample();
 
  public:
    MyWindow( const char *name);
    ~MyWindow();
    virtual const char* className();
};
MyWindow::~MyWindow()
{
    // Empty
}
 
const char* MyWindow::className() { return "MyWindow";}
 
void MyWindow::sampleCallback(Widget, XtPointer clientData, XtPointer)
{
    MyWindow *obj = ( MyWindow * ) clientData;
    obj->sample();
}
 
void MyWindow::sample()
{
    cout << "sample callback" << "\n" << flush;
}
 
void MyWindow::addOneCallback(Widget, XtPointer clientData, XtPointer)
{
    MyWindow *obj = ( MyWindow * ) clientData;
    obj->addOne();
}
 
void MyWindow::addOne()
{
    _menuPaneTwo->addAction("A New Action", &MyWindow::sampleCallback,
                            (XtPointer) this);
}
 
void MyWindow::removeOneCallback(Widget, XtPointer clientData,
                                 XtPointer)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->removeOne();
}
 
void MyWindow::removeOne()
{
    _menuPaneTwo->removeItem("A New Action");
}
void MyWindow::activateOneCallback(Widget, XtPointer clientData,
                                   XtPointer)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->activateOne();
}
 
void MyWindow::activateOne()
{
    _menuPaneTwo->activateItem("A New Action");
}
 
void MyWindow::deactivateOneCallback(Widget, XtPointer clientData,
                                     XtPointer)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->deactivateOne();
}
 
void MyWindow::deactivateOne()
{
    _menuPaneTwo->deactivateItem("A New Action");
}
 
void MyWindow::quitCallback (Widget, XtPointer, XtPointer)
{
    theApplication->quitYourself();
}
 
MyWindow::MyWindow( const char *name) : VkWindow( name) 
{
    Widget label =  XmCreateLabel(mainWindowWidget(), "a menu",
                                  NULL, 0);
 
    // Add a menu pane
 
    _appMenuPane = addMenuPane("Application");
    
    _appMenuPane->addAction("Add One",
                            &MyWindow::addOneCallback,
                            (XtPointer) this); 
    _appMenuPane->addAction("Remove One",
                            &MyWindow::removeOneCallback,
                            (XtPointer) this); 
    _appMenuPane->addAction("Activate One",
                            &MyWindow::activateOneCallback,
                            (XtPointer) this); 
    _appMenuPane->addAction("Deactivate One",
                            &MyWindow::deactivateOneCallback,
                            (XtPointer) this); 
    _appMenuPane->addSeparator();
    _appMenuPane->addAction("Quit",
                            &MyWindow::quitCallback,
                            (XtPointer) this ); 
 
    // Add a menu second pane
    
    _menuPaneTwo = addMenuPane("PaneTwo");
 
    addView(label);	
}
 
 
void main(int argc, char **argv)
{
  VkApp  *myApp = new VkApp("MenuDemo3",  &argc,  argv);
  MyWindow  *menuWin  = new MyWindow("menuWindow");
 
  menuWin->show();
  myApp->run();
}


Menu Access Functions

The VkMenu class also provides access functions to help manipulate menu items.

You can determine the number of items currently associated with a menu by using VkMenu::numItems():

int numItems() const

You can determine the position of an item in a menu with VkMenu::getItemPosition():

int getItemPosition(VkMenuItem * item)
int getItemPosition(char *name)
int getItemPosition(Widget w)

You can specify the menu item by pointer, name, or widget. getItemPosition() returns the position of the item within the menu, with zero representing the first position in the menu.

As a convenience, you can also access items in a menu using standard array subscript notation:

VkMenuItem * operator[] (int index) const

For example, you can use VkMenu::numItems() with the array subscript notation to loop through an entire menu and perform an operation on all of the items it contains. For example, if menubar is a menu, the following code prints the name and class of each item in the menubar menu:

for ( i=0; i < menubar->numItems(); i++ )
    cout << "Name: " << (*menubar)[i]->name() << "\t"
         << "Class: " << (*menubar)[i]->className() << "\n";

Using ViewKit Menu Subclasses

This section describes the features of each ViewKit menu subclass. In addition to specific member functions listed, each class also supports all functions provided by the VkMenu class.

Menu Bar

The VkMenuBar class provides a menu bar designed to work with the VkWindow class. In addition to the functions described in this section, the VkWindow class provides some member functions for installing a VkMenuBar object as a menu bar. “Menu Bar Support” describes the functions provided by VkWindow.

Examples of menu bar construction were given in “Creating a Menu Bar Using a Static Description” (Example 5-2) and “Creating a Menu Bar Dynamically” (Example 5-3).

Menu Bar Constructors

There are four different versions of the VkMenuBar constructor:

VkMenuBar(Boolean showHelpPane = TRUE)
 
VkMenuBar(const char *name,
          Boolean showHelpPane = TRUE);
 
VkMenuBar(VkMenuDesc *menuDesc,
          XtPointer defaultCientData= NULL,
          Boolean showHelpPane = TRUE)
 
VkMenuBar(const char *name,
          VkMenuDesc *menuDesc,
          XtPointer defaultCientData = NULL,
          Boolean showHelpPane = TRUE)

To work with Silicon Graphics' color schemes, give the menu bar the name “menuBar.” (For information on schemes, consult Chapter 3, “Using Schemes,” in the IRIX Interactive Desktop Integration Guide) The forms of the constructor that do not take a name argument automatically use the name “menuBar.” You can specify another name, but schemes does not work correctly if you do.

If you use a form of the VkMenuBar constructor that accepts a menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide.

Some forms of the constructor also accept an optional defaultClientData argument. If this argument is provided, any menu item that does not provide a client data argument uses this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks, as described in “Special Considerations for Xt Callback Client Data When Using Static Menu Descriptions”.

The last argument to each version of the constructor is a Boolean value that specifies whether the constructor should create a help pane that interfaces to the Silicon Graphics help system. The default is to automatically provide the help pane. The help pane is implemented by the VkHelpPane class (see “ViewKit Help Menu” for more information).

Menu Bar Access Functions

The VkMenuBar class also provides two functions for accessing the menu bar's help pane. The helpPane() member function returns a pointer to the menu bar's help pane:

VkHelpPane *helpPane() const

If the menu bar does not have a help pane, helpPane() returns NULL.

The showHelpPane() member function controls whether or not the menu bar's help pane is visible:

void showHelpPane(Boolean showit)

Submenus

The VkSubMenu class supports pulldown menu panes. You can use these menu panes within a menu bar (a VkMenuBar object), or as a cascading, pull-right menu in a popup or pulldown menu.

Submenu Constructor

You should seldom need to instantiate a VkSubMenu object directly. You can add a submenu to any type of menu by calling that menu's addSubmenu() member function. You can also add menu panes to the menu bar of a VkWindow object by calling VkWindow::addMenuPane().

For those cases where you need to instantiate a VkSubMenu object directly, the form of the constructor to use is as follows:

VkSubMenu(const char *name, 
          VkMenuDesc *menuDesc = NULL, 
          XtPointer defaultClientData = NULL)

name specifies the name of the submenu. If you provide the optional menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide. If you provide the optional defaultClientData argument, any menu item that does not provide a client data argument uses this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks, as described in “Special Considerations for Xt Callback Client Data When Using Static Menu Descriptions”.

Submenu Utility and Access Functions

The VkSubMenu class provides a couple of additional public member functions:

  • Motif supports tear-off menus, which enable the user to retain a menu pane on the screen. If tear-off behavior is enabled for a menu pane, a tear-off button, which has the appearance of a dashed line, appears at the top of the menu pane. The user can tear off the pane by clicking the tear-off button.

    By default, tear-off behavior is disabled for all menu panes. You can change the tear-off behavior of a submenu using VkSubMenu::showTearOff():

    void showTearOff(Boolean showIt)
    

    If you pass the Boolean value TRUE to showTearOff(), the submenu displays the tear-off button; if you pass the value FALSE, it hides the tear-off button.

    You can also enable tear-off behavior for a menu by setting its XmNtearOffModel resource to XmTEAR_OFF_ENABLED (for example, in a resource file).

  • You can access the RowColumn widget used to implement the submenu's pulldown pane by calling VkSubMenu::pulldown():

    Widget pulldown()
    


    Note: The baseWidget() function of a VkSubMenu object returns the CascadeButton widget required by Motif pulldown menus.


Radio Submenus

The VkRadioSubMenu class, derived from VkSubMenu, supports pulldown menu panes. Its function is similar to that of VkSubMenu, but the RowColumn widget used as a menu pane is set to exhibit radio behavior. This class is intended to support one-of-many collections of VkToggleItem objects. You can use VkRadioSubMenu objects as menu panes within a menu bar (a VkMenuBar object), or as a cascading, pull-right menu in a popup or pulldown menu.

It is seldom necessary to directly create a VkRadioSubMenu object. You can add radio submenus to any VkMenuBar, VkPopupMenu, or VkSubMenu by calling those classes' addRadioSubmenu() member function. You can also add menu panes to a VkWindow by calling VkWindow::addRadioMenuPane().

Radio Submenu Constructor

You seldom need to instantiate a VkRadioSubMenu object directly. You can add a radio submenu to any type of menu by calling that menu's addRadioSubmenu() member function. You can also add radio menu panes to the menu bar of a VkWindow object by calling VkWindow::addRadioMenuPane().

For those cases where you need to instantiate a VkRadioSubMenu object directly, the form of the constructor to use is as follows:

VkRadioSubMenu(const char *name, 
               VkMenuDesc *menuDesc = NULL, 
               XtPointer defaultClientData = NULL)

name specifies the name of the radio submenu. If you provide the optional menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide. If you provide the optional defaultClientData argument, any menu item that does not provide a client data argument uses this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks, as described in “Special Considerations for Xt Callback Client Data When Using Static Menu Descriptions”.

Radio Submenu Utility and Access Functions

The VkRadioSubMenu class does not provide any public member functions in addition to those provided by the VkSubMenu class. For information on the utility and access functions provided by VkSubMenu, see “Submenu Utility and Access Functions”.

Using a Radio Submenu Object

Example 5-5 demonstrates the use of the VkRadioSubMenu class.

Example 5-5. Using a VkRadioSubMenu Object

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkSubMenu.h>
#include <Vk/VkRadioSubMenu.h>
#include <Vk/VkMenu.h>
#include <Xm/Label.h>
#include <stream.h>
#include <stdlib.h>
 
class MyWindow: public VkWindow {
 
  private:
 
    static void sampleCallback( Widget,  XtPointer , XtPointer);
    static void quitCallback( Widget,  XtPointer , XtPointer);
 
  protected:
 
    void sample();
 
  public:
 
    MyWindow( const char *name);
    ~MyWindow();
 
    virtual const char* className();
};
 
MyWindow::~MyWindow()
{
    // Empty
}
 
void MyWindow::sampleCallback( Widget,  XtPointer clientData , XtPointer)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->sample();
}
 
const char* MyWindow::className() { return "MyWindow";}
 
void MyWindow::sample()
{
    cout << "In Sample Callback" << "\n" << flush;
}
 
void MyWindow::quitCallback ( Widget, XtPointer, XtPointer )
{
    exit(0);
}
 
MyWindow::MyWindow( const char *name) : VkWindow( name) 
{
    Widget label =  XmCreateLabel(mainWindowWidget(), "a menu", NULL, 0);
 
    // Add a menu pane
 
    VkSubMenu *appMenuPane = addMenuPane("Application");
    
    appMenuPane->addAction("One",  &MyWindow::sampleCallback, (XtPointer) this);
    appMenuPane->addAction("Two",  &MyWindow::sampleCallback, (XtPointer) this);
    appMenuPane->addSeparator();
    appMenuPane->addAction("Quit", &MyWindow::quitCallback, (XtPointer) this); 
 
    // Add a menu second pane
    
    VkSubMenu *sampleMenuPane = addMenuPane("Sample");
    
    sampleMenuPane->addLabel("Test Label");
    sampleMenuPane->addSeparator();
    sampleMenuPane->addAction("An Action", &MyWindow::sampleCallback,
                              (XtPointer) this); 
 
    // Create a cascading submenu
 
    VkRadioSubMenu *subMenu = sampleMenuPane->addRadioSubmenu("A Submenu"); 
    
    subMenu->addToggle("foo", &MyWindow::sampleCallback, (XtPointer) this); 
    subMenu->addToggle("bar", &MyWindow::sampleCallback, (XtPointer) this); 
    subMenu->addToggle("baz", &MyWindow::sampleCallback, (XtPointer) this); 
    
    addView(label);
}
void main(int argc, char **argv)
{
    VkApp  *myApp = new VkApp("Menu",  &argc,  argv);
    MyWindow  *w1  = new MyWindow("menuwindow");
 
    w1->show();
    
    myApp->run();
}


Option Menus

The VkOptionMenu class supports option menus. You can use this component anywhere in your interface.


Note: Unlike many other ViewKit components, VkOptionMenu objects are automatically visible when you create them; you do not need to call show() initially to display a VkOptionMenu object.


Option Menu Constructors

There are two different versions of the VkOptionMenu constructor that you can use:

VkOptionMenu(Widget parent,
             VkMenuDesc *menuDesc,
             XtPointer defaultClientData = NULL)
 
VkOptionMenu(Widget parent,
             const char *name = "optionMenu",
             VkMenuDesc *menuDesc = NULL,
             XtPointer defaultClientData = NULL)

You must provide a parent argument specifying the parent widget of the option menu.

To work with Silicon Graphics' color schemes, give the option menu the name “optionMenu.” (For information on schemes, consult Chapter 3, “Using Schemes,” in the IRIX Interactive Desktop Integration Guide.) The forms of the constructor that do not take a name argument automatically use the name “optionMenu.” You can specify another name, but schemes does work correctly if you do.

If you provide the optional menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide.

If you provide the optional defaultClientData argument, any menu item that does not provide a client data argument uses this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks. This is described in “Special Considerations for Xt Callback Client Data When Using Static Menu Descriptions”.

Setting the Option Menu Label

To specify the string that is displayed as the option menu's label, you must set the XmNlabelString resource for the menu's label widget. To do so you can do one of the following:

  • Use the VkComponent::setDefaultResources() function to provide default resource values.

  • Set resource values in an external app-defaults resource file. Any values you provide in an external file override values that you set using the VkComponent::setDefaultResources() function. This is useful when your application must support multiple languages; you can provide a separate resource file for each language supported.

  • Set the resource value directly using the XtSetValues() function. Values you set using this method override any values set using either of the above two methods. You should generally avoid using this method as it “hard codes” the resource values into the code, making them more difficult to change.

All option menus must be named “optionMenu” to work with Silicon Graphics' color schemes, so if you set the label through a resource value, qualify the resource specifications with the name of a parent widget or component so that the X resource database can distinguish between instances of VkOptionMenu. For example, you can use resource specifications such as *mainWindow*optionMenu*labelString and *graphWindow*optionMenu*labelString to distinguish between an option menu that is a descendant of an XmMainWindow component and one that is a descendant of an SgGraph widget, respectively.

Selecting Items in an Option Menu

You can programmatically set the selected item in an option menu using VkOptionMenu::set():

void set(char* name)
void set(int index)
void set(VkMenuItem *item)

You can specify the selected item either by a pointer to the item, the item's component name, or the item's index (position) in the option menu, where the top item in the menu has an index of zero.

Determining Selected Items in an Option Menu

There are two functions that you can use to determine which item is selected in an option menu:

  • You can retrieve the index (position) of the currently selected menu item using VkOptionMenu::getIndex():

    int getIndex()
    

    getIndex() returns the index (position) of the selected item, where the top item in the menu has an index of zero.

  • You can retrieve a pointer to the currently selected menu item using VkOptionMenu::getItem():

    VkMenuItem *getItem()
    

Option Menu Utility Functions

Normally, the width of the option menu is set to be that of the largest item it contains. You can force the option menu to a different width using VkOptionMenu::forceWidth():

void forceWidth(int width)

forceWidth() sets all of the items in the option menu to be width pixels wide.

Example 5-6 illustrates the use of a VkOptionMenu class.

Example 5-6. Using a VkOptionMenu Object

////////////////////////////////////////////////////////////////////
// Demonstrate viewkit interface to option menus
///////////////////////////////////////////////////////////////////
#include <Vk/VkApp.h>
#include <Vk/VkSimpleWindow.h>
#include <Vk/VkOptionMenu.h>
#include <stream.h>
#include <Xm/RowColumn.h>
class MyWindow: public VkSimpleWindow {
 
  private:
 
    static void sampleCallback( Widget,  XtPointer , XtPointer);
 
    static VkMenuDesc MyWindow::optionPaneDesc[];
 
  protected:
 
    void sample(Widget, XtPointer);
    VkOptionMenu *_optionMenu;
 
  public:
 
    MyWindow( const char *name);
    ~MyWindow( );
 
    virtual const char* className();
};
 
VkMenuDesc MyWindow::optionPaneDesc[] = {
  { ACTION,   "Red",   &MyWindow::sampleCallback},
  { ACTION,   "Green", &MyWindow::sampleCallback},
  { ACTION,   "Blue",  &MyWindow::sampleCallback},
  { END},
};
 
MyWindow::MyWindow( const char *name) : VkSimpleWindow( name)
{
    Widget rc = XmCreateRowColumn(mainWindowWidget(), "rc", NULL, 0);
 
    _optionMenu = new VkOptionMenu(rc, optionPaneDesc, (XtPointer) this);
    _optionMenu->set("Green");
 
    addView(rc);
}
 
MyWindow::~MyWindow( )
{
 
}
 
const char* MyWindow::className() { return "MyWindow";}
 
void MyWindow::sampleCallback(Widget w, XtPointer clientData, XtPointer callData)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->sample(w, callData);
}
 
void MyWindow::sample(Widget, XtPointer)
{
    cout << "Selected item's index = " 
    << _optionMenu->getIndex() 
    << ",  name = " 
    <<  _optionMenu->getItem()->name() 
    << "\n"
    << flush;
}
 
void main(int argc, char **argv)
{
  VkApp     *app    = new VkApp("Option",  &argc,  argv);
  MyWindow  *win  = new MyWindow("OptionMenu");
 
  win->show();
 
  app->run();
}


Popup Menus

The VkPopupMenu class supports popup menus. You can attach a ViewKit popup menu to one or more widgets in your application so that it pops up automatically whenever the user clicks any of those widgets with the right mouse button. You can also pop up the menu programmatically.

Popup Menu Constructors

There are four versions of the VkPopupMenu constructor:

VkPopupMenu(VkMenuDesc *menuDesc,
            XtPointer defaultClientData = NULL)
 
VkPopupMenu(const char *name = "popupMenu",
            VkMenuDesc *menuDesc = NULL,
            XtPointer defaultClientData = NULL)
 
VkPopupMenu(Widget parent,
            VkMenuDesc *menuDesc = NULL,
            XtPointer defaultClientData = NULL)
 
VkPopupMenu(Widget parent,
            const char *name = "popupMenu",
            VkMenuDesc *menuDesc = NULL,
            XtPointer defaultClientData = NULL)

The forms of the constructor that do not take a name argument automatically use the name “popupMenu.” You can specify another name, but schemes does not work correctly if you do.

If you provide the optional menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide.

If you provide the optional defaultClientData argument, any menu item that does not provide a client data argument uses this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks. This is described in “Special Considerations for Xt Callback Client Data When Using Static Menu Descriptions”.

If you use a form of the VkPopupMenu constructor that accepts a parent argument, the constructor automatically attaches the menu to the widget. This builds the menu as a child of the widget and installs an event handler to pop up the menu whenever the user clicks the widget with the right mouse button. For more information on attaching a popup menu to a widget, see the description of VkPopupMenu::attach() in “Attaching Popup Menus to Widgets”.

Attaching Popup Menus to Widgets

The VkPopupMenu::attach() function attaches a popup menu to a widget:

virtual void attach(Widget w)

The first call to attach() creates all widgets in the popup menu, using the given widget as the parent of the menu. attach() then adds an event handler to post the menu automatically whenever the user clicks the widget with the right mouse button. Subsequent calls to attach() add the ability to post the menu over additional widgets.

Popping Up Popup Menus

Once you have attached a popup menu to one or more widgets in your application, ViewKit automatically posts the menu whenever the user clicks any of those widgets with the right mouse button.

You can also post the menu programmatically even if you have not attached the popup menu to a widget, by first building the menu using VkPopupMenu::build():

virtual void build(Widget parent)

build() builds the menu as a child of the parent widget, but does not install an event handler to post the menu.

Once you have built the menu, you can post it with VkPopupMenu::show():

virtual void show(XEvent *buttonPressEvent)

show() requires an X ButtonPress event as an argument to position the menu on the screen. This requires you to register your own event handler to handle the ButtonPress events.

build() and show() support applications that wish to control the posting of menus directly. Normally, attach() provides an easier way to use popup menus.

Using a Popup Menu

Example 5-7 illustrates the use of the VkPopupMenu class.

Example 5-7. Using a VKPopupMenu Object

//////////////////////////////////////////////////////////////////
// Sample program that demonstrates how to create a popup menu
/////////////////////////////////////////////////////////////////
#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkPopupMenu.h>
#include <stream.h>
#include <Xm/Label.h>
 
class MyWindow: public VkWindow {
 
  private:
 
    VkPopupMenu *_popup;
 
    static void sampleCallback( Widget,  XtPointer , XtPointer);
    void sample();
 
    static VkMenuDesc subMenu[];
    static VkMenuDesc sampleMenuPane[];
 
  protected:
 
  public:
 
    MyWindow( const char *name);
    ~MyWindow();
 
    virtual const char* className();
};
 
MyWindow::MyWindow( const char *name) : VkWindow( name)
{
    Widget label =  XmCreateLabel(mainWindowWidget(), "a menu", NULL, 0);
 
    _popup = new VkPopupMenu(label, sampleMenuPane, (XtPointer) this);
    
    addView(label);	
}
 
MyWindow::~MyWindow( )
{
 
}
 
const char* MyWindow::className() { return "MyWindow";}
 
// The menu bar is essentially a set of cascading menu panes, so the
// top level of the menu tree is always defined as a list of submenus
 
VkMenuDesc MyWindow::sampleMenuPane[] = {
  { LABEL,    "Test Label"},
  { SEPARATOR },
  { ACTION,    "An Action",       &MyWindow::sampleCallback},
  { ACTION,    "Another Action",  &MyWindow::sampleCallback},
  { SUBMENU,   "A Submenu",       NULL, MyWindow::subMenu},
  { END},
};
 
 
VkMenuDesc MyWindow::subMenu[] = {
  { ACTION,  "foo",   &MyWindow::sampleCallback},
  { ACTION,  "bar",   &MyWindow::sampleCallback},
  { ACTION,  "baz",   &MyWindow::sampleCallback},
  { END},
};
 
void MyWindow::sample()
{
    cout << "sample callback" <<  "\n" << flush;
}
void MyWindow::sampleCallback( Widget,  XtPointer clientData  , XtPointer)
{
    MyWindow *obj = (MyWindow *) clientData;
    obj->sample();
}
 
void main(int argc, char **argv)
{
  VkApp     *myApp    = new VkApp("Menudemo",  &argc,  argv);
  MyWindow  *menuWin  = new MyWindow("MenuWindow");
 
  menuWin->show();
 
  myApp->run();
}


Putting Menus in the Overlay Planes

By default, menus appear in the normal planes. ViewKit menus, however, may be explicitly placed in the overlay planes. Doing so prevents the menus from causing expose events that disturb such things as complex GL rendering in the normal planes.

There are three ways to enable menus in the overlay planes:

  • Call VkMenu::useOverlayMenus(TRUE). This forces menus into the overlay planes, with no way to put them back in the normal planes without recompiling.

  • Put the resource string “*useOverlayMenus:True” in your application's default file. This will put menus in the overlay planes by default, but allow users to use the normal planes by changing their .Xdefaults file.


    Note: This is an application-specific resource. There is no class resource, so “*UseOverlayMenus” is not supported.


  • Have users add the -useOverlayMenus command-line switch when they run your application if they wish to use the overlay planes for menus.

If you do decide to place menus in the overlay planes, here are some factors to consider:

  • Menus are placed in the deepest available overlay planes: generally 4- or 8-bit planes, occasionally 2-bit planes.

  • If the deepest available overlay is 2 bits, any menus placed in that visual may not look right. Because the colormap in the 2-bit overlay planes only has three color entries (the fourth being a transparent pixel), any items in the menus other than labels (for example cascade or toggle buttons) may look odd.

  • Other applications using the overlay planes may display in the wrong colors when the application posting the menu gets colormap focus. The colors in the other applications may flash because the menu's colormap is installed and replaces any previous overlay colormap.

  • Tear-off menus may display in the wrong colors. Since tear-off menus are no longer transient, they may be susceptible to color distortions as in previous examples.