[ Previous | Next | Contents | Glossary | Home | Search ]
Motif 2.1 Widget Writer's Guide



Writing Your Own Menu Button Widget

The ExmMenuButton demonstration widget is a subclass of ExmString. Figure 11-2 shows its position in the hierarchy.

Figure 7. ExmMenuButton Position in Hierarchy.




View figure.

Like an ExmString, an ExmMenuButton can display one compound string. However, ExmMenuButton provides the additional ability to function as a menu child widget. That is, an ExmMenuButton can be a child of an XmRowColumn whose XmNrowColumnType resource is set to something other than XmWORK_AREA.

Before getting too deeply into menu buttons, we want to remind you that you should not write an original menu button widget. If you feel the standard Motif menu button widgets and gadgets do not meet your needs, then you should modify ExmMenuButton. For example, you might provide a realize method for ExmMenuButton that creates circular-shaped menu buttons. Another possibility is to extend the visuals of ExmMenuButton so that each menu button simultaneously displays both a pixmap and some text.

The following subsections detail selected features of ExmMenuButton.

Traversal Translations

ExmMenuButton provides two different translation strings: defaultTranslations and traversalTranslations.

The defaultTranslations string is specified in the tm_table field of the Core class record. These translations handle activation events.

The traversalTranslations string is used in the translations field of the Primitive class record. These translations allow a user to move between menu choices. None of the action routines associated with the traversalTranslations string are defined by the ExmMenuButton widget. Therefore, the Intrinsics automatically search for the action routines in the parent of ExmMenuButton. The only possible parent of an ExmMenuButton widget is an XmRowColumn widget. As it happens, XmRowColumn defines all the MenuTraverse action routines.

The XmQTmenuSavvy Trait

Every menu button widget must install the XmQTmenuSavvy trait. The menu system widget (XmRowColumn) examines its children for this trait. If a child does not hold this trait, the child cannot be a child of the Motif menu system widget.

ExmMenuButton installs the XmQTmenuSavvy trait as part of its ClassInitialize method. The following call to XmeTraitSet does the installation:

XmeTraitSet(exmMenuButtonWidgetClass, XmQTmenuSavvy,
            (XtPointer) &menuSavvyTraitRec);

The third argument to XmeTraitSet, menuSavvyTraitRec, is defined earlier in the MenuB.c file as follows:

static XmConst XmMenuSavvyTraitRec menuSavvyTraitRec = {
  0,                                        /* Version */
  (XmMenuSavvyDisableProc) DisableCallback, /* disableCallback   */
  GetAccelerator,                           /* getAccelerator    */
  GetMnemonic,                              /* getMnemonic       */
  GetActivateCBName,                     /* getActivateCBName */
};

Installing Accelerators and Mnemonics

The ExmMenuButton widget supports accelerators and mnemonics. (See the Motif Programmer's Guide for details on accelerators and mnemonics.) In brief, mnemonics and accelerators each provide a way for a user to activate a menu button without actually clicking on it. For example, the AllExmDemo.c demonstration program places both an accelerator and a mnemonic on the ExmMenuButton Quit button. The accelerator is "Alt-q". Therefore, a user can activate the ExmMenuButton Quit button by pressing Alt-q even when the button is not visible. The mnemonic is "Q". Therefore, a user can activate the ExmMenuButton Quit button by pressing Q whenever the button is visible.

To support accelerators and mnemonics, ExmMenuButton does the following:

  1. Provides appropriate resources; manages the resource values in initialize and set_values

  2. Provides appropriate XmQTmenuSavvy trait methods

  3. Provides appropriate visual support for accelerators and mnemonics in its DrawVisual method

    The following subsections examine each of the preceding items.

  4. Step 1: Provide Appropriate Resources

    ExmMenuButton provides the following resources definitions in its resources array to support accelerators and mnemonics:

    ...
    {
        XmNmnemonic,
        XmCMnemonic,
        XmRKeySym,
        sizeof(KeySym),
        XtOffsetOf( ExmMenuButtonRec, menu_button.mnemonic),
        XmRImmediate,
        (XtPointer) XK_VoidSymbol
    },
     
    {
        XmNaccelerator,
        XmCAccelerator,
        XmRString,
        sizeof(char *),
        XtOffsetOf(ExmMenuButtonRec, menu_button.accelerator),
        XmRImmediate,
        (XtPointer) NULL
    },
     
    {
        XmNacceleratorText,
        XmCAcceleratorText,
        XmRXmString,
        sizeof(XmString),
        XtOffsetOf(ExmMenuButtonRec, menu_button.accelerator_text),
        XmRImmediate,
        (XtPointer) NULL
    },
     
    {
        XmNmnemonicCharSet,
        XmCMnemonicCharSet,
        XmRString,
        sizeof(XmStringCharSet),
        XtOffsetOf(ExmMenuButtonRec, menu_button.mnemonic_charset),
        XmRImmediate,
        (XtPointer) XmFONTLIST_DEFAULT_TAG
    }

    Step 2: Provide XmQTmenuSavvy Trait Methods

    The XmQTmenuSavvy trait contains two relevant trait methods: getAccelerator and getMnemonic. What follows is how ExmMenuButton implements these two trait methods:

    static char*
    GetAccelerator(Widget w)
    {
      ExmMenuButtonWidget mw = (ExmMenuButtonWidget)w;
     
      return(mw -> menu_button.accelerator);
    }
     
     
    static KeySym
    GetMnemonic(Widget w)
    {
      ExmMenuButtonWidget mw = (ExmMenuButtonWidget)w;
     
      return(mw -> menu_button.mnemonic);
    }

    The menu system widget (XmRowColumn) calls these two trait methods to determine what the accelerator and mnemonic are.

    Step 3: Provide Appropriate Visuals

    ExmMenuButton must provide visuals that tell the user what the accelerator and mnemonic are. The visuals are rendered by the DrawVisual method of ExmMenuButton.

    It is customary to identify the mnemonic by underlining one of the characters in the regular button text. The DrawVisual method of ExmMenuButton calls XmStringDrawUnderline to do the underlining.

    If a button has an accelerator, the accelerator must be shown following the label of the button. After DrawVisual draws the regular button text, DrawVisual calls XmStringDraw to write the accelerator text.

    The XmQTmenuSystem Trait

    ExmMenuButton does not install the XmQTmenuSystem trait. Rather, XmRowColumn installs this trait, and ExmMenuButton calls many of its trait methods. We now consider several of those calls to XmQTmenuSystem trait methods.

    When the cursor enters the window associated with an ExmMenuButton, the ExmMenuButton method is called. This method is responsible for giving focus to the ExmMenuButton, but only if the menu system is in drag mode. (When the menu system is in drag mode, each menu button child will automatically be highlighted as it gains keyboard focus.)

    In order to do this, the MenuButtonEnter method must ask its parent if it holds the XmQTmenuSystem trait. The following code illustrates this:

    {
    ExmMenuButtonWidgetClass wc = (ExmMenuButtonWidgetClass)XtClass(w);
    ExmMenuButtonWidget mw = (ExmMenuButtonWidget)w;
    XmMenuSystemTrait menuSTrait;
    int status;
     
    menuSTrait = (XmMenuSystemTrait)
      XmeTraitGet((XtPointer) XtClass(XtParent(w)), XmQTmenuSystem);
     
    if (! menuSTrait)
      return;

    Assuming that the parent is a menu system widget, the MenuButtonEnter method must now find out what state the widget is in. To do so, the MenuButtonEnter method must call the status trait method as follows:

    status = menuSTrait->status(w);

    The status trait method returns a bit mask into the status variable. The Xm/MenuT.h header file provides several macros for probing the returned bit mask. The MenuButtonEnter method calls the XmIsInDragMode macro to determine if the menu system is in drag mode as follows:

    if ((((ShellWidget) XtParent(XtParent(mw)))->shell.popped_up)
    &&
      XmIsInDragMode(status)) {

    If the menu system is in drag mode, then the ExmMenuButton must grab the keyboard focus by calling the childFocus trait method of XmQTmenuSystem as follows:

    menuSTrait -> childFocus(w);

    [ Previous | Next | Contents | Glossary | Home | Search ]