Chapter 5. Traits

This chapter explains traits through text and examples. You should also see Chapter 18, which contains reference pages for all the traits.

What Is a Trait?

Motif provides widget writers with about a dozen traits. A widget writer can install any number of these traits on a widget. A trait is named by an XrmQuark that symbolizes a specific widget capability. If a widget holds a particular trait, then that widget is announcing a specific capability to other widgets. Conversely, if a widget does not hold a particular trait, than that widget is implicitly announcing to other widgets that it is incapable of providing a particular service.

For example, consider the XmQTaccessTextual trait. A widget holding this trait is announcing to other widgets that it is capable of displaying one primary text parcel. Many standard Motif widgets, including XmText and XmLabel, hold this trait. If you are writing a widget that can display one primary text parcel, then it too should hold this trait.

Table 5-1 summarizes the standard Motif traits.

Table 5-1. Standard Motif Traits

Trait NameA Widget Holding This Trait Can:
XmQTaccessTextualDisplay one primary text parcel
XmQTactivatableBecome a command button in a DialogBox
XmQTcareParentVisualBorrow its parent's visual information
XmQTcontainerManage one or more XmQTcontainerItem children
XmQTcontainerItemBecome a child of an XmQTcontainer parent
XmQTdialogShellSavvyBecome a child of XmDialogShell
XmQTjoinSideAttach itself to one side of a suitable parent
XmQTmenuSavvyBecome a menu child
XmQTmenuSystemManage a menu system
XmQTnavigatorAct as a navigator to a scrollable widget
XmQTscrollFrameHandle one or more navigator widgets
XmQTspecifyRenderTableSupply default render tables
XmQTtakesDefaultChange its appearance to show that it is the default button
XmQTtransferTransfer data to other widgets and/or receive data from other widgets

Why Use Traits?

The trait mechanism provides an easy way for two widgets to communicate with each other. Frequently, the two communicating widgets will be a parent and its child; however, unrelated widgets can also communicate through traits. This communication takes two forms:

  • Widgets can ask each other about specific capabilities.

  • Widgets can access each other's trait methods. Trait methods are defined in a trait structure variable rather than in a class record. (Trait structure variables are described later in this chapter.)

For example, consider a Motif application that needs to create a menu. To create the menu, the Motif application will need at least one XmRowColumn manager and some appropriate menu children. The XmRowColumn widget needs some way to ask each of its children if they are suitable menu children. The XmQTmenuSavvy trait provides a way. A child widget holding the XmQTmenuSavvy trait is announcing to a menu parent (XmRowColumn) that it is capable of becoming a menu child. Therefore, the menu parent (XmRowColumn) can ask each child if it holds this trait. If a child does not hold this trait, XmRowColumn can issue a warning that the child is not an acceptable menu child.

The XmQTmenuSavvy trait provides several trait methods. One of these trait methods, disableCallback, enables or disables the activate callback method associated with a menu child. Without a trait mechanism, there would be no way for the XmRowColumn widget to call disableCallback. After all, XmRowColumn shares no common ancestry with any primitive widgets. However, since disableCallback is a trait method, XmRowColumn can call disableCallback.

Installing a Trait

You must perform the following steps in order to install a trait on a widget:

  1. Include the appropriate header file for the trait you are using.

  2. Declare the trait structure variable.

  3. Call the XmeTraitSet function.

  4. Write the code for any trait methods named in the trait structure variable.

The coding for all four steps is to be added to the widget source code file. That is, you do not need to make any changes to the widget's header files.

The following subsections examine these four activities by focusing on how the XmQTaccessTextual trait is installed on the ExmString widget.

Step 1: Include the Appropriate Header Files

If you plan to install a trait in your widget, then your widget source code file must include the appropriate trait header files. Every widget that installs one or more traits must include the general-purpose trait header file TraitP.h. In addition, depending on which traits are being installed, the widget source code file must also include the appropriate trait-specific header file documented in the reference pages of Chapter 19.

For example, the widget source code file (String.c) for the ExmString widget contains the following three declarations:

#include <Xm/TraitP.h>
#include <Xm/AccTextT.h>
#include <Xm/SpecRenderT.h>

The Xm/AccTextT.h header file is included because ExmString installs the XmQTaccessTextual trait. In addition, the Xm/SpecRenderT.h header file is included because ExmString also uses the XmQTspecifyRenderTable trait.

Step 2: Declare the Trait Structure Variable

You must declare a trait structure variable for every trait that you plan to install. You should place this declaration in the source code file after the class record declaration but before the first widget method.

The data type of a trait structure variable must be the trait structure associated with the trait. (See Chapter 19 to find the relevant trait structure for your trait.) For example, the trait structure associated with the XmQTaccessTextual trait is XmAccessTextualTraitRec.

Motif recommends giving the trait structure variable a name that consists of the widget name followed by an acronym that symbolizes the trait. For example, ExmString uses the variable name StringATT to identify its XmQTaccessTextual trait structure variable.

All trait structure variable declarations should be prefixed with static XmConst. (See Chapter 2 for details about XmConst.)

For example, the ExmString widget declares a trait structure variable for the XmQTaccessTextual trait as follows:

static XmConst XmAccessTextualTraitRec StringATT = {
  0,                       /* version */
  StringGetValue,          /* trait method */
  StringSetValue,          /* trait method */
  StringPreferredFormat,   /* trait method */
};

The preceding declaration declares a trait structure variable named StringATT. This variable has data type XmAccessTextualTraitRec. This data type is defined in the AccTextT.h trait header file.

The first field of all trait structure variables is the version field. You must always specify an integer value for this field. At Motif Release 2.0, none of the standard Motif traits make any attempt to interpret the value in the version field. However, at Motif Release 2.0, you must set the version field to 0 since future releases of Motif may use the version field.

The remaining fields following the version field specify the names of the trait methods. By convention, the trait method names in your trait structure variables should correspond to the trait method names documented in the reference pages of Chapter 19. However, your trait method names should eliminate underscores and should capitalize the first letter of every word. For example, we named the second trait method StringSetValue because the trait method name string_set_value is documented in the XmQTaccessTextual trait reference page of Chapter 19.

Some traits allow you to omit certain trait methods. (See the individual reference pages of Chapter 19 for details.) To tell Motif that you are omitting a certain trait method, simply mark the appropriate field in the trait structure record as being NULL. For example, if we did not provide a StringSetValue method in the ExmSimple widget, the preceding declaration would have looked like this:

static XmConst XmAccessTextualTraitRec StringATT = {m
0,                         /* version */m
StringGetValue,            /* trait method */m
NULL,                      /* not providing this trait method */m
StringPreferredFormat,     /* trait method */
};

Step 3: Call XmeTraitSet

Call the XmeTraitSet function to install a trait on a widget class. Typically, your widget calls XmeTraitSet from the class_part_initialize method. By so doing, the trait is installed not only on the current widget, but also on all of its subclasses. If you want the trait installed on the current widget class only (and not its subclasses), then you should call XmeTraitSet from the class_initialize method instead of the class_part_initialize method.

For example, ExmString calls XmeTraitSet to install the XmQTaccessTextual trait as follows:

XmeTraitSet((XtPointer) widgetclass, XmQTaccessTextual,
           (XtPointer) &StringATT);

The third argument to XmeTraitSet holds the address of the trait structure variable created in Step 2.

Because the call to XmeTraitSet took place in the class_part_initialize method, Motif installs this trait on ExmString and on all its subclasses.

In some cases, you may not want the widget you are writing to inherit some of the installed traits of its superclasses. For these situations, you can prevent trait inheritance by specifying NULL as the third argument to XmeTraitSet. For example, suppose you are writing a subclass of ExmString, named ExmStringSubclass. If you do not want ExmStringSubclass to install ExmQTaccessTextualTrait, the class_part_initialize method of ExmStringSubclass should call XmeTraitSet as follows:

XmeTraitSet((XtPointer) widgetclass, XmQTaccessTextual, NULL);

Step 4: Writing Trait Methods

A trait method is a function that is callable by another widget, even if that other widget is not a subclass of the current widget. Typically, the caller is either the parent widget or the child widget of the current widget.

For example, the XmQTaccessTextual trait defines three trait methods. The simplest trait method, StringPreferredFormat, is shown as follows:

static int
StringPreferredFormat(
        Widget  w)
{
/* Choose XmFORMAT_XmSTRING because the ExmString widget holds its displayed
   text in XmString format (as opposed to Multibyte or WCS format). */
        return(XmFORMAT_XmSTRING);
}

By convention, you should place your widget's trait methods toward the end of your widget source code file, immediately prior to the publicly accessible functions.

Accessing Traits

As mentioned earlier in this chapter, there are two general ways for a widget to access the trait information of another widget:

  • A widget can ask another widget if it holds a certain trait.

  • Assuming that a widget does hold a certain trait, another widget can call that widget's trait methods.

The following subsections describe both ways.

Determine if a Widget Holds a Particular Trait

Use the XmeTraitGet function to determine if a specified widget holds a certain trait. This function returns NULL if the specified widget does not hold the trait, and a non-NULL value (a trait record) if the widget does hold the trait. For example, a parent widget can use the following code to determine if its child widget holds the XmQTaccessTextual trait:

if ( XmeTraitGet((XtPointer)a_widget_class, XmQTaccessTextual))
  /* Yes, a_widget_class renders a primary text block. */
else
  /* a_widget_class does not render a primary text block. */

Call Another Widget's Trait Method

If a widget does hold a trait, the XmeTraitGet function returns a pointer to the appropriate trait structure variable. Another widget can use this returned pointer to call trait methods. For example, suppose that a parent of ExmString needs to determine the preferred string format of ExmString. The following code from the parent does just that:

XmAccessTextualTrait  childs_trait_record;
int                   preferred_string_format_of_child;

 /* Get a pointer to the trait structure variable. */
   childs_trait_record =
                        (XmQTaccessTextual) XmeTraitGet((XtPointer)a_widget_class,                                                                                                                                                                                                                      XmQTaccessTextual);

 /* Use the returned pointer to call the child's
                preferred_format trait method. */
preferred_string_format_of_child =
     childs_trait_record->preferred_format((Widget)parent_widget);

Note that, in some cases, the widget holding the trait has not defined a particular trait method; that is, the trait method is set to NULL. For that reason, the following code is an improvement over the previous example:

/* Does this trait method exist? */
if      (childs_trait_record->preferred_format !=
        (XmAccessTextualTraitRec) NULL)   {
/* It does exist, so call the trait method. */
        preferred_string_format_of_child =
                childs_trait_record->preferred_format((Widget)parent_widget);
        }


Overriding a Trait Record Variable

Trait record variables are declared as constants. Therefore, after being declared, a trait record variable must not be modified or deallocated. Consequently, if the subclass you are writing requires a different implementation of a trait than its superclass, the subclass should not attempt to modify or deallocate the trait record variable of its superclass. Instead, the subclass simply needs to reinstall the trait (with XmeTraitSet) on itself.

For example, suppose you are writing a subclass of ExmString called ExmStringSubclass. As you know, ExmString installs the XmQTaccessTextual trait. Suppose that ExmStringSubclass wants a different implementation of one of the trait methods of XmQTaccessTextual than ExmString does. In this case, ExmStringSubclass would install XmQTaccessTextual on itself.