Chapter 10. Resource Management and Type Conversion

This chapter is a more thorough discussion of how resources work and how they should be used. This chapter describes in detail the resource file format and the rules that govern the precedence of resource settings. It also describes how to add your own type converter so that you can have application or widget-specific data set through resources. Finally, it describes subresources and how to use them.

This chapter provides a thorough discussion of how resources work and how they should be used. First, we describe how to define resources, the complete syntax of resource files, and the rules that describe the precedence of one resource setting over another. For the sake of completeness, and to make sure that the ideas are presented in context, there is some repetition of material that has been presented earlier.

Next, the chapter describes the resource conversions performed automatically by Xt. As you may recall from the discussion in Chapter 2, Introduction to the X Toolkit and Motif, a value converter is invoked by Xt to convert a resource from the string form specified in resource files to the representation type actually used in the application or widget. For the representation types understood by Xt, simply listing the representation symbol (a constant beginning with XmR, or XtR in standard Xt) in the resource list is enough to make Xt automatically perform the conversion. But if you create a representation type unknown to Xt, you need to write a type converter routine and register it with Xt before the automatic conversion can take place. We discuss both the standard converters and how to write a new one.

Finally, the chapter describes a mechanism Xt provides whereby widgets or applications may have subparts with separate sets of resources. Special routines are provided for setting and getting these resources. The R3 Athena Text widget used subparts to implement replaceable units that provide the data storage and display for text data. This allowed the same central code to edit a disk file or a string. But using subparts is now out of favor; the Athena Text widget and the Motif Text widgets now use objects to accomplish the same modularity (see Chapter 14, Miscellaneous Toolkit Programming Techniques).

Review of Resource Fundamentals

As we've previously discussed, widgets and applications can declare some or all of their variables as resources. Not every variable need be a resource--only those for which values need to be supplied by the user (or for a widget, also by the application programmer) through the Resource Manager. Both applications and widgets may use nonresource variables for internal bookkeeping, or for storing values calculated or otherwise derived from resources.

Resources are defined using an XtResource structure, which is declared as follows:

typedef struct {
    String resource_name;        /* specify using XmN symbol */
    String resource_class;       /* specify using XmC symbol */
    String resource_type;    /* actual data type of variable */
    Cardinal resource_size;        /* specify using sizeof() */
    Cardinal resource_offset;  /* specify using XtOffsetOf() */
    String default_type; /* will be converted to resrce_type */
    XtPointer default_address;   /* address of default value */
} XtResource, *XtResourceList;

For example, Example 10-1 shows three of the resources defined by the Primitive widget:

Example 10-1. Three resources defined by the Motif Primitive widget

static XtResource resources[] = {
       .
       .
       .
    {
    XmNforeground,  /* Resource name is foreground */
    XmCForeground,  /* Resource class is Foreground */
    XmRPixel,       /* Resource type is Pixel */
    sizeof(Pixel),  /* allocate enough space to hold a Pixel value */
    XtOffsetOf(XmPrimitiveRec,
            primitive.foreground),      /* where in instance strct */
    XmRCallProc,    /* type of default value */
    _XmForegroundColorDefault /* Address of default value, or function */
    },
       .
       .
       .
    {
    XmNhighlightColor,
    XmCHighlightColor,
    XmRPixel,
    sizeof (Pixel),
    XtOffsetOf (XmPrimitiveRec, primitive.highlight_color),
    XmRString,
    "Black"
    },
       .
       .
       .
    {
    XmNtraversalOn,
    XmCTraversalOn,
    XmRBoolean,
    sizeof (Boolean),
    XtOffsetOf (XmPrimitiveRec, primitive.traversal_on),
    XmRImmediate, (XtPointer) True
    },
       .
       .
       .
}


The fields in the XtResource structure are used as follows:

  • The resource name is usually similar to the name of the variable being set by the resource; by convention, the resource name begins with a lowercase letter, and no underscores are used to separate multiple words. Instead, the initial character of subsequent words is given in uppercase. For example, the resource name for a variable named border_width would be borderWidth, and the defined constant used to refer to this name would be XmNborderWidth.

    As previously described, the name, class, and representation type of resources are specified in the resource list (and elsewhere in Xt code, but not in user database files) using symbolic constants defined in <Xm/Xm.h> (or <X11/StringDefs.h> in standard Xt), and consist of the resource name, class, or type preceded by the characters XmN, XmC, or XmR, respectively. Use of these constants provides compile-time checking of resource names, classes, and types. Without the constants, a misspelling would not be noticed by the compiler, since resource names, classes, and representation types are simply strings. The misspelling would be considered a real resource at run time. Nothing would happen if it were set from the application, because no widget would actually use it. If, on the other hand, the misspelling were in the widget resource list, the application's setting of the intended resource would have no effect.

    Newly-defined resources may use a name, class, or type constant defined in <Xm/Xm.h> (or <X11/StringDefs.h> if not using Motif), if an appropriate constant exists. Otherwise, the constant is defined in the widget's public header file, or for application resources, in the application itself, or in the application header file, if any.

  • For many resources, the class name is simply the same as the resource name, except that the XmC prefix is used, and, the first letter of the name is conventionally capitalized. For example, the class name constant for the XmNbackgroundPixel resource is XmCBackgroundPixel. However, when appropriate, a single class can be used for a group of related resources. This allows a single setting in the resource database to control the value of multiple resources.

    For example, a widget can have several elements that use pixel values (i.e., colors) as resource settings: background, foreground, border, block cursor, pointer cursor, and so on. Typically, the background defaults to white and everything else to black. If the background resource has a class of Background, and all the other pixel resources a class of Foreground, then a resource file needs only two lines to change all background pixels to offwhite and all foreground pixels to darkblue:

    *Background:     offwhite
    *Foreground:     darkblue
    
  • The representation type of the resource is specified by the resource_type field of the resource list, using a symbolic constant prefixed by XmR. Table 10-1 lists the correspondence between the XmR symbols defined by Xt, and actual C data types or X data types and structures.

    Table 10-1. Resource Type Strings

    Resource Type

    Data type

    XmRAcceleratorTable

    XtAccelerators

    XmRAtom

    Atom

    XmRBitmap

    Pixmap (of depth one)

    XmRBoolean

    Boolean

    XmRBool

    Bool

    XmRCallback

    XtCallbackList

    XmRCallProc

    see final bullet below

    XmRCardinal

    Cardinal

    XmRColor

    XColor

    XmRColormap

    Colormap

    XmRCursor

    Cursor

    XmRDimension

    Dimension

    XmRDisplay

    Display *

    XmREnum

    XtEnum

    XmRFile

    FILE *

    XmRFloat

    float

    XmRFont

    Font

    XmRFontStruct

    XFontStruct *

    XmRFunction

    (*)()

    XmRGeometry

    String - format as defined by XParseGeometry()

    XmRImmediate

    see final bullet below

    XmRInitialState

    int

    XmRInt

    int

    XmRLongBoolean

    long

    XmRObject

    Object

    XmRPixel

    Pixel

    XmRPixmap

    Pixmap

    XmRPointer

    XtPointer

    XmRPosition

    Position

    XmRScreen

    Screen *

    XmRShort

    short

    XmRString

    char *

    XmRStringArray

    String *

    XmRStringTable

    char **

    XmRTranslationTable

    XtTranslations

    XmRUnsignedChar

    unsigned char

    XmRVisual

    Visual *

    XmRWidget

    Widget

    XmRWidgetClass

    WidgetClass

    XmRWidgetList

    WidgetList

    XmRWindow

    Window

    As we'll discuss in detail in Section 10.3.5, Xt automatically converts values in the resource database (which always have the type XmRString, since resource files are made up entirely of strings) into the target type defined by resource_type.

  • The resource_size field is the size of the resource's actual representation in bytes; it should always be specified as sizeof(type) (where type is the C-Language type of the resource) so that the compiler fills in the value.

  • The resource_offset field is the offset in bytes of the field within the widget instance structure or application data structure. The XtOffsetOf() macro is normally used to obtain this value. This macro takes as arguments the data structure type, and the name of the structure field to be set by the resource.

  • If no value is found in the resource database, the value pointed to by the default_address field will be used instead. The type of this default value is given by the default_type field. If the default_type is different from the resource_type, a conversion will be performed automatically in this case as well.

    There are two special resource types that can be used only as the default_type. XmRImmediate means that the value in the default_address field is to be used as the actual resource value, rather than as a pointer to it (or in the case of a string, the value is a pointer to a string, rather than a pointer to a pointer to a string). The other special resource type, XmRCallProc, is a pointer to a function that will supply the default value at run time. We'll demonstrate the use of these values in Section 10.3.3.

How Xt's Resource Manager Works

Xt's resource handling is based on the resource manager built into Xlib, but Xt adds a great deal. While using the resource manager from Xlib is cumbersome, from Xt it is easy: to use resources in existing widgets, all you have to do is write the app-defaults file.

Xt's handling of resources occurs in two stages:

  1. When the application starts up, with a call to XtAppInitialize(), Xt reads the app-defaults file, along with several other resource files, command-line options, and the RESOURCE_MANAGER property stored in the server by the user with xrdb. (Any, all, or none of these may contain data.) It merges all these sources of data into one internal database per screen (per display in R4, per screen in R5) that is used when each widget is created.

  2. Whenever you create a widget, the call to XtVaCreateManagedWidget() reads the resource database and automatically sets widget resources to the values in the database. In order to explain this stage more clearly, we further divide it into two separate steps in the sections that follow. First, Xt compares the settings in the database to the widget's class and instance hierarchy, to find which settings apply to the widget being created. Second, Xt decides which one of the (possibly conflicting) settings that apply to that widget should actually be used.

If the value of a resource is hardcoded by passing arguments to XtVaCreateManagedWidget() or XtVaSetValues(), the hardcoded value overrides the value looked up from the resource database.

To retrieve the value of application resources from the database, an application must make an explicit call to XtGetApplicationResources(), as described in Section 3.6.3.

Basic Syntax of Resource Specifications

As discussed in Chapter 2, each entry in the merged database (and in the source databases) is a resource specification/value pair. For application resources, the specification is the application name followed by a period and the resource name. The value to which the resource is to be set follows, after a colon and optional white space.[62]For example:

xterm.scrollBar:  on

An asterisk can be used as a “wildcard” in place of the application name. For example:

*scrollBar:  on

would set a resource named scrollBar to “on” in any application that recognized a resource of that name.

For widget resources, the specification leading up to the resource name may contain a widget instance or class hierarchy (or a mixed instance/class hierarchy). Some examples are shown below. (Remember that instance names begin with a lowercase letter, while class names begin with an uppercase letter.)

specification                               value
       |                                      |
xbitmap.rowColumn.quit.labelString:          Quit     fully-specified instance hierarchy
XBitmap.RowColumn.PushButton.Foreground:     blue     fully-specified class hierarchy
XBitmap.RowColumn.quit.foreground:           blue     mixed class and instance hierarchy

An instance hierarchy describes the instance names of the widget's ancestors. A class hierarchy describes the class names of the widget's ancestors. This portion of the resource specification may consist of a mixture of instance names and class names (each of which describes one generation in the widget's hierarchy), separated by periods or asterisks.

  • A period (.) is referred to as a tight binding.

  • An asterisk (*) is referred to as a loose binding.

A tight binding means the left component must be the parent of the right component in the instance hierarchy. A loose binding means the left component must only be an ancestor of the right component; there can be any number of levels in the hierarchy between the two.

Loose bindings are preferable because they stand a better chance of working when the instance hierarchy changes. Tight bindings are rarely necessary at every position in the resource specification, since widget names are usually unique and single widgets can be identified by name. Furthermore, it takes more text to specify the complete instance hierarchy for every widget to be set.

Using loose bindings, the instance, class, or instance/class hierarchy may be abbreviated to the point where specifying the hierarchy as a single asterisk would indicate that any instance or class hierarchy (any widget in the application) will match. However, care should be taken when using loose bindings. One common mistake is to abbreviate a hierarchy too much, so that resource settings that were supposed to apply to only one widget now apply to several. This can be particularly serious (and hard to trace) when the resources being set are translation tables or constraints.

The resource name must be the string that appears in the resource name or resource class field in a resource list. This is the value of the XmN or XmC symbolic constant used in that field of the resource list.

Any entry that is not a resource specification/value pair or does not match any resource for any widget in the application or any application resource is quietly ignored (no warning message is printed). This means that a slight error in the resource specification of an entry will cause that entry to be quietly and completely ignored. It is often difficult to detect such errors.

Lines beginning with an exclamation point (!) are treated as comments. Some people have been using # instead, since it is currently supported in MIT's sample implementation of Xt. But # is not mandated in the Xt specification and therefore may be eliminated in future sample implementations from MIT or in a vendor's implementation. You are advised to use the exclamation point. (Even in the MIT implementation, # elicits warning messages from xrdb.)

Wildcarding Resource Component Names

R5 (and later) resource databases allow the character ? to be used to wildcard a single component (name or class) in a resource specification. Thus the specification:

xmail.?.?.Background: antique white

sets the background color for all widgets (and only those widgets) that are grandchildren of the top-level shell of the application xmail. And the specification:

xmail.?.?*Background: brick red

sets the background color of the grandchildren of the shell and all of their descendants. It does not set the background color for the child of the top-level shell or for any popup shells. These kinds of specifications simply cannot be done without the ? wildcard; sometimes the * wildcard does not provide the necessary fine-grained control. To set the background of all the grandchildren of an application shell widget without the ? wildcard, it would be necessary to specify the background for each grandchild individually.

There is one obvious restriction on the use of the ? wildcard: it cannot be used as the final component in a resource specification--you can wildcard widget names, but not the resource name itself. Also, remember that the wildcard ? (like the wildcard *) means a different thing in a resource file than it does on a UNIX command line.

The ? wildcard is convenient in cases like those above, but it has more subtle uses that have to do with its precedence with respect to the * wildcard, as discussed later. First, note the important distinctions between the ? and the * wildcards: a ? wildcards a single component name or class and falls between two periods (unless it is the first component in a specification), while the * indicates a “loose binding” (in the terminology of the resource manager) and falls between two component names or classes. A ? does not specify the name or class of a resource component, but does at least specify the existence of a component. The * on the other hand only specifies that zero or more components have been omitted from the resource.

Merging of Resource Files

XtAppInitialize() constructs the resource database by consulting the following sources of resource settings, in the order shown in the list below. The effect is that if there is a specification that applies to a particular resource of a particular widget instance in more than one of these locations, and these specifications are of equal precedence, then the first one in this list is the one used (the previous edition of this book presented this list in the reverse order). The unfamiliar topics touched upon in this list are discussed in detail in the following sections.

The list shows the R5 merging of files. This has changed since R4, but the result is the same accept as affected by the new customization resource or by the new screen-dependent resources. All filenames and environment variables are for a POSIX-based system.

  1. If the application has defined any command-line options by passing an options table to XtAppInitialize(), values from the command line will override those specified by any other resource settings.

  2. Any values specified on the command line with the -xrm option will be loaded for that instance of the program.

  3. A temporary database is created by parsing the resources contained in the RESOURCE_MANAGER property on the root window of the default screen of the display. If this property does not exist, the contents of the file $HOME/.Xdefaults are used instead. The contents of the property or file are assumed to be entirely in the X Portable Character Set so that the database can be correctly parsed before the application's locale has been set. (See Volume One, Xlib Programming Manual, Third Edition, for information about locale and the X Portable Character Set.)

  4. If a language procedure has been set with XtSetLanguageProc() (see Chapter 14, for more information on the language procedure and the internationalization of Xt programs), the application command line is scanned (but not actually parsed into a database) for the -xnlLanguage option or an -xrm option that specifies the xnlLanguage resource. (See Section 10.2.6, "The Language String" for a discussion of the language string.) Because the command line is scanned before the locale has been set, the value of this resource must be in the X Portable Character Set. If neither command-line option is found, the temporary database is queried for the value of the xnlLanguage resource. The value of this resource, or the empty string if it is not found, is passed to the registered language procedure which sets the locale and returns its name. The return value of the language procedure is associated with the display for future use (for example, by XtResolvePathname()). All future resource specifications will be parsed in the encoding of this locale, and resource databases will have the locale associated with them. XrmLocaleOfDatabase() will return the name of the locale.

  5. The application command line is parsed, and the resulting resource specifications are stored in a newly created database. This database will be augmented with resources from a number of sources and will become the screen resource database.

  6. If a language procedure has not been set, the value of the xnlLanguage resource is looked up in the screen database, the temporary database, and the environment variable LANG. This language string (or the empty string) is associated with the display for future use.

  7. If the XENVIRONMENT environment variable is defined, the resource file it points to is merged into the screen database with XrmCombineFileDatabase(); the new resources do not override existing resource values in the database. If the environment variable does not exist, the file $HOME/.Xdefaults-hostname is used, if it exists. The difference between this and XAPPLRESDIR below is that this is a complete path name including the file name.

  8. The per-screen specifications stored in the SCREEN_RESOURCES property are merged into the database. The new resources do not override existing values in the database. The user can set the SCREEN_RESOURCES property using xrdb. This is intended to be the method whereby the user specifies screen-wide resources (to apply to all clients no matter which system they are running on).

  9. The temporary database which was created in step 1, and which contains the per-server RESOURCE_MANAGER resources is merged into the screen database. The resources in the temporary database do not override the resources in the screen database. The user can set the RESOURCE_MANAGER property using xrdb. This is intended to be the method whereby the user specifies server-wide resources (to apply to all clients no matter which system they are running on).

  10. The screen database being built is associated with the display with a call to XrmSetDatabase(), and the old value of the “display database” is saved so that it can later be restored. The user's application specific app-defaults file is searched for, using XtResolvePathname() with the path specified by the XUSERFILESEARCHPATH environment variable if it exists. If this environment variable does not exist, a default path is used relative to the user's home directory and relative to the value of the XAPPLRESDIR environment variable, if that exists. XtResolvePathname() uses XrmGetDatabase() to find the current database of the display (which is the screen database created up to this step), and uses that database to look up the value of the customization resource for substitution into the path. If a resource file is found in one of these paths, the resource specifications in it are merged into the screen database using XrmCombineFileDatabase(). The new resources do not override the resources already in the database.

  11. The application's app-defaults file is located using XtResolvePathname() and the path specified in XFILESEARCHPATH, or a default path if that environment variable does not exist. As above, XtResolvePathname() looks up the value of the customization resource in the screen database constructed so far. The resources from the app-defaults file are merged into the screen database using XrmCombineFileDatabase(), and do not override resource values already there. The original “database of the display,” which had been stored away in step 8 above, is restored using XrmSetDatabase().

  12. If no app-defaults file is located in step 9 and the application has registered fallback resources with XtAppSetFallbackResources(), then those fallback resources are merged into the screen database without overriding the values already there.

The order in which these various sources are loaded, as shown in the list above, is the order of their priority. That is, those that are loaded first override those loaded later if both specifications have the same precedence and affect the same widget or widgets.

If a resource value is hardcoded in the arguments of the call to create a widget, that value takes precedence over any value for that resource in the resource database. If a widget is created and no setting exists in the database for a particular resource, the value pointed to by the default_address field of the resource list in the widget is used. This is also true for application resources and subresources.

Figure 10-1 shows where Xt looks for resource files and in what order, on most UNIX-based systems. The exact directories are operating system and implementation dependent. Remember that the app-defaults file is written by the application writer, and all the rest of the resource sources are for the user. In practice, few users use more than one or two of these sources of resource settings.

Figure 10-1. Typical resource setting search path on UNIX-based systems


Syntax of Environment Variables

The environment variables listed above that describe paths allow a special syntax involving % (the percent symbol) to substitute various elements into the path.

For example, the XFILESEARCHPATH environment variable is a colon separated path. Under Sun OpenWindows, the openwin script appends the string “/usr/openwin/lib/%T/%N%S” to the XFILESEARCHPATH variable. The effect is that app-defaults files are found in the directory /usr/openwin/lib/app-defaults.

XtDisplayInitialize() reads the environment variables, makes the % syntaxes into paths with XtResolvePathname(), and uses XtFindFile() to actually find the files. %T becomes the type arg of XtResolvePathname(), %S becomes the suffix arg, and %N is the filename arg. Type is a category of files, such as app-defaults, help or bitmap. Suffix is file suffix such as .txt. So in your example, %T is app-defaults, and %N%S is the complete filename.

This syntax can get much more complicated when it includes %l, %L, %t, and %c, all of which have to do with internationalization, and %C, which is for specifying different files for different types of displays (usually color and mono), as described in Section 10.2.11, "Customized Resource Files."

Including Files in a Resource File

The Xrm functions that read resources from files, XrmGetFileDatabase() and XrmCombineFileDatabase() (the latter new in R5), recognize a line of the form:

#include "filename"

as a command to include the named file at that point. The directory of the included file is interpreted relative to the directory of the file in which the include statement occurred. Included files may themselves contain #include directives, and there is no specified limit to the depth of this nesting. Note that the C syntax #include <filename> is not supported; neither Xlib nor Xt defines a search path for included files.

The ability to include files is useful when producing a special app-defaults file for use on a color screen, for example, you can simply include the monochrome app-defaults file and then set or override the color resources as you desire. This technique is particularly useful when producing app-defaults files for use with the customization resource described below. Example 10-2 shows a hypothetical color resource file for the “xmail” application.

Example 10-2. The resource file XMail-color.

! include the basic (monochrome) defaults
#include "XMail"
! and augment them with color
*Background: tan
*Foreground: navy blue
*Command*Foreground: red
*to*Background: grey
*subject*Background: grey

Do not confuse this file inclusion syntax with the #include, #ifdef, etc. syntax provided by the program xrdb. That program invokes the C preprocessor to provide C include, macro, and conditional processing. The include functionality described here is provided directly by Xlib.

The Language String

Xt's resource handling is designed to support resource files in different languages. The goal is to have all the language dependencies of an application in files, so that just by selecting a different set of resource files and localization files at run time, the application will operate correctly in another language.[63]

The language to use is selected with a language string, which is an application resource defined by Xt: “xnlLanguage” (no symbol XmNxnlLanguage is defined for it). The default language string is NULL, which makes Xt operate in English, with all files in the locations used prior to when the internationalization features were added. But when the *xnlLanguage resource, or the LANG environment variable, is set to a language name such as “spanish,” Xt looks first in certain directories for the Spanish app-defaults and user defaults files, before defaulting to the normal app-defaults or user defaults directories.

Note that all app-defaults and user defaults files have the same name (the class name of the application); only the directory in which they are first examined for changes according to the language string. Therefore it is possible to have all the files for all the languages installed on a system at the same time.

The search path for resource files is implementation dependent, but it is always begins with a language string-based directory. Each app-defaults file on UNIX-based systems is normally in a directory /usr/lib/X11/$LANG/app-defaults. When the user sets LANG to “spanish,” for example, Xt looks for an app-defaults file first in /usr/lib/X11/spanish/app-defaults.

The path searched for user defaults files (which have the same name as the app-defaults file) is more complicated. If the XUSERFILESEARCHPATH environment variable is defined, Xt follows that path. If it is not defined but the XAPPLRESDIR environment variable is defined, Xt will look in $XAPPLRESDIR/LANG first before looking in $XAPPLRESDIR. If XAPPLRESDIR is not defined either, Xt looks in $HOME/LANG and then $HOME.

The remaining resource setting sources are not affected by the language string. As described in the previous section, the next source of resource settings merged is the RESOURCE_ MANAGER property set with xrdb, or if not set, .Xdefaults, followed by the file specified by the XENVIRONMENT environment variable, or if not set, the .Xdefaults-host file.

For a summary of the path of resource files searched by Xt, see Figure 10-1.

Most western languages other than English use characters with accents or other marks. These are non-ASCII characters, in the second half of the ISO Latin-1 character set. Most fonts now in the X distribution provide glyphs (bitmaps) for these characters. Therefore, all you have to do is specify these characters in your app-defaults file in the directory named for the desired language. However, most standard English keyboards do not provide a way to type non-ASCII characters (and some computers can't store them). Because of this, the resource file format has been augmented to provide a special syntax for specifying non-ASCII characters. Each character is specified using a backslash followed by a three-character octal number that represents the character. You can use xfd to determine the proper octal number to use. Just click on the desired character to have its index displayed. This index is the number you enter in the app-defaults file. For example, Example 10-3 shows a Spanish app- defaults for xhello.

Example 10-3. An app-defaults file for the Spanish language

*hello.fontList:      *courier-bold*18*iso8859-1
*hello.labelString:   \161Hola, Mundo!

The sequence \161 produces the inverted exclamation point that begins the Spanish exclamatory sentence. It is important to know for sure that the font you choose has the desired glyphs. Note that iso8859-1 means ISO Latin-1, so that if you specify it in your font name, you are guaranteed to have all ISO-Latin-1 characters. Many of the fonts in the misc directory (such as the standard terminal fonts) do not include these characters. Also note that strings such as \161 may not work on systems that can only handle seven bit (ASCII) characters (they ignore the eighth bit).

Screen-specific Resource Strings and Databases

The (pre-R5) function, XResourceManagerString(), returns the contents of the RESOURCE_MANAGER property on the root window of the default screen of the display. This property contains the user's resource customizations in string form, and is usually set with the program xrdb. It is read by XtDisplayInitialize() when creating the resource database to be used by an application. Until R5 this single RESOURCE_MANAGER property contained resources for all screens of a display, and there was no way for a user to specify different resources for different screens (for example, color vs. monochrome).

In R5, xrdb can set resources in the SCREEN_RESOURCES property on the root window of each screen of a display. XtDisplayInitialize() reads the SCREEN_RESOURCES specifications and uses them to override the screen-independent RESOURCE_MANAGER specifications. The resource database that is created in this way is the database of the screen, rather than the database of the display. If the same application is executed on different screens of a display, or if a single application creates shell widgets on more than one screen of a display, a resource database will be created for each screen, and the application instances or shell widgets will find resources in them that are appropriate for that screen.

Recall that XtDisplayInitialize() creates databases using resources from a number of sources other than these window properties, so in many cases resource databases on different screens will contain substantially the same values. Note, however, that the SCREEN_RESOURCES property can be used to set the customization resource and thereby cause different app-defaults files to be merged into different screen databases. Screen-specific resource databases are created by Xt only as needed, so an application running exclusively on the default screen of a two-screen system will not have the overhead of maintaining two per-screen databases.

Two new functions support screen-dependent resources and resource databases. The contents of the SCREEN_RESOURCES property on the root window of a screen are returned by the function XScreenResourceString(). The database of a screen may be obtained with the function XtScreenDatabase(). The function XtDatabase(), which prior to R5 returned the (single) database of the display, is now specified to return the database of the default screen of the display.

The client xrdb has been rewritten for R5 to handle the new screen-specific properties. Any load, merge, or query operation can now be performed on the global RESOURCE_MANAGER property, a specific screen property, all screen properties, or all screen properties plus the global property. This last option is the default and “does the right thing"--the input file is processed through the C preprocessor once for each screen, and resource specifications that would appear in all of the per-screen properties are placed in the global property and removed from the screen-specific properties. With this new system, a defaults file which uses #ifdef COLOR to separate color from monochrome resource specifications can be used to correctly set the values of the screen-dependent and screen-independent properties for a two-screen monochrome-and-color display. An application can then be run on either screen and find the correct user defaults for that screen. Example 10-4 shows a user default file that takes advantage of the new xrdb functionality and the customization resource to set different defaults on color and monochrome screens.

Example 10-4. A user defaults file for color and monochrome screens

! generic, non-color resources
*Font: -*-courier-medium-r-*-*-*-180-75-75-*-*-iso8859-1
xclock.geometry: -0+0
#ifdef COLOR
! resources for color screens here
*Background: grey
*Foreground: navy blue
XTerm*Foreground: maroon
#else
! resources for monochrome screens here
XTerm*reverseVideo: true
#endif
! set the customization resource to get
! special app-defaults, if they exist.
#ifdef COLOR
*customization: -color
#else
*customization: -mono
#endif

When an Xt application creates a shell widget with XtAppCreateShell(), the value of the XtNscreen resource is used to determine what screen the shell should be created on and which screen's resource database should be used to look up the widget's resources. But the value of the XtNscreen resource must be looked up itself. The Xt specification resolves this circular problem by stating that the value of the XtNscreen resource is looked up first in the argument list passed to XtAppCreateShell(), and then in the resource database of the default screen of the display. If it is found, then all further resources are looked up in the resource database for that particular screen. If the XtNscreen is not found, then the widget is created on the default screen, using the database for that screen.

Fallback Resources

As you may recall from Chapters 2, 3, and 4, XtAppInitialize() has an argument in which you can specify fallback resources, which we didn't use. Many applications won't operate properly without their app-defaults file. Fallback resources are a defense in case the user doesn't install the app-defaults file, or if something happens to prevent access to it. They provide minimal application-specific resource settings that either allow the application to run safely or instruct the user to install the app-defaults file properly.

If your app-defaults file is small, you can (and should) put the whole app-defaults file in the fallback resources. However, if your app-defaults is large, it probably makes more sense just to set one or more labels in the application to tell the user that the app-defaults file is not installed properly or cannot be accessed. Remember that the fallback resources are used only if the app-defaults file is not found. Fallback resources are a NULL-terminated list of strings, each containing a resource setting. Example 10-5 shows how they are declared and then passed to XtVaAppInitialize().

Example 10-5. Setting fallback resources in XtAppInitialize

main(argc, argv)
int argc;
char **argv;
{
    XtAppContext app_context;
    Widget topLevel, hello;
    static String fallback_resources[] = {
        "*hello.labelString: App-defaults file not installed or not\
             accessible.",
        "*hello.fontList: *courier-bold*18*iso8859-1",
        NULL,         /* Must be NULL terminated */
    };
	XtSetLanguageProc(NULL, (XtLanguageProc)NULL, NULL);
    topLevel = XtVaAppInitialize(
            &app_context,        /* Application context */
            "NoFile",            /* Application class */
            NULL, 0,             /* command-line option list */
            &argc, argv,         /* command line args */
            fallback_resources,  /* for missing app-defaults file */
            NULL);               /* terminate varargs list */
    .
    .
}


Note that there is also a separate function, XtAppSetFallbackResources(), that can be used to set the fallback resources separately from the XtAppInitialize() call.

Resource Matching Algorithm

When a widget is created, its expanded instance hierarchy and class hierarchy together with its resource names and classes are compared to each entry in the merged resource database. To demonstrate how matches are made, we'll look at a sample widget hierarchy and follow the process of finding the value for one resource of one widget from the merged resource database. Figure 10-2 shows the widget instance hierarchy for the quit widget in the xrowcolumn application shown in Chapter 3, More Techniques for Using Widgets. The figure also shows the corresponding fully specified instance and class names for the quit widget. This section describes how this widget's resources are set by the resource manager.[64]

Figure 10-2. The quit widget in a sample widget hierarchy

We know that quit is a Command class widget and therefore that Xt will be searching the resource database for each resource in Command's resource list (and the resources in its superclasses' resource lists). It will search for one resource at a time. To demonstrate the conflicts that can occur, we'll use the Core resource XmNbackground, which is common to all widgets. It will appear in the resource database as background.

The matching process can be thought of as a process of elimination. Let's assume the merged resource database is as shown in Example 10-6.

Example 10-6. A sample merged resource database

*rowColumn.background:                blue             (entry 1)
*background:                          red              (entry 2)
*quit.background:                     green            (entry 3)
*quit.labelString:                    Quit             (entry 4)
*XmPushButton.background:             yellow           (entry 5)
*XmRowColumn.XmPushButton.background: violet           (entry 6)
*rowColumn*background:                pink             (entry 7)
xrowcolumn.background:                orange           (entry 8)
*?.background:                        purple           (entry 9)

Only resource database entries that specify background as the last element before the colon are possible matches. That eliminates entry 4. The fully specified instance and class hierarchies are then compared with each possible match, beginning with the first component in each hierarchy.

  1. Every entry beginning with the asterisk wildcard binding (*) as well as the one beginning with xrowcolumn, matches xrowcolumn, the first component of the fully specified instance name. All those beginning with * also match the first component of the fully specified class name, XRowColumn. Since entry 8 actually contains the string xrowcolumn, the xrowcolumn component is removed for comparison at the next level. Entry 8 now begins with .background. Similarly, the R5 ? wildcard in Entry 9 matches any single instance or class in the hierarchy, so it matches xrowcolumn or XRowColumn. Therefore, the leading * and the ? in Entry 9 are removed for comparison at the next level. Entry 9 also now begins with .background.

  2. The first component of each resource specification (after removal of previously matched components) is now compared to the second element in the widget's class and instance hierarchies. This should be either rowColumn or XmRowColumn. All the entries that begin with * still match, because * matches any number of levels in the hierarchy. However, there is no second element in entries 8 and 9, since the resource name background is not counted. Therefore, entries 8 and 9 are eliminated. Also, since entries 1, 6, and 7 actually contain the strings rowColumn or XmRowColumn, the leading asterisk and the strings rowColumn and XmRowColumn are removed before comparison of the next level. Example 10-7 shows the resource database as it would appear after the components and entries eliminated so far.

    Example 10-7. Sample resource database with eliminated entries and components

    .background:                   blue                  (entry 1)
    *background:                   red                   (entry 2)
    *quit.background:              green                 (entry 3)
    *XmPushButton.background:      yellow                (entry 5)
    .XmPushButton.background:      violet                (entry 6)
    *background:                   pink                  (entry 7)
    

    Note that entries 2 and 7 are now duplicates except for the resource value. The resource manager actually eliminates one of these entries based upon the levels at which each entry matched, whether the instance name or class name matched, whether a tight or loose binding was used, and which had more elements specified. These are the precedence rules to be described in the next section. In order to keep the example clearer, we'll pretend that the resource manager keeps the information necessary to apply all the precedence rules, and keeps all the entries, until the end.

  3. Now the contents of the resource database are compared to the third component in the widget's instance and class hierarchies, quit and XmPushButton. As usual, anything beginning with an asterisk or anything beginning with a period (.) followed by either the expected class or instance name is a match. This matches all but entry 1, which is eliminated.

    Before going on to the next comparison, any components that matched specifically (a . or * followed by either string) are removed, which results in the resource database shown in Example 10-8.

    Example 10-8. Resource database after final elimination of entries and components

    *background:     red                     (entry 2)
    .background:     green                   (entry 3)
    .background:     yellow                  (entry 5)
    .background:     violet                  (entry 6)
    *background:     pink                    (entry 7)
    

    Now you see that we are left with only the resource names and tight or loose bindings. The matching process is finished, and the precedence analysis begins. The next section describes the precedence rules and then finishes this example to determine the priority of the finalist entries.

Resource Precedence Rules

Because of the way merging works, no two resource specifications in the merged resource database will be alike. (Remember that we are using the term specification for the part of the resource setting up to and including the colon.) For example, the merged database could never contain both of the following:

XBitmap*rowColumn*background: green
XBitmap*rowColumn*background: red

because the merging process would remove the setting that appeared earlier in the list of database sources (or that appeared later in a single file).

However, the database could contain two or more resource settings that apply to the same resource of the same widget, because of differences in the widget class or instance hierarchy or the bindings. For example, the database could contain:

XBitmap*rowColumn*background: green
XBitmap*quit.background: red

If the quit button is a child of rowColumn, both settings apply to the quit button's background.

The resource manager provides a set of rules that govern which setting takes precedence in cases where there are two settings for the same resource of the same widget. Here are the three rules:

  1. A resource that matches the current component by name, by class, or with the ? wildcard takes precedence over an resource that omits the current component by using a *.

    XBitmap*Form.quit.background:      and
    XBitmap*Form.Command.background:   and
    XBitmap*Form.?.background:         take precedence over
    XBitmap*Form*background:
    
  2. A resource that matches the current component by name takes precedence over a resource that matches it by class, and both take precedence over a resource that matches it with the ? wildcard.

    *quit.background:                  takes precedence over
    *Command.background:               takes precedence over
    *?.background:
    
  3. A resource in which the current component is preceded by a dot (.) takes precedence over a resource in which the current component is preceded by a *.

    *box.background:                   takes precedence over
    *box*background:
    

Situations where both rule 2 and rule 3 apply often cause confusion. In these cases, remember that rule 2 takes precedence since it occurs earlier in the list above. Here is an example:

*box*background:                   takes precedence over
*box.Background:

To understand the application of these rules, let's return to our extended example. In the course of developing that example, we eliminated information about the level at which components occurred. However, the actual process of matching applies the precedence rules at each step. As a result, let's start again with the original appearance of the entries that pass the matching test. The remaining five as they appeared originally are shown in Example 10-9.

Example 10-9. Resource database finalists in original form

*background:                          red            (entry 2)
*quit.background:                     green          (entry 3)
*XmPushButton.background:             yellow         (entry 5)
*XmRowColumn.XmPushButton.background: violet         (entry 6)
*rowColumn*background:                pink           (entry 7)

From here on, we will determine not only which one of these five will take effect, but the actual precedence of the five. In other words, once the one with highest precedence is determined, we'll see which would take effect if that one was commented out, and so on.

The precedence rules are applied in order to determine the order of the finalist entries.

  1. Rule 1 specifies that a specification that contains higher components in the instance or class hierarchy takes precedence over one that contains only lower ones. The highest components that appear in our example are rowColumn and XmRowColumn in entries 6 and 7. Therefore, these two have higher priority than any others.

  2. To choose between these two, we continue to Rule 2. Instance names (rowColumn) take precedence over class names (XmRowColumn). Therefore, entry 7 has the highest precedence, followed by entry 6. Note that the precedence comparison of two finalists proceeds in the same manner as the original matching--from left to right in the entry, one component at a time.

  3. To determine the precedence of the remaining three entries, 2, 3, and 5, we begin again with Rule 1. However, Rule 1 does not apply because no two entries here specify different levels in the hierarchy. Entries 3 and 5 contain the quit level and entry 2 nothing (an asterisk does not count for Rule 1 because it is not a specified level--it is any level). Rule 2 specifies that the instance name quit takes precedence over the class name XmPushButton, and therefore entry 3 has higher priority than entry 5. Rule 3 does not apply, because no two entries are identical except for binding. Because of Rule 4 we know that both entries 3 and 5 are higher priority than entry 2, because 3 and 5 state a name or class that is omitted in 2.

Therefore, the final precedence is as shown here:

1.    *rowColumn*background:                pink    (entry 7)
2.    *XmRowColumn.XmPushButton.background: violet  (entry 6)
3.    *quit.background:                     green   (entry 3)
4.    *XmPushButton.background:             yellow  (entry 5)
5.    *background:                          red     (entry 2)

People get used to the fact that they can set the resources of all the children of rowColumn with something like entry 7, but then are shocked to find that nothing happens when they attempt to override entry 7 with entry 3--entry 3 seems more specific to them. Even the following entry (using a class name) takes precedence over entry 3 because the rule about being higher in the widget hierarchy carries more weight than the rule that instance names take precedence over class names:

*XmRowColumn*background:    pink        (entry 7)

Finally, a note about creating app-defaults files. Consider what happens when users specify a line like *Background: grey in their personal resource files. They would like to set the background of all widgets in all applications to grey, but if the app-defaults file for the application “xmail” has a specification of the form *Dialog*Background: peach, the background of the dialog boxes in the xmail application will be peach-colored, because this second specification is more specific. So if they really don't like those peach dialog boxes, (pre-R5) users will have to add a line like XMail*Background: grey to their personal resource files, and will have to add similar lines for any other applications that specify colors like “xmail” does. The reason this line works is rule 1 above: at the first level of the resource specification, “XMail” is a closer match than *.

This brings us to the specific reason that the ? wildcard was introduced in R5: any resource specification that “specifies” an application name with a ? takes precedence over a specification that omits the application name with a *, no matter how specific the rest of that specification is. So in R5, the frustrated users mentioned above could add the single line:

?*Background: grey

to their personal resource files and achieve the desired result. The sequence ?* is odd-looking, but correct. The ? replaces a component name, and the * is resource binding, like a dot (.).

The solution described above relies, of course, on the assumption that no app-defaults files will specify an application name in a more specific way than the user's ?. If the “xmail” app-defaults file contained one of the following lines:

xmail*Dialog*Background: peach
XMail*Background: maroon

then the user would be forced to explicitly override them, and the ? wildcard would not help. To allow for easy customization, programmers should write app-defaults files that do not use the name or class of the application, except in certain critical resources that the user should not be able to trivially or accidentally override. The standard R5 clients have app-defaults files written in this way.

The moral of this story is that you should choose the bindings in your resource file to reflect how easy (or difficult) you want to make it for users to override your specifications. (But of course, even a specification containing only tight bindings can still be overridden by a user specification that also includes all tight bindings.)

Be careful with tight bindings, however. Since there are no messages telling you which resource specifications are actually being used, you can be tricked into thinking that you have set resources that you actually haven't.

Figure 10-3. Steps in matching resource entries for one resource of widget being created

A useful tool for figuring out the priority of resource files is the appres utility. You specify the class name of the application on the command line, and appres shows you what resource settings that application will see when run. Note, however, that this does not tell you how these resource settings will actually apply to the widgets in the application, since appres has no knowledge of the widget instance hierarchy in your application.

Another tactic in tracing resource settings is to build a routine into your application that gets and prints all the critical resources of a widget. Using XtGetResourceList() you can get the list of resources supported by a widget class. Then you can query each of those resources and print most of them out. Remember that certain resources are compiled into internal forms, so you can't print out translation tables, accelerator tables, or callback lists. It may also be difficult to interpret other values, since many of them will have already been converted from the string form in the resource file into the most convenient internal form. For example, colors will have been changed from strings such as “red” into a pixel value (a number which could change each time the application is run).

Customized Resource Files

In X11R4, it was possible to install an application with multiple app-defaults files so that users could use the xnlLanguage resource to specify what language the application should display itself in. But it was not possible to install one app-defaults file for use on monochrome screens and another for use on color screens and allow the user to specify, through a resource, which file was to be used. Applications are moved between color and monochrome environments far more often than they are ported to other languages, and in X11R5 the X Toolkit allows a user to easily specify which resources should be used.

You may recall that XtResolvePathname() searches for a file by performing printf-type string substitutions on a specified or default directory path. The string “%L” is replaced with the language string (the value of the xnlLanguage resource), for example, and the string “%T” is replaced with the type of the file--"app-defaults,” “bitmap,” “help,” etc. In X11R5, XtResolvePathname() supports a new substitution variable “%C,” which is replaced with the value of the application resource named customization and of class Customization. It is intended that the default or user-provided XFILESEARCHPATH will contain filename path components of the form %N%C%S--name, customization, suffix. Then a system administrator could install app-defaults files (which do not have suffixes) like XMail-color and XMail-mono and a user could run the xmail application as follows:

xmail -xrm "*customization: -color" &

in order to get the app-defaults file with color defaults. (Unfortunately, X11R5 Xt does not define a standard -customization command-line option to set the value of this resource.) The system administrator would also have to install an uncustomized app-defaults file, XMail, for use when no customization is set. This file could be a symbolic link to the monochrome file, or it could be a base resource file that contains all the resources except those that affect color. Then the customized files could simply include the base file with the new #include feature. The hyphen in the customization name (-color) is not part of the Xt specification; it is simply a useful convention that makes for legible customized resource filenames (like XMail-color).

The customization resource was designed primarily to allow color customizations, but there are other possibilities as well: app-defaults files with customized translations that work with a single-button mouse might be installed with a -single-button customization, or files customized for use within a specific group or department of an organization could be selected by department name. Note, however, that because there is no way to specify multiple customizations with the single customization resource, (e.g., users couldn't specify that they wanted both color resources and single button translations) this technique should not be overused. In general, it should be reserved for the color vs. monochrome customization for which it was designed.

A related change in X11R5 is to the Xt specification of the default paths to be used when the environment variables, XFILESEARCHPATH and XUSERFILESEARCHPATH (on UNIX systems), are not defined. In X11R4, these paths were required to contain specific directories in a specific order. The individual directories in X11R4 had a fixed substitution order: language (optionally), type, name, and suffix. This order meant that an application's auxiliary files (app-default, bitmaps, help text, etc.) had to be installed in scattered locations throughout a file system. In X11R5, the default paths still have a required number of entries in a specified order, but the order in which substitutions appear in these path entries is no longer specified. This means that vendors may choose default paths for their implementations that allow auxiliary files to be installed in a single directory. The MIT implementation retains the substitution order of X11R4, except for the insertion of the “%C” customization substitution between the name and suffix substitutions. When using a vendor-supplied X11R5, be sure to check your documentation for the default file search path. When writing a Makefile or Imakefile to distribute with an application, bear in mind that app-defaults files, bitmap files, help files, and so on may not be installed in the “usual” places on all systems.

The XtNbaseTranslations Resource

Until R5, there was an annoying problem with specifying translation tables as resources. Because a translation table is a multi-line resource, it is useful to be able to override or add individual lines to the table rather than always replace the table. Application developers often do this in their application's app-defaults file, using the #override or #augment directive as part of the XtNtranslations resource value. Sometimes, though, users would also like to add or override individual translations, but if they specify a value for the XtNtranslations resource in a personal resource file, it will take precedence over the resource in the application's app-defaults file, and the application's customizations will be lost from the database before the widget ever sees them. So until R5, users had to duplicate the application's translation settings in its app-defaults file and then modify their copy.

In R5, there is a new resource, XtNbaseTranslations, that can be used by an application developer to specify a base set of translations that will be correctly overridden, augmented, or replaced by the value of the XtNtranslations resource. Note that this is an Xrm resource, but is not a widget resource in the usual Xt sense of the word (the Xt specification uses the term “pseudo-resource”)--it does not correspond to an instance field in Core or any other widget class, and it cannot be set with XtSetValues() or queried with XtGetValues(). The value of the translations field of the Core widget is handled specially by the Xt resource management code. The final value for a widget's translations are obtained as follows:

  1. The default value of a widget instance's translations are obtained from the Core widget class field tm_table. These translations are specified by the widget writer.

  2. When a widget is initialized, the Intrinsics' resource management code looks up the value of the XtNbaseTranslations resource for that widget, and if it exists, uses the translations it specifies to replace, override, or augment the widget's default translations. These translations are intended to be specified exclusively by the application developer in the application's app-defaults file.

  3. Finally, the value of the XtNtranslations resource is looked up for the widget, and if it exists the value is used to replace, override, or augment the widget's translations (which may have once already been replaced, overridden, or augmented). These translations are intended to be specified by the end user of the application.

For maximum flexibility to the user, all R5 applications should specify translations in a resource file using the XtNbaseTranslations resource rather than with XtNtranslations. If the same resource file is to be used by applications compiled under both the R5 and R4 versions of the Intrinsics, then the resource file should specify the application's translations as the value of both resources. Under R4, the XtNbaseTranslations resource will be ignored, and under R5, any value of the XtNtranslations resource specified by the user will override the value specified in the application's app-defaults file.

Type Conversion

You already know that Xt is capable of converting resource values from the string representation specified in resource files to the actual type required in code. In fact, Xt does so automatically if the resource list is properly written. This section describes this process in more detail, and tells you how to create converters for converting your own data types.

In R4, a new set of interfaces for type converters was introduced. The new interfaces support display-specific conversions and better control of converted-value caching. This section describes these new interfaces, but does not describe the older ones. Be aware that many existing widgets and applications still use the older interfaces.

Conversions from XmRString

The primary purpose of converters is to allow resource files to contain string values for nonstring program variables. (They are also used internally to convert default values that cannot be specified easily in the desired data type.) Secondly, converters confine the details of data conversion to a single routine that is registered with Xt. This is a big benefit because users of the converted types need not know the details of how the conversion takes place, or the internal definition of the target type.

Xt provides converters from XmRString to the representation types listed in Table 10-2. You can use these representation types as target types in resource lists in applications or widgets, without having to register them, if you want the user to be able to specify the resource in a resource file. Note that if you are using standard Xt, the various representation types begin with XtR rather than XmR.

Table 10-2. Built-in Type Converters from XmRString

Target Type

Description of Converter

XmRAcceleratorTable

Compiles a string accelerator table into internal accelerator format (no need to call XtParseAcceleratorTable() ).

XmRAtom

Converts a string property name into the corresponding Atom.

XmRBoolean

Converts strings "true", "false", "yes", "no", "on", and "off" to corresponding Boolean value (case insensitive).

XmRBool

Same as for XmRBoolean .

XmRCursor

Given a standard X cursor name, returns a cursor ID.

XmRDimension

Converts a width or height value to a Dimension .

XmRDisplay

Given a display name, opens the display and returns a Display structure.

XmRFile

Given a filename, opens the file and returns the file descriptor.

XmRFloat

Converts a numeric string to floating point

XmRFont

Given a font name, loads the font (if it is not already loaded), and returns the font ID. See Appendix B, Specifying Fonts and Colors , for more information on legal values. The value XtDefaultFont will return the default font for the screen.

XmRFontSet

Given a base font name list, creates a font set needed to display text in the user's locale, and returns a font set ID and the list of missing charsets if any. The value XtDefaultFontSet will return the default font set determined by the user's locale.

XmRFontStruct

Given a font name, loads the font (if it is not already loaded), and returns a pointer to the FontStruct containing font metrics. The value XtDefaultFont will return the default font for the screen.

XmRGeometry

Given a standard geometry string, this converter simply copies a pointer to the string and calls it a new resource type.

XmRInitialState

Converts strings "Normal" or "Iconic" into the symbols NormalState or IconicState .

XmRInt

Converts a numeric string to an integer.

XmRPixel

Converts a color name string (e.g., "red" or "#FF0000") into the pixel value that will produce the closest color possible on the hardware. See Appendix B, Specifying Fonts and Colors , for more information on legal values. The two values XtDefaultBackground and XtDefaultForeground are always guaranteed to exist, and to contrast, on any server. However, Motif calculates its own colors to implement its four-color shadow scheme.

XmRPosition

Converts an x or y value to a Position .

XmRShort

Converts a numeric string to a short integer.

XmRTranslationTable

Compiles string translation table into internal translation table format (no need to call XtParseTranslationTable() ).

XmRUnsignedChar

Converts a string to an unsigned char.

XmRVisual

Converts a string specifying a visual class to a pointer to a supported visual structure of that class.

If there is no converter from XmRString to a particular resource type, it may not be possible to specify that resource type in a resource file. For example, there is no converter for XmRCallback since it would be meaningless to specify a function in a resource file. The only ways to set a callback resource are with XtAddCallback() or a static callback list declared in the application.

Most of the Xt converters allow the resources of the base classes Core, Composite, Constraint, and Shell to be set from resource files. Motif provides numerous converters that allow the resources of its widgets to be set from resource files. However, not all Motif widget resources can be set from resource files, because the needed converters are not provided. (As of Motif 1.2, Motif now includes a StringToWidget converter, which allows Form and other constraint resources to be set from resource files.) Motif provides converters from XmRString to Widget, Window, Char, XmFontList, XmString, KeySym, HorizontalPosition, HorizontalDimension, VerticalPosition, VerticalDimension, BooleanDimension, CharSetTable, KeySymTable, ButtonType, XmStringTable, StringTable, AtomList, and Cardinal.

In addition, the Xmu library contains several useful converters. For example, Xmu provides a converter that converts from a filename (string) to a bitmap suitable for use as an icon pixmap. Even though Xmu (like Xaw) is not part of the X Consortium standard, it is part of MIT's core distribution and is available on most systems. However, because the Xmu converters are not built into Xt, you need to register them with a call to XtSetTypeConverter() or XtAddConverter() (from widget code) or XtAppSetTypeConverter() or XtAppAddConverter() (from application code) before using them in an application or widget. (We'll describe the converters and show how to register them in Section 10.3.4, "Registering Type Converters.")

Other Built-in Type Conversions

While the conversions from XmRString are the most widely used, since they allow a resource to be specified from a resource file, there are also a number of built-in converters between other data types, for use internally within Toolkit programs.

Most commonly, these converters are used to convert between the resource_type and default_type fields of a resource definition.

Table 10-3 lists those converters automatically recognized by Xt.

Table 10-3. Other Built-in Converters

From

To

Description of Converter

XmRColor

XmRPixel

Converts an XColor structure to a pixel value.

XmRPixel

XmRColor

Converts a pixel value to an XColor structure.

XmRInt

XmRBoolean

Converts an int to a Boolean .

XmRBool

Converts an int to a Boolean .

XmRColor

Converts an int to an XColor .

XmRDimension

Converts an int to a Dimension .

XmRFloat

Converts an int to a float .

XmRFont

Converts an int to a Font .

XmRPixel

Converts an int to a pixel value.

XmRPixmap

Converts an int to a Pixmap .

XmRPosition

Converts an int to a Position .

XmRShort

Converts an int to a short .

XmRUnsignedChar

Converts an int to an unsigned char .

For example, the default value of the Core resource XmNborderPixmap is set as shown in Example 10-10.

Example 10-10. A resource definition converting an integer to a pixmap

static XtResource resources[] = {
    .
    .
    .
     {
    XmNborderPixmap,
    XmCPixmap,
    XmRPixmap,
    sizeof(Pixmap),
    XtOffsetOf(CoreRec, core.border_pixmap),
    XtRImmediate,
    (XtPointer) XtUnspecifiedPixmap
     },
    .
    .
    .
}


(Note that the code for the base classes defined by Xt use constants beginning with Xt, not Xm. However, the value of each of these constants is the same in both versions.)

The specified default value XtUnspecifiedPixmap is an integer defined to have a value that does not equal the constant CopyFromParent or any valid Pixmap ID. The initialize method for the Core widget class checks for this value, and does not set the background window attribute unless the application or a resource file has set the XmNborderPixmap resource to some value other than the default.

Special Resource Defaults That Do Not Use Conversion

There are two special values, XmRImmediate and XmRCallProc that can be used only in the default_type field of a resource definition. These values require no type conversion. The value provided in the default_address field must be of the correct type.

The type XmRImmediate means that the value in the default_address field is the default value itself, not its address. For a string, this means that the default_address field is a pointer to the string, not a pointer to a pointer.

In Example 10-11, the value in the default_address field of the XmNheight resource definition is the actual default--in this case, zero.

Example 10-11. A resource definition using XmRImmediate

static XtResource resources[] = {
    .
    .
    .
     {
    XmNheight,
    XmCHeight,
    XmRDimension,
    sizeof(Dimension),
    XmOffsetOf(RectObjRec, rectangle.height),
    XmRImmediate,
    (XtPointer) 0
     },
    .
    .
    .
};


The type XmRCallProc means that the value in the default_address field is a pointer to a function. This function is of type XtResourceDefaultProc,[65] and it is expected to retrieve the desired default value at run time. When the widget instance is created, the function is automatically invoked with these parameters: the widget ID, the resource offset, and a pointer to the XrmValve in which to store the result. The function should fill in the addr field of the XrmValue with a pointer to the default data in its correct type.

In Example 10-12, the value in the default_address field of the XmNscreen resource definition is the name of a function that retrieves the screen on which the widget is displayed.

Example 10-12. A resource definition using XmRCallProc

static XtResource resources[] = {
    .
    .
     {
    XmNscreen,
    XmCScreen,
    XmRPointer,
    sizeof(int),
    XtOffsetOf(CoreRec, core.screen),
    XmRCallProc,
    (XtPointer) XtCopyScreen
     },
    .
    .
};


Example 10-13 shows an example of an XtResourceDefaultProc.

Example 10-13. An example of an XtResourceDefaultProc

/*ARGSUSED*/
void XtCopyScreen(widget, offset, value)
     Widget        widget;
     int           offset;
     XrmValue     *value;
{
     value->addr = (XtPointer)(&widget->core.screen);
}


Registering Type Converters

As noted earlier, not every representation type symbol defined in Xm.h is supported by a built-in converter, though Motif and the Xmu library do provide some of the most important converters that are missing in Xt. In addition, you can define your own resource types, and write converter routines to convert from a string representation in a resource file to the appropriate data type. (You can write converters from any type to any other type, but converters from String are by far the most useful.)

Table 10-4 lists the converters from XmRString provided by the Xmu library.

Table 10-4. Xmu Converters

From

To

Description of Converter

XmRString

XmRBackingStore

The XmuCvtStringToBackingStore converter converts the strings "NotUseful", "WhenMapped", and "Always" (in any case) into the corresponding constants (in proper case) for use in setting the backing_store window attribute. (See Volume One, Xlib Programming Manual , for details on backing store.)

XmRBitmap

The XmuCvtStringToBitmap converter takes the string filename of a file in standard X11 bitmap format and creates a one-plane pixmap containing that bitmap data.

XmRCursor

The XmuCvtStringToCursor and XmuCvtStringToColorCursor converters convert one of the standard cursor names (from <X11/cursorfont.h ), a font name and glyph index of the form "FONT fontname index [[font] index]", or a bitmap file name as in XmRPixmap below, and converts it to an X Cursor .

XmRGravity

XmRJustify

The XmuCvtStringToJustify converter converts the strings "right", "left", or "center", in any case, to an enumeration constant suitable for use by a justify resource. This converter is used by the Athena Label widget.

XmRLong

Converts a string to a long integer.

XmROrientation

The XmuCvtStringToOrientation converter converts the strings "horizontal", or "vertical", in any case, to an enumeration constant suitable for use by an orientation resource. This converter is used by the Athena Scrollbar widget.

XmRShapeStyle

Converts the strings ShapeRectangle , ShapeOval , ShapeEllipse , and ShapeRoundedRectangle in shape style constants, for use in making Xmu calls that access the Shape extension.

XmRWidget

The XmuCvtStringToWidget and XmuNewCvtStringToWidget converters convert a widget name into the corresponding widget ID. These are old-style and new-style converters respectively. This convert is commonly used to specify the relative positions of the children of constraint widgets, as in the Athena Form widget resources fromHoriz and fromVert . Motif 1.2 provides its own String-To-Widget converter.

XmRFunction

XmRCallback

Converts a function pointer to a callback list containing that function.

Whether defined in Xmu or in your own program, a converter other than those built into Xt or Motif must be registered with a call to XtAddConverter() or XtSetTypeConverter() before a resource list is used that references the converted types. Resource lists are used when widgets are created or when the application calls XtGetApplicationResources(). In the application, a converter must be registered after XtAppInitialize() but before XtGetApplicationResources().

Within a widget, the class_initialize method is the standard place to register type converters. This method is responsible for doing processing that should be done only once when the first instance of a particular class is created.

Example 10-14 shows the code needed to register the XmuCvtStringToJustify converter in a widget. As noted above, this converter would be used for a resource (such as the Athena Label widget's XtNjustify resource) designed to give the user the option of justifying text (or a graphic object) to the right, left, or center of a widget.[66]

Example 10-14. Registering a type converter

static void
ClassInitialize()
{
        .
        .
        .
     XtSetTypeConverter(XmRString,   /* source type */
             XmRJustify,             /* target type */
             XmuCvtStringToJustify,  /* converter routine */
             (XtConvertArgList) NULL,
             /* args for converter routine */
             0                       /*# args for converter routine */
             XtCacheAll,             /*  caching instructions */
             NULL);                  /*  destructor function */
}


Note that XtSetTypeConverter() was added to Xt in R4. Its predecessor, XtAppAddConverter(), is still available and can still be used, but has reduced functionality. XtAppAddConverter() cannot be used for conversions that require a server query (such as those involving colors or fonts), and does not provide the caching control provided by XtSetTypeConverter(). Note that the explicit application context version of the call (XtAppSetTypeConverter()) is not used in this case, because the class_initialize method does not pass in a widget from which the application context could be determined.

The first two arguments of XtSetTypeConverter() are the source and target type, respectively, specified using XmR symbolic constants defined in <Xm/Xm.h> (or defined in an application header file or the widget public header file if the type is not standard in Xt).

The third argument is the name of the converter routine, which by convention contains the string Cvt. The Xmu converters all add the prefix Xmu.

The fourth and fifth arguments of XtSetTypeConverter() are an argument list that will be passed to the converter routine when it is called. Some converter routines require information about the context in which the converter is called. This is usually passed in via an XtConvertArgRec structure, as described in the next section. If no arguments are needed, the fourth and fifth arguments of XtSetTypeConverter() can be NULL and 0 respectively.

The sixth argument specifies whether the results of the conversion should be cached. The basic symbols are XtCacheNone, XtCacheAll, and XtCacheByDisplay. Expensive conversions should be cached. Xt caches resource values to avoid repetitive conversions of the same value, which are common in an application made up of many identical widgets. It is especially important to cache conversions that require round-trips to the server, such as color, font, and atom conversions. But it is wasteful of memory to cache silly conversions such as XtCvtStringToGeometry, which actually doesn't do any conversion or even validity checking. (The XmRGeometry representation type is a geometry string, as defined by XParseGeometry(). It takes no conversion to convert XtRString to XtRGeometry. The converter could at least test the string to make sure it looks like a geometry string.) Another benefit of caching is that Xt remembers unsuccessful conversions and efficiently generates warning messages without attempting any conversion more than once.

The symbol XtCacheRefCount can be ORed with any of the above values, in which case Xt keeps track of how many widgets still exist that used a converted value, and frees the cached value when the count reaches zero. The application, if it uses the converted value in a call to XtGetApplicationResources(), is also counted as one reference. Reference counting is needed only if an application destroys widgets whose resource values may take up extensive space. Because reference counting takes up space and requires time, it should not be done unless necessary. To control reference counting on a widget-by-widget basis, an application must explicitly set the XmNinitialResourcesPersistent resource to False for each widget whose conversions are to be reference counted. These should be widgets that might be destroyed before the application exits.

The seventh argument of XtSetTypeConverter() is a pointer to a procedure called a destructor. If the reference count for a particular resource reaches zero, Xt calls the destructor function, and removes the resource value from the conversion cache. The destructor will also be called when XtCloseDisplay() is called if the converter was registered with XtCachePerDisplay. Before calling XtCallConverter() or XtConvertAndStore() to convert a value, the client must allocate memory in which to place the result. The job of the destructor is to deallocate this memory. If you are allowing Xt to convert data automatically, by declaring a resource list, Xt allocates the memory, and you don't need a destructor (so specify NULL). If you plan to call XtCallConverter() from an application, and you allocate memory statically for the converted value, you don't need a destructor function and can specify NULL for this argument. A destructor is needed only if you allocate memory dynamically for an explicit call to XtCallConverter(). (Remember that reference counting happens only if the converter is registered with XtCacheRefCount set and XmNinitialResourcesPersistent set to False for at least one widget. If these conditions are not met, the destructor is only called when XtCloseDisplay() is called and if registered with XtCachePerDisplay.)

Xt provides several functions for manipulating reference counts, which are rather obscure and mentioned here only for completeness. XtAppReleaseCacheRefs() explicitly decrements the reference counts for resource values converted with XtCallConverter(). This would be used in the rare occasion where a value has been cached but you don't want it cached. If this is the last reference to the conversion, the registered destructor is called. Xt also defines two built-in callback functions that decrement reference counts, XtCallbackReleaseCacheRef() and XtCallbackReleaseCacheRefList().

Passing Arguments to a Type Converter

Some type converters need to be registered with additional arguments that provide information needed during the conversion. For example, XmuCvtStringToWidget needs to be passed the parent of the current widget in the application, so that it can compare the name specified in the resource to the names of the children of the parent. Example 10-15 shows the code used by a widget to register the Xmu string-to-widget converter.

Example 10-15. Adding a converter with arguments

static void ClassInitialize(w)
Widget w;
{
     static XtConvertArgRec parentCvtArgs[] = {
          {
               XtWidgetBaseOffset,
               (XtPointer)XtOffsetOf(CoreRec, core.parent),
               sizeof(CoreWidget)
          }
     };
          .
          .
          .
     XtSetTypeConverter(XtRString,
             XtRWidget,
             XmuCvtStringToWidget,
             parentCvtArgs,
             XtNumber(parentCvtArgs),
             XtCacheAll,
             NULL);
}


The format of the argument list for XtSetTypeConverter() shown in Example 10-16 looks complicated, but in practice almost all converter argument lists will look very similar to the one in this example. The argument list is specified as an XtConvertArgRec:

typedef struct {
    XtAddressMode address_mode;
    XtPointer address_id;
    Cardinal size;
} XtConvertArgRec, *XtConvertArgList;

The address_mode field specifies how the address_id field should be interpreted. address_id itself is a pointer to the needed data, an offset within the widget instance structure to the data, or a function that provides the needed data. The size field specifies the length of the data in bytes.

By specifying the address mode as XtWidgetBaseOffset (see below), you can use XtOffsetOf() to find the offset of appropriate data in the instance structure.[67] All you have to do is change the name of the instance structure field (in this case core.parent), and the structure type that contains that field (in this case WidgetRec). Notice that a pointer to this structure type appears in the sizeof call (in this case Widget).

You have just seen the normal way of specifying the converter arguments. Using different XtAddressMode values you can also specify them in other ways. The enumerated type XtAddressMode (defined in <X11/Convert.h>) specifies the possible values for the address_mode field:

typedef enum {
          /* address mode parameter representation */
         XtAddress,             /* address */
         XtBaseOffset,          /* offset */
         XtImmediate,           /* constant */
         XtResourceString,      /* resource name string */
         XtResourceQuark,       /* resource name quark */
         XtWidgetBaseOffset,    /* offset */
         XtProcedureArg         /* procedure to call */
} XtAddressMode;
  • XtAddress causes address_id to be interpreted as the address of the data.

  • XtBaseOffset causes address_id to be interpreted as the offset from the widget base address. XtWidgetBaseOffset (see below) is now slightly preferred, since it works with objects and gadgets as well as widgets.

  • XtImmediate causes address_id to be interpreted as a constant.

  • XtResourceString causes address_id to be interpreted as the name of a resource that is to be converted into an offset from the widget base address.

  • XtResourceQuark causes address_id to be interpreted as a quark--that is, as an internal compiled form of an XtResourceString.

  • XtWidgetBaseOffset is similar to XtBaseOffset except that it searches for the closest windowed ancestor if the object is not a subclass of Core. This must be used in the resource list of objects and gadgets, but can also be used for widgets.

  • XtProcedureArg specifies that address_id is a pointer to a procedure to be invoked to return the conversion argument. address_id must contain the address of a function of type XtConvertArgProc. This function type takes three arguments: an object (or widget), pointer to size (Cardinal *), and a pointer to an XrmValue. The value is returned in the XrmValue.

In most cases, you will use XtWidgetBaseOffset in widgets, gadgets and objects, as shown in Example 10-15.

When registering a type converter in an application rather than a widget, the structure field specified in the argument list shown in the example would be a field of the AppData structure instead of the instance part structure, and CoreRec would be replaced by AppData.

Explicitly Invoking a Converter

Converters are normally invoked by Xt because the types they convert are specified in a resource list. But this is not the only way in which converters can be invoked. It is possible to manually invoke type converters, the easiest way being XtConvertAndStore().[68] This may be useful in an application, such as for reading an icon pixmap from a file using a converter, or you may need to explicitly invoke a converter from within a converter you want to write. (Converter routines can themselves invoke other converters directly.)

One possible manual use of type converter routines is in the processing of the string parameters passed to action routines. Perhaps in the action routine itself it is more convenient to have some parameters converted to another form. For example, if an action is passed the string “True,” the action code might prefer to convert this parameter to a Boolean value. The CvtStringToBoolean converter understands many strings that would be interpreted as Boolean, such as “Off,” “On,” “True,” “False,” “No,” and “Yes,” in upper, lower, or mixed case. It saves code to use the converter rather than comparing a string to all these strings in your own code. Example 10-16 shows an action routine of the Athena Text widget (modified for R4) in which a converter is manually invoked.

Example 10-16. Manually invoking a type converter

static void
DisplayCaret(w, event, params, num_params)
Widget w;
XEvent *event;             /* CrossingNotify special-cased */
String *params;            /* Off, False, No, On, True, Yes, etc. */
Cardinal *num_params;      /* 0, 1 or 2 */
{
      .
      .
      .
    if (*num_params > 0) { /* default arg is "True" */
        XrmValue from, to;
        from.size = strlen(from.addr = params[0]);
        if (XtConvertAndStore(w, XtRString, &from, XtRBoolean, &to)
                == False)
            XtAppError(XtWidgetToApplicationContext(w),
                    "DisplayCaret action: String to Boolean\
                    conversion failed");
        else {
            ; /*
               *      (*(Boolean*)to.addr) has boolean value;
               *              do something with it here */
               */
        }
    }
}


Note that the from and to arguments of XtConvertAndStore() are pointers to structures containing length/pointer pairs--they are not values. The actual data is passed as a pointer to a pointer. Thus the cast to Boolean (in the comment) must dereference the pointer twice.

The widget argument of XtConvertAndStore() is used internally by Xt as the argument for the XtDisplay() macro, to get the pointer to the display structure, and for other purposes. In normal applications you can pass any widget here. (For some converters, the widget may need to be realized.)

XtConvertAndStore() calls a lower-level routine called XtCallConverter().[69]If you prefer, you can use this routine. Instead of passing it the widget and the source and destination type, you pass it a display, the name of the conversion routine, any arguments to the routine, and storage in which to place the cache reference count. See Volume Five, X Toolkit Intrinsics Reference Manual, for details.

Writing a Type Converter

If your application or widget has a data type that you would like the user to be able to set through resource files, and no Xt or Xmu converter exists, you will need to write (and register) a type converter from XtRString to your type. The first step in creating a converter is to decide upon the characteristics of the string you will be converting from, and the C-Language type you will be converting to. Then you can copy an existing similar converter and fill in the code to convert to your desired type. Note that the R5 Athena widgets still use a mixture of the old-style (R3) and new-style converter interfaces. To use the current converter functions XtConvertAndStore() or XtCallConverter(), or any of the cache reference counting features, you must define your converter using the type XtTypeConverter. The difference between new-style and old-style type converters is just that new-style type converters have a display as their first argument, and they must do a display-specific conversion when a server query is involved. The new-style converters also must return TRUE or FALSE to indicate whether the conversion succeeded. Example 10-17 shows an Xmu converter that shows the essential elements of a converter. It has been modified to be compatible with the new-style resource conversion routines such as XtSetTypeConverter(). (The only difference is the presence of a leading Display argument and the return value.)

Example 10-17. A simple type converter

#define done(address, type) \
        { (*toVal).size = sizeof(type); \
          (*toVal).addr = (XtPointer) address; }
Boolean XmuCvtStringToLong (display, args, num_args, fromVal, toVal)
    Display display;
    XrmValuePtr args;
    Cardinal    *num_args;
    XrmValuePtr fromVal;
    XrmValuePtr toVal;
{
    static long l;
    if (*num_args != 0)
        XtWarningMsg("wrongParameters","cvtStringToLong",
                "XtToolkitError","String to Long\
                conversion needs no extra arguments",
                (String *) NULL, (Cardinal *)NULL);
    if (sscanf((char *)fromVal->addr, "%ld", &l) == 1) {
        done(&l, long);
        return(TRUE);
    } else {
        XtDisplayStringConversionWarning(display, (char *)
                fromVal->addr, XtRLong);
        return(FALSE);
    }
}

Note that Xt manages the cache of converted values: converter routines are not responsible for caching their own returned data. Notice that the XrmValuePtr arguments passed into the converter are pointers to structures, not values. The XrmValue structure contains an address field and a size field.

XtDisplayStringConversionWarning() takes as arguments a pointer to the Display structure, the string that could not be converted, and the type to which it could not be converted. It issues a warning message with name conversionError, type string, class XtToolkitError, and the default message string “Cannot convert "src" to type dst_type.” (See Chapter 14, Miscellaneous Toolkit Programming Techniques, for more information on error and warning messages.)

Many converters need to compare multiple strings in order to convert a value. For example, Motif's string to arrow-direction converter has to determine whether a string matches XmARROW_UP, XmARROW_DOWN, XmARROW_LEFT, or XmARROW_RIGHT. Normally this would require up to four time-consuming string comparisons for each conversion. Instead, converters of this type use a shortcut based on quarks. A quark is a unique integer ID for a string, of type XrmQuark (defined by Xlib). A call to the Xlib routine XrmStringToQuark returns the quark for a string. A converter determines quarks for each of its reference strings (such as XmARROW_UP) only once during run time, and determines a quark for each string to be converted. Then quarks can be compared quickly since they are just integers. (See Volume Two, Xlib Reference Manual, for details on quarks.)

When a nonstandard type converter that uses quarks is defined and registered in widget code, the XrmStringToQuark calls are normally placed in the class_initialize method just before the XtSetTypeConverter() call.

Defining the Default Value

When performing conversions, such as from strings to fonts or colors, for which there is no string representation that all server implementations will necessarily recognize, a type converter should define some set of conversion values that the converter is guaranteed to succeed on, so that these can be used as resource defaults.

For example, Xt's default string-to-pixel converter recognizes the symbols XtDefaultForeground and XtDefaultBackground. As part of its conversion, it tests for these values, and establishes the appropriate value based on the string value. The code is shown in Example 10-18.

Example 10-18. Testing for a special-case default value

    /*
      * CompareISOLatin1 is an undocumented Xt function, allowed
      * since this converter is within Xt.  In your converters,
      * you would use XmuCompareISOLatin1.
      */
     if (CompareISOLatin1(str, XtDefaultBackground) == 0) {
         *destructor_data = False;
         if (pd->rv)
             done(Pixel, BlackPixelOfScreen(screen))
         else
             done(Pixel, WhitePixelOfScreen(screen));
     }
     if (CompareISOLatin1(str, XtDefaultForeground) == 0) {
         *destructor_data = False;
         if (pd->rv)
             done(Pixel, WhitePixelOfScreen(screen))
         else
             done(Pixel, BlackPixelOfScreen(screen));
     }

Although Motif uses Xt's string to pixel value converter when colors are specified in resource files, it uses its own algorithm to calculate the default value depending on the type of screen and on the colors that can be successfully allocated.

Subparts and Subresources

A subpart is a section of a widget that is replaceable but that cannot operate independently. It is just a further subdivision of the widget into smaller pieces. Gadgets and objects usually provide a more elegant way to do this.

Subresources allow subparts of a widget to have separate sets of resources. Since the R3 Athena Text widget was the only example of the use of subresources in MIT's core distribution, we'll describe how Text used subparts and subresources so you can understand the motivation behind them. You can then compare this concept with the current Athena Text widget which is implemented using objects. Motif 1.1 and 1.2 also use subresources to fetch data that isn't part of the primary widget structure. This happens for resource data that is shared among widgets (gadget caching) or that can't reside in the primary widget due to binary compatibility (e.g., VendorShell resources).

The R3 Text widget has three parts: the source, the sink, and the coordinator widget. The source manages the storage of data and the sink manages how it is displayed. The coordinator is the central widget that manages the communication between the source and the sink, and is inoperable without them. Both the source and the sink are replaceable pieces of code. Xaw provides only one source, which edits a string or disk file, and only one sink, which displays text in one color and in one font. The idea of providing the subparts in the first place is that they would allow enhancements to be made without changing the basic editor functionality that is in the coordinator. For example, only the source and sink would need replacing in order to implement a multifont and/or multicolor text widget.

Each subpart has its own resource list so that it truly can be replaced without any modifications to the central widget. These are the subresources.

The Hook Methods

The initialize_hook, set_values_hook, and get_values_hook methods are used by widgets that have subparts. They have the same function as their nonhook counterparts, except that they process only the resources of a subpart, and any subpart instance fields that depend on the subpart resources. These methods are called immediately after their nonhook counterparts.

However, the initialize_hook and set_value_hook methods have become obsolete in R4 because their arguments have been added to the initialize and set_values methods. The hook methods are still called for compatibility with existing widgets, but new widgets should move the code that would have been in the hook methods into initialize and set_values.

The get_values_hook method is passed a single copy of the widget instance structure (the new copy already modified in the nonhook methods), and the argument list passed to the Xt routine that triggered the method. The set_values and get_values_hook methods simply take this widget ID and argument list and pass them to XtSetSubvalues() or XtGetSubvalues() respectively. The initialize method uses the contents of the argument list to validate resource settings for subparts and to set nonresource subpart data.

The get_values_hook method is still used in R4. Example 10-19 shows the get_values_hook for the AsciiSrc subpart of the R3 Text widget (somewhat simplified to show the essential elements).

Example 10-19. Simplified get_values_hook method of the AsciiSrc subpart of the Text widget

static void
GetValuesHook(src, args, num_args)
XawTextSource src;
ArgList args;
Cardinal * num_args;
{
        .
        .
        .
     XtGetSubvalues((XtPointer) src,
             sourceResources,
             XtNumber(sourceResources),
             args,
             *num_args);
}


Managing Subresources

Managing subresources is very similar to managing application resources. Like the application, the subpart must have a structure containing the fields to set through resources. In the application you call XtGetApplicationResources() or XtVaGetApplicationResources() to set these fields. In a subpart, the analogous calls are XtGetSubresources() or XtVaGetSubresources(), which is called from the initialize method.

Like widgets, the resources of subparts can be queried and set manually. XtVaGetSubvalues() or XtGetSubvalues() queries the values, and XtVaSetSubvalues() or XtSetSubvalues() sets them. However, because subvalues are not part of any widget, these calls cannot identify what object is being queried or set simply by passing the widget ID. These calls have different arguments than XtSetValues() and XtGetValues(). Instead of the widget ID, you pass the pointer to the data structure, the resource list, and the number of resources. Therefore, XtSetSubvalues() and XtGetSubvalues() can be invoked only from within the widget or subpart. Actually, all these routine do is set or get the value in the specified structure.

Any application using the widget will set or get subresources using XtSetValues() and XtGetValues() as for normal resources, specifying only the coordinating widget as the first argument. These calls are translated into XtSetSubvalues() and XtGetSubvalues() calls by the set_values and get_values_hook methods. These methods are passed the arguments from the XtSetValues() or XtGetValues() calls and translate them into XtSetSubvalues() or XtGetSubvalues() calls by adding the data structure and resource list arguments. But in addition, the set_values method is responsible for validating the resource settings passed in before it calls XtSetSubvalues(), and for changing any nonresource subpart structure fields like GCs that depend on resources.



[62] Note the distinction between what we are calling the resource specification (the fully qualified name of the resource, up to the colon), and the value (the actual value to which the resource is to be set). We refer to both the specification and the value together as a resource setting.

[63] See the discussion of internationalization in Chapters 10 and 11 of Volume One, Xlib Programming Manual, Third Edition.

[64] The actual algorithm used by Xt differs slightly from that described here, because there are shortcuts that the resource manager takes that are hard to follow even if you have the source code. However, the algorithm described here gives the same result, with more clarity.

[65] This function type is relevant primarily because the reference page in Volume Five, X Toolkit Intrinsics Reference Manual, that describes its calling sequence is listed under this name, XtResourceDefaultProc. This reference page is in the Prototype Procedures section in Volume Five.

[66] While it may seem a little backwards to describe how to add a converter before we say how to write one, the availability of the Xmu converters makes it likely that you would in fact want to add converters you haven't written.

[67] You can also use XtOffset() instead of XtOffsetOf(), but it is less portable. The usage difference between the two is that XtOffset() takes a pointer to the structure type, while XtOffsetOf() takes the structure type itself.

[68] XtConvertAndStore() replaced XtConvert() in R4. Both have the same arguments, but XtConvertAndStore() returns Boolean while XtConvert() returns void. XtConvertAndStore()'s return value indicates whether the conversion succeeded or failed. XtConvertAndStore() also implements new display-specific conversion, caching and reference counting features.

[69] XtCallConverter() superceded its rough equivalent R3 routine XtDirectConvert().