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. The Inheritance Graph for the ViewKit Menu Classes


Overview of ViewKit Menu Support

IRIS IM 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 IRIS IM: 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 IRIS IM 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 IRIS IM 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 VkHelpPane.3confirms 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.

The 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 instead 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:

*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 dynamically set and change menu item labels without hard-coding the exact label strings in the application code.

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 PushButtonGadget to implement the menu choice and the VkQuestionDialog(3Vk) to implement the question dialog. (See “The 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:

*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.

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

The 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.

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:

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:

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

menuType

name

callback

submenu

clientData[a]

undoCallback

ACTION

R

O[b]

I

O

O

CONFIRMFIRSTACTION

R

Ob

I

O

I

TOGGLE

R

Ob

I

O

I

LABEL

R

I

I

I

I

SEPARATOR

I

I

I

I

I

SUBMENU

R

I

R

O[c]

I

RADIOSUBMENU

R

I

R

Oc

I

END

R

I

I

I

I

R = Required parameter

 

 

 

 

 

O = Optional parameter

 

 

 

 

 

I = Ignored parameter

 

 

 

 

 

[a] 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.

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

[c] 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.


Example of a 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. Example of 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 },
  { ACTION,   "Quit",    &MyWindow::quitCallback },
  { END },
};

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 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


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. A 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. A 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. “The ViewKit Help Menu” describes the Help menu. Appendix C, “Using a Help System With ViewKit,” 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: 1) add an existing VkRadioSubMenu object; 2) create and add a VkRadioSubMenu object containing no items; or 3) 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.

Example of 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. Example of 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.

Example of 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. Example of 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 “Example of a Creating a Menu Bar Using a Static Description” (Example 5-2) and “Example of 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 the schemes(3x) reference page.) 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, described in “The ViewKit Help Menu”.

Menu Bar Access Functions

The VkMenuBar class also provides the helpPane() member function:

VkHelpPane *helpPane() const

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

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 other 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:

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:

  • IRIS IM 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()
    

The baseWidget() function of a VkSubMenu object returns the CascadeButton widget required by IRIS IM 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 other 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:

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”.

Radio Submenu Example

Example 5-5 shows an example of using a VkRadioSubMenu class.

Example 5-5. Example of 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.

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 the schemes(3x) reference page.) 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:

  • 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.

Because all option menus must be named “optionMenu” to work with Silicon Graphics' color schemes, 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 a “mainWindow” component and one that is a descendant of a “graphWindow” component, 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.

Option Menu Example

Example 5-6 shows an example of using a VkOptionMenu class.

Example 5-6. Example of 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.

Popup Menu Example

Example 5-7 shows an example of using a VkPopupMenu class.

Example 5-7. Example of 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();
}

The ViewKit Help Menu

The Help menu, implemented by the VkHelpPane class, provides a simple user interface to a help system. For more information on integrating a help system with your application, consult Appendix C, “Using a Help System With ViewKit.”

Implementation of the Help Menu

VkHelpPane is a subclass of VkSubMenu. VkHelpPane automatically provides five standard menu items, as shown in Figure 5-5:

Figure 5-5. The ViewKit Help Menu


The first four items interface to a help system. This help system must provide help request handling and appropriate help messages for the menu item selected:

“Click for Help” 


Provides context-sensitive help. When the user chooses this item, the cursor changes into a question mark. The user can then click any widget in the application.

“Overview” 

Requests overview help.

“Index” 

Requests an index of available help topics.

“Keys & Shortcuts” 


Requests help on keys and shortcuts.

“Product Information” displays the Product Information dialog described in “Maintaining Product and Version Information”. The Product Information dialog has no connection to the help system.

Because VkHelpPane is a subclass of VkSubMenu, you can also use the functions provided by VkSubMenu to add custom Help menu items and delete predefined Help menu items.

Adding the Help Pane to a Menu

The VkMenuBar constructor, described in “Menu Bar Constructors”, accepts a showHelpPane argument. If this argument is TRUE, the default, the VkMenuBar constructor automatically creates a VkHelpPane object and installs it in the menu bar.

You can create a VkHelpPane object and add it to another menu, for example a popup menu, but you should rarely need to do this.

X Resources Associated With the Help Pane

The following X resources affect the appearance and behavior of the VkHelpPane class:

*helpMenu.labelString 


The label for the Help menu (default value “Help”)

*helpMenu.mnemonic 


The Help menu mnemonic (default value “H”)

*helpMenu.helpOnContextMenuItem.labelString 


The label for the context-sensitive help item (default value “Click for Help”)

*helpMenu.helpOnContextMenuItem.mnemonic 


The context-sensitive help item mnemonic (default value “C”)

*helpMenu.helpOnContextMenuItem.accelerator 


The context-sensitive help item accelerator (default value “Shift<Key>F1”)

*helpMenu.helpOnContextMenuItem.acceleratorText 


The context-sensitive help item accelerator label (default value “Shift+F1”)

*helpMenu.helpOverviewMenuItem.labelString 


The label for the help overview item (default value “Overview”)

*helpMenu.helpOverviewMenuItem.mnemonic 


The help overview item mnemonic (default value “O”)

*helpMenu.helpIndexMenuItem.labelString 


The label for the help index item (default value “Index”)

*helpMenu.helpIndexMenuItem.mnemonic 


The help index item mnemonic (default value “I”)

*helpMenu.helpKeysMenuItem.labelString 


The label for the keys and shortcuts item (default value “Keys & Shortcuts”)

*helpMenu.helpKeysMenuItem.mnemonic 


The keys and shortcuts item mnemonic (default value “K”)

*helpMenu.helpVersionMenuItem*labelString 


The label for the product information item (default value “Product Information”)

*helpMenu.helpVersionMenuItem*mnemonic 


The product information item mnemonic (default value “P”)

Pulldown Menus in the Overlay Planes

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

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

  • Call VkMenu::useOverlayMenus(TRUE). This will force pulldowns 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 pulldowns in the overlay planes by default, but allow users to use the normal planes by changing their .Xdefaults file.

  • 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 will be 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 menu pulldowns 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 menu is activated. The colors in those applications may flash when a pulldown menu appears because the menu's colormap will get installed and replace 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.