Contents Previous Next

20 Compound Strings


This chapter describes Motif's technology for encoding font changes and character directions in the strings that are used by almost all of the Motif widgets.

Compound strings are designed to address two issues frequently encountered by application designers: the use of foreign character sets to display text in other languages and the use of multiple fonts to render text. With the addition of internationalized string rendering capabilities in X11R5, the use of compound strings for internationalization purposes is theoretically no longer necessary. However, the Motif widget set still uses compound strings extensively, so applications have no choice but to create them to display text.

20.1 Internationalized Text Output

The internationalization features in X11R5 are based on the ANSI-C locale model. Under this model, an application uses a library that reads a customization database at run-time to get information about the user's language environment. An Xt-based application can establish its language environment (or locale) by registering a language procedure with XtSetLanguageProc(), as described in Section #slangproc. The language procedure returns a language string that is used by XtResolvePathname() to find locale-specific resource files. See Volume Four, X Toolkit Intrinsics Programming Manual, for more information on the localization of the resource database.

One of the important characteristics of a language environment is the encoding that is used to represent the character set for the particular language. In X, character set simply refers to a set of characters, while an encoding is a numeric representation of these characters. Both of these terms are different from the definition of a font, which is a collection of glyphs used to represent the characters in an encoding. A charset (not the same as a character set) is an encoding in which all of the characters have the same number of bits. Charsets are often defined by standards bodies such as the International Standards Organization (ISO). For example, the ISO Latin-1 charset (ISO8859-1) defines an encoding for the characters used in all Western languages. The first half of Latin-1 is standard ASCII, while the second half (with the eighth bit set) contains accented characters needed for Western languages other than English. Character 65 in ISO Latin-1 is an uppercase "A", while 246 is a lowercase "o" with an umlaut (<OVERSTRIKE<o[[umlaut]]>OVERSTRIKE>).

However, not all languages can be represented by a single charset. Japanese text commonly contains words written using the Latin alphabet, as well as phonetic characters from the katakana and hirigana alphabets, and ideographic kanji characters. Each of these character sets has its own charset; the phonetic and Latin charsets are 8-bits wide, while the ideographic charset is 16-bits wide. The charsets must be combined into a single encoding for Japanese text, so the encoding uses shift sequences to specify the character set for each character in a string.

Strings in an encoding that contains shift sequences and characters with non-uniform width can be stored in a standard NULL-terminated array of characters; this representation is known as a multibyte string. Strings can also be stored using a wide-character type in which each character has a fixed size and occupies one array element in the string. The text output routines in X11R5 support both multibyte and wide-character strings. To support languages that use multiple charsets, X developed the XFontSet abstraction for its text output routines. An XFontSet contains all of the fonts that are needed to display text in the current locale. The new text output routines work with font sets, so they can render text for languages that require multiple charsets. See Volume One, Xlib Programming Manual, for more information on internationalized text output.

With the addition of these features in X, a developer can write an internationalized application without using the internationalization features provided by compound strings. In an internationalized application, strings are interpreted using the encoding for the current locale. To support a number of locales, the application needs to store string data in separate files from the application code. The application must provide a separate file for each of the locales supported, so that the program can read the appropriate file during localization.

However, since most Motif widgets use compound strings for representing textual data, a Motif application has to use compound strings to display text. As we describe compound strings in this chapter, we'll discuss how to use them so as not to interfere with the lower-level X internationalization features.

20.2 Creating Compound Strings

Almost all of the Motif widgets use compound strings to specify textual data. Labels, PushButtons, and Lists, among others, all require their text to be given in compound string format, whether or not you require the additional flexibility compound strings provide. The only widgets that don't use compound strings are the Text and TextField widgets. As a result, you cannot use the compound string techniques for displaying text using multiple fonts. However, in Motif 1.2, these widgets do support internationalized text output, so they can display text using multiple character sets. For information on the internationalization capabilities of the Text and TextField widgets, see Section #stexti18n.

A compound string (XmString) is made of three components: a tag, a direction, and text. The tag is an arbitrary name that the programmer can use to associate a compound string with a particular font or font set. In Motif 1.1, the tag was referred to as a character set. Since the tag doesn't necessarily specify a character set, Motif 1.2 now refers to the entity as a font list tag; this change is strictly semantic. The tag-to-font mapping is done on a per-widget basis, so the same name can map to different fonts for different widgets.

An application can create a compound string that uses multiple fonts by concatenating separate compound strings with different tags to produce a single compound string. Concatenating compound strings with different fonts is a powerful way to create graphically interesting labels and captions. More importantly, because fonts are loosely bound to compound strings via resources, you can dynamically associate new fonts with a widget while an application is running and effectively change text styles on the fly.

20.2.1 The Simple Case

Many applications only need to use compound strings to specify various textual resources. In this case, all that is needed is a routine that converts a standard C-style NULL-terminated text string into a compound string. The most basic form of conversion can be done using the XmStringCreateLocalized() function, as demonstrated in examples throughout this book. This routine takes the following form:

   XmString
   XmStringCreateLocalized(text)
       char *text;
The text parameter is a common C char string. The value returned is of type XmString, which is an opaque type to the programmer.

XmStringCreateLocalized() is a new routine in Motif 1.2; it creates a compound string in the current locale, which is specified by the tag XmFONTLIST_DEFAULT_TAG. This routine interprets the text string in the current locale when creating the compound string. If you are writing an internationalized application that needs to support multiple locales, you should use XmStringCreateLocalized() to create compound strings. The routine allows you to take advantage of the lower-level internationalization features of X.

Most applications specify compound string resources in resource files. This technique is appropriate for an internationalized application, as there can be a separate resource file for each language environment that is supported. Motif automatically converts all strings that are specified in resource files into compound strings using XmStringCreateLocalized(), so the strings are handled correctly for the current locale. If an application needs to create a compound string programmatically, it should use XmStringCreateLocalized() to ensure that the string is interpreted in the current locale. All of the examples in this book use XmStringCreateLocalized() to demonstrate the appropriate technique, even though the examples are only designed to work in the C locale.

With Motif 1.1, you should use the XmStringCreateSimple() routine to create a compound string that uses the default character set and direction. This function is obsolete in Motif 1.2; it remains for backwards-compatibility purposes only. With both XmStringCreateLocalized() and XmStringCreateSimple(), you cannot explicitly specify the tag or the string direction that is used for the compound string, and the string cannot have multiple lines.

Both XmStringCreateLocalized() and XmStringCreateSimple() allocate memory to store the compound string that is returned. Widgets that have compound string resources always allocate their own space and store copies of the compound string values you give them. When you are done using a compound string to set widget resources, you must free it using XmStringFree(). The following code fragment demonstrates this usage:

   XmString str = XmStringCreateLocalized ("Push Me");

   XtVaCreateManagedWidget ("widget_name",
       xmPushButtonGadgetClass, parent,
       XmNlabelString,  str,
       NULL);

   XmStringFree (str);
The process of creating a compound string, setting a widget resource, and then freeing the string is the most common use of compound strings. However, this process involves quite a bit of overhead, as memory operations are expensive. Memory is allocated by the string creation function and again by the internals of the widget for its own storage, and then your copy of the string must be deallocated.

The programmatic interface to the string creation process can be simplified by using the XtVaTypedArg feature in Xt. This special resource can be used in variable argument list specifications for functions such as XtVaCreateManagedWidget() and XtVaSetValues(). It allows you to specify a resource using a convenient type and have Xt do the conversion for you. In the case of compound strings, we can use this method to convert a C string to a compound string. The following code fragment has the same effect as the previous example:

   XtVaCreateManagedWidget ("widget_name",
       xmPushButtonWidgetClass, parent,
       XtVaTypedArg, XmNlabelString, XmRString,
           "Push Me", 8, /* or strlen ("Push Me") + 1 */
       NULL);
XtVaTypedArg takes four additional parameters: the name of the resource, the type of the value specified for the resource, the value itself, and the size of the value. We set the XmN­labelString resource. We want to avoid converting the character string to a compound string, so we specify a char * value and XmRString as its type. This terminology may be confusing to a new Motif programmer. Xt uses the typedef String for char *. The representation type used by Xt resource converters for this type is XtRString (XmRString in Motif). A compound string, on the other hand, is of type XmString; its representation type is XmRXmString. You just have to read the symbols carefully. Resource converters are described in detail in Volume Four, X Toolkit Intrinsics Programming Manual, Motif Edition . The string "Push Me" is the string value; the length of the string, including the NULL-terminating byte, is 8.

The XtVaTypedArg method for specifying a compound string resource is only a programmatic convenience; it does not save time or improve performance. The three-step process of creating, setting, and freeing the compound string still takes place, but it happens within Motif's compound string resource converter. Using automatic conversion is actually slower than converting a string using XmStringCreateLocalized(). However, unless you are creating hundreds of strings, the difference is negligible. The convenience and elegance of the XtVaTypedArg method may be worth the performance tradeoff.

The reason most of the examples in this book do not make use of the feature is that we are trying to demonstrate good programming techniques tuned to a large-scale, production-size, and quality application. Using the XtVaTypedArg method for compound strings is painfully slow when repeated over hundreds of Labels, PushButtons, Lists, and other widgets. The XtVaTypedArg method is perfectly reasonable for converting other types of resources, however. If you are converting a lot of values from one type to another, it is in your own best interest to evaluate the conversion process yourself by testing the automatic versus the manual conversion methods.

20.2.2 Font List Tags

Motif provides two different compound string creation routines that allow you to specify a tag used to associate the compound string with a font or a font set. This tag is a programmer-specified identifier that enables a Motif widget to pick its font from a list of fonts at run-time. In Motif 1.1, the font list tag was referred to as a character set, but strictly speaking, it does not specify a character set.

The XmStringCreate() and XmStringCreateLtoR() routines allow you to specify a font list tag. These routines take the following form:

   XmString
   XmStringCreate(text, tag)
       char *text;
       char *tag;
   XmString
   XmStringCreateLtoR(text, tag)
       char *text;
       char *tag;
Both of these routines create and allocate a new compound string and associate the tag parameter with that string. As with any compound string, be sure to free it with XmStringFree() when you are done using it.

XmStringCreate() creates a compound string that has no specified direction. The default direction of a string may be taken from the XmNstringDirection resource. This resource is defined by manager widgets; it specifies the string direction for all the children of the manager. If the default direction is not adequate, XmStringDirectionCreate() can be used to create a compound string with an explicit direction, as we'll discuss shortly.

XmStringCreateLtoR() creates a compound string in which the direction is hard-coded as left-to-right. Motif also defines the XmStringLtoRCreate() routine; its functionality is identical to XmStringCreateLtoR(). This function is also useful for converting newline-separated strings into compound strings, as we explain later in this section. Unfortunately, Motif does not provide a corresponding right-to-left compound string creation function. If you need such a routine, it is not that difficult to write one.

The actual font or font set that is associated with the compound string is dependent on the widget that renders the string. Every Motif widget that displays text has an XmNfontList resource. This resource specifies a list of fonts and/or font sets for the widget; each entry in the list may have an optional tag associated with it. For example, a resource file might specify a font list as follows:

   *fontList: -*-courier-*-r-*--*-120-*=TAG1,           -*-courier-*-r-*--*-140-*=TAG2,           -*-courier-*-r-*--*-180-*=TAG3
At run-time, the compound string is rendered using the first font or font set in the widget's font list that matches the font list tag specified in the compound string creation function. In Motif 1.2, the compound string rendering functions use the new X11R5 text output functions, so compound strings are displayed appropriately for the current locale. If Motif cannot find a match, the compound string is rendered using the first item in the widget's font list, regardless of its tag. This loose binding between the compound string and the font or font set used to render it is useful in a number of ways:

the source code demonstrates how a compound string can be rendered using different fonts in different PushButton widgets. XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4. XmStringCreateLocalized() is only available in Motif 1.2; XmStringCreateSimple() is the corresponding function in Motif 1.1.

   /* string.c  -- create a compound string with the "MY_TAG" tag.
    * The tag defaults to the "9x15" font.  Create three pushbuttons:
    * pb1, pb2, and pb3.  The user can specify resources so that each of the
    * widgets has a different font associated with the "MY_TAG" tag
    * specified in the compound string.
    */
   #include <Xm/RowColumn.h>
   #include <Xm/PushBG.h>

   String fallbacks[] = { "*fontList:9x15=MY_TAG", NULL };

   main(argc, argv)
   int argc;
   char *argv[];
   {
       Widget        toplevel, rowcol;
       XtAppContext  app;
       XmString      text;
       Display      *dpy;

       XtSetLanguageProc (NULL, NULL, NULL);

       toplevel = XtVaAppInitialize (&app, "String", NULL, 0,
           &argc, argv, fallbacks, NULL);

       text = XmStringCreate ("Testing, testing...", "MY_TAG");

       rowcol = XtVaCreateWidget ("rowcol",
           xmRowColumnWidgetClass, toplevel,
           NULL);

       XtVaCreateManagedWidget ("pb1",
           xmPushButtonGadgetClass, rowcol,
           XmNlabelString, text,
           NULL);

       XtVaCreateManagedWidget ("pb2",
           xmPushButtonGadgetClass, rowcol,
           XmNlabelString, text,
           NULL);

       XtVaCreateManagedWidget ("pb3",
           xmPushButtonGadgetClass, rowcol,
           XmNlabelString, text,
           NULL);

       XmStringFree (text);
       XtManageChild (rowcol);
       XtRealizeWidget (toplevel);
       XtAppMainLoop (app);
   }
This simple program creates three PushButton gadgets that all use the same compound string for their labels. The font list tag MY_TAG is associated with the 9x15 font in the fallback resources. By default, all of the buttons look the same, as shown in the figure.

figs.eps/V6a.19.01.eps.png
Output of string.c

However, the figure shows what happens to the output when the following resources are specified:

   *pb1.fontList: -*-courier-*-r-*--*-120-*=MY_TAG
   *pb2.fontList: -*-courier-*-r-*--*-140-*=MY_TAG
   *pb3.fontList: -*-courier-*-r-*--*-180-*=MY_TAG

figs.eps/V6a.19.02.eps.png
Output of string.c with font list resources set

The font associated with MY_TAG for each of the PushButtons is different, so the compound string for each one is rendered in a different font. This case isn't really that exciting, however, because we could have achieved the same effect without specifying a font list tag for each font. Since each font list only contains one font, Motif has no choice but to display the compound string using that font. The following resource specification creates the output shown in the figure:

   *pb1.fontList: -*-courier-*-r-*--*-120-*
   *pb2.fontList: fixed,-*-courier-*-r-*--*-140-*=ANOTHER_TAG
   *pb3.fontList: fixed,-*-courier-*-r-*--*-180-*=MY_TAG

figs.eps/V6a.19.03.eps.png
Output of string.c with multiple font list resources set

In this case, the compound string in the first PushButton uses a 12-point Courier font, since that is the only font in the font list. The second PushButton uses the fixed font because it is first in the list and neither of the fonts has MY_TAG associated with it. The third button uses the 18-point Courier font associated with MY_TAG. In Motif 1.2, the constant XmFONTLIST_DEFAULT_TAG is used to tag compound strings that are created in the encoding of the current locale. When a compound string is created using XmStringCreateLocalized(), this tag is used. The equivalent compound string can also be created using XmStringCreate() with the tag explicitly set to XmFONTLIST_DEFAULT_TAG. Just as with other font list tags, Motif looks for a font or font set with a matching tag when it renders the compound string. This font list tag is used to identify the font or font set that is correct for the encoding of the current locale. If a font list does not use XmFONTLIST_DEFAULT_TAG, the first item in the font list is automatically associated with this tag.

An internationalized application should only use XmFONTLIST_DEFAULT_TAG in font lists to ensure that compound strings are rendered correctly for the current locale. However, it is possible to use explicit font list tags for locale-specific text. Explicit tags are necessary when an application wants to display compound strings using different point sizes or type styles. In this case, the compound string and the font list associated with it need to use the same tag, and the tag should be mapped to XmFONTLIST_DEFAULT_TAG using XmRegisterSegmentEncoding().

In Motif 1.1, the first font in widget's font list is the default character set for that widget. If the widget does not have a font list, it uses a default character set referred to by the constant XmSTRING_DEFAULT_CHARSET. If the user has set the LANG environment variable, its value is used for this character set. If this value is invalid or its associated font cannot be used, Motif uses the value of XmFALLBACK_CHARSET, which is vendor-defined but typically set to "ISO8859-1".

For backwards compatibility, Motif 1.2 essentially equates XmFONTLIST_DEFAULT_TAG with XmSTRING_DEFAULT_CHARSET when it cannot find an exact match between a compound string and a font list. XmFONTLIST_DEFAULT_TAG in a compound string or font list matches the tag used in creating a compound string or specifying a font list entry with the tag XmSTRING_DEFAULT_CHARSET. Some Motif widgets define font list resources that allow them to provide a consistent appearance for all of their children. In Motif 1.2, the VendorShell widget defines the XmNbuttonFontList, XmNlabelFontList, and XmNtextFontList resources, while the MenuShell defines XmNbuttonFontList and XmNlabelFontList. These resources apply to all of the buttons, Labels, and Text widgets that are descendents of the widget. In Motif 1.1, the VendorShell and MenuShell only defined the XmNdefaultFontList resource; this resource applied to all of the children of the widget. For backwards compatibility, if one of the more specific font list resources is not set, its value is taken from XmNdefaultFontList.

The BulletinBoard widget defines the XmNbuttonFontList, XmNlabelFontList, and XmNtextFontList resources primarily for use in dialog boxes. These font lists apply to the buttons, Labels, and Text widgets that descend from a BulletinBoard. For more information on the use of the resources in dialog boxes, see Chapter 5, Introduction to Dialogs.

All of these font list resources are designed to help you maintain a consistent interface. However, you can always specify the font for a particular button, Label, or Text widget using the widget's XmNfontList resource, as this resource overrides the more general ones.

20.2.3 Compound String Segments

A compound string is composed of segments, where each segment contains a continuous sequence of text with no change in font list tag or direction. A compound string segment can be terminated by a separator, which is the equivalent of a newline in a character string. Separators in compound strings should not be confused with the Separator widget and gadget class. Segments can be concatenated with other segments or compound strings to create longer strings; each segment can specify a different tag and direction to make a string that uses mutiple fonts and directions.

XmStringSegmentCreate() provides complete control over the creation of a compound string, as it allows you to specify the text, a font list tag, and a direction. This routine also lets you specify whether or not a separator is added to the compound string. The routine takes the following form:

   XmString
   XmStringSegmentCreate(text, tag, direction, separator)
       char              *text;
       char              *tag;
       XmStringDirection  direction;
       Boolean            separator;
Compound strings are rendered either from left-to-right or from right-to-left. If you are going to use left-to-right strings uniformly in your applications, you really don't need to read this section. There are several ways to build a compound string that is rendered from right-to-left; the best method is dependent on the nature of your application.

If your application uses right-to-left strings for all of its widgets, you may want to use the Manager XmNstringDirection resource. This resource specifies the direction for compound strings used by widgets that are immediate children of a Manager widget, provided that the string direction is not hard-coded in the compound strings. If you use this resource, you can continue to use XmStringCreate() or XmStringCreateLocalized() to create compound strings.

Most right-to-left languages display certain things, like numbers, from left-to-right, so it is not always possible to use the XmNstringDirection resource. In this case, you have to create compound string segments that hard-code their directional information. You can create individual string segments with a specific by direction using either XmStringDirectionCreate() or XmStringSegmentCreate(). Both of these routines take an argument of type XmStringDirection, which is defined as an unsigned char. You can specify either XmSTRING_DIRECTION_R_TO_L or XmSTRING_DIRECTION_ L_TO_R for values of this type.

When using XmStringSegmentCreate(), you specify the string direction using the direction parameter. For example, we can change the call to XmStringCreate() in the source code to the following:

   text = XmStringSegmentCreate ("Testing, testing...", "MY_TAG",
       XmSTRING_DIRECTION_R_TO_L, False);
Obviously, you would normally do this only if you were using a font that was meant to be read from right-to-left, such as Hebrew or Arabic. The output that results from this change is shown in the figure.

figs.eps/V6a.19.04.eps.png
Output of string.c using a right-to-left string direction


You can also use the function XmStringDirectionCreate() to create a compound string segment that contains only directional information. This routine takes the following form:

   XmString
   XmStringDirectionCreate(direction)
       XmStringDirection direction;
The routine returns a compound string segment that can be concatenated with another compound string to cause a directional change. Separators are used to break compound strings into multiple lines, in much the same way that a newline character does in a character string. To demonstrate separators, we can change the string creation line in the source code to the following:
   text = XmStringCreateLtoR ("Testing,0esting...", "MY_TAG");
In this case, we use XmStringCreateLtoR() not because we need to specify the left-to-right direction explicitly, but because this function interprets embedded newline characters (\n) as separators. The effect of this change is shown in the figure, where the PushButtons display multiple lines of text.

figs.eps/V6a.19.05.eps.png
Output of string.c using separators to render multiple lines

XmStringCreate() and XmStringSegmentCreate() do not interpret newline characters as separators; they create a single compound string segment in which the '\n' is treated just like any other character value in the associated font or font set, as shown in the figure. XmStringSegmentCreate(), however, can be told to append a separator to the compound string it creates.

figs.eps/V6a.19.06.eps.png
Output of string.c with \n not interpreted as a separator

Most applications need newline characters to be interpreted as separators. For example, if you are using fgets() or read() to read the contents of a file, and newlines are read into the buffer, you should use XmStringCreateLtoR() to convert the buffer into a compound string that contains separators. the source code shows a function that reads the contents of a file into a buffer and then converts that buffer into a compound string. XmFONTLIST_DEFAULT_TAG replaces XmSTRING_DEFAULT_CHARSET in Motif 1.2.

   XmString
   ConvertFileToXmString(filename, &lines)
   char *filename;
   int *lines;
   {
       struct stat  statb;
       int          fd, len, lines;
       char        *text;
       XmString     str;

       *lines = 0;
       if (!(fd = open (filename, O_RDONLY))) {
           XtWarning ("Internal error -- can't open file");
           return NULL;
       }
       if (fstat (fd, &statb) == -1 ||
               !(text = XtMalloc ((len = statb.st_size) + 1))) {
           XtWarning ("Internal error -- can't show text");
           close (fd);
           return NULL;
       }
       (void) read (fd, text, len);
       text[len] = 0;

       str = XmStringCreateLtoR (text, XmFONTLIST_DEFAULT_TAG);

       XtFree (text);
       close (fd);

       *lines = XmStringLineCount (str);
       return str;
   }

Since separators are considered to be line breaks, we can count the number of lines in the compound string using the function XmStringLineCount(). However, this does not imply that separators terminate compound strings or cause font changes. As we have shown, a separator can be inserted into the middle of a compound string without terminating it. The fact that separate segments are created has little significance unless you need to convert compound strings back into C strings, which we discuss in Section #sstringcvt.

20.2.4 Multiple-font Strings

Once multiple font list tags are specified in a font list, you can use the list to display more than one font or font set in a single compound string. You can create a multi-font string in one of two ways: create the compound text in segments or create separate compound strings. Either way, once the segments or strings have been created, they must be concatenated together to form a new compound string that has font-change information embedded in it. the source code demonstrates the creation of a compound string that uses three fonts. XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4.

   /* multi_font.c -- create three compound strings using 12, 14 and 18
    * point fonts.  The user can specify resources so that each of the strings
    * use different fonts by setting resources similar to that shown
    * by the fallback resources.
    */
   #include <Xm/Label.h>

   String fallbacks[] = {
       "multi_font*fontList:-*-courier-*-r-*--12-*=TAG1,-*-courier-bold-o-*--14-*=TAG2,-*-courier-medium-r-*--18-*=TAG3",
       NULL
   };

   main(argc, argv)
   int argc;
   char *argv[];
   {
       Widget        toplevel;
       XtAppContext  app;
       XmString      s1, s2, s3, text, tmp;
       String        string1 = "This is a string ",
                     string2 = "that contains three ",
                     string3 = "separate fonts.";

       XtSetLanguageProc (NULL, NULL, NULL);

       toplevel = XtVaAppInitialize (&app, "String", NULL, 0,
           &argc, argv, fallbacks, NULL);

       s1 = XmStringCreate (string1, "TAG1");
       s2 = XmStringCreate (string2, "TAG2");
       s3 = XmStringCreate (string3, "TAG3");

       /* concatenate the 3 strings on top of each other, but we can only
        * do two at a time.  So do s1 and s2 onto tmp and then do s3.
        */
       tmp = XmStringConcat (s1, s2);
       text = XmStringConcat (tmp, s3);

       XtVaCreateManagedWidget ("widget_name",
           xmLabelWidgetClass, toplevel,
           XmNlabelString,     text,
           NULL);

       XmStringFree (s1);
       XmStringFree (s2);
       XmStringFree (s3);
       XmStringFree (tmp);
       XmStringFree (text);

       XtRealizeWidget (toplevel);
       XtAppMainLoop (app);
   }
The output of this program is shown in the figure.

figs.eps/V6a.19.07.eps.png
Output of multi_font.c

The XmNfontList resource is specified using three fonts, each with a distinct font list tag. We create each string using XmStringCreate() with the appropriate text and tag. Then we concatenate the strings using XmStringConcat(), two at a time until we have a single compound string that contains all the text. XmStringConcat() does not work like strcat() in C. The Motif function creates a new compound string that is composed of the two existing strings, rather than appending one string to the other string. Details of this function and other related functions are discussed in Section #sstringfunc.

It is possible to specify compound string resource values, such as the XmNlabelString resource of the Label widget, in a resource file as normal C strings. Motif provides a resource converter that converts the character string into a compound string. However, this resource converter does not allow you to specify font list tags in the character string. If you need font changes within a compound string, you need to create the compound strings explicitly in your application as we have done in the source code

20.3 Manipulating Compound Strings

Most C programmers are used to dealing with functions such as strcpy(), strcmp(), and strcat() to copy, compare, and modify strings. However, these functions do not work with compound strings, as they are not based on a byte-per-character format, and they may have NULL characters as well as other types of information embedded in them. In order to perform these common tasks, you can either convert the compound string into a character string, or you can use the compound string manipulation functions provided by Motif. The method you choose depends largely on the complexity of the compound strings you have and/or the complexity of the manipulation you need to do.

20.3.1 Compound String Functions

Motif provides a number of functions that allow you to treat compound strings in much the same way that you treat C-style character arrays. The toolkit provides the following routines:

   XmStringByteCompare()
   XmStringCompare()
   XmStringConcat()
   XmStringCopy()
   XmStringEmpty()
   XmStringHasSubstring()
   XmStringLength()
   XmStringNConcat()
   XmStringNCopy()
Both XmStringCompare() and XmStringByteCompare() compare two compound strings. These routines take the following form:
   Boolean
   XmStringCompare(string1, string2)
       XmString string1, string2;
   Boolean
   XmStringByteCompare(string1, string2)
       XmString string1, string2;
XmStringCompare() simply checks if the strings have the same text components, directions, and separators; it returns True if they do. This routine is simpler and more frequently used than XmStringByteCompare(), which performs a byte-by-byte comparison of the two compound strings. If each string uses the same font list tags, has the same direction, and contains the same embedded char string internally, the function returns True. The mapping between font list tags and fonts does not happen until a compound string is rendered by a widget, so whether or not the same font list tag actually maps to two different fonts does not affect the results of this function.

XmStringConcat() and XmStringNConcat() can be used to concatenate compound strings. These functions take the following form:

   XmString
   XmStringConcat(string1, string2)
       XmString string1, string2;
   XmString
   XmStringNConcat(string1, string2, n)
       XmString string1, string2;
       int n;
Both of these routines create a new compound string and copy the concatenation of string1 and string2 into the newly allocated string. XmStringNConcat() copies all of string1, but only n bytes from string2, into the new string. The original strings are preserved, and you are responsible for freeing the string returned by the routines using XmStringFree().

You can copy a compound string using either XmStringCopy() or XmStringNCopy(), which take the following forms:

   XmString
   XmStringCopy(string)
       XmString string;
   XmString
   XmStringNCopy(string, n)
       XmString string;
       int n;
Both functions copy string into a newly-allocated compound string; XmStringNCopy() only copies n bytes from string.

XmStringHasSubstring() determines whether or not a compound string contains a particular substring. The routine has the following form:

   Boolean
   XmStringHasSubstring(string, substring)
       XmString string, substring;
For this function, substring must be a single-segment compound string. If its text is completely contained within any single segment of string, the function returns True. The two strings must use the same font list tags for the routine to return True.

To get the length of a compound string, use XmStringLength(), which has the following form:

   int
   XmStringLength(string)
       XmString string;
This function returns the number of bytes in the compound string including all tags, direction indicators, and separators. If the structure of string is invalid, the routine returns zero. This function cannot be used to get the length of the text represented by the compound string; it is not the same as strlen() ).

You can determine whether or not a compound string contains any segments using XmStringEmpty(), which takes the following form:

   Boolean
   XmStringEmpty(string)
       XmString string;
This function returns True if there are no segments in the specified string and False, otherwise. If the routine is passed NULL, it returns True.

20.3.2 Compound String Retrieval

You can retrieve a compound string from a Motif widget using XtVaGetValues(). However, the way XtVaGetValues() is used for compound string resources is different than how it is used for most other resources. The value returned by XtVaGetValues() for a compound string resource is a copy of the internal data, so the compound string must be freed by the application, as shown in the following code fragment:

   XmString str;
   extern Widget pushbutton;
   char *text;

   XtVaGetValues (pushbutton, XmNlabelString, &str, NULL);
   ...
   /* do whatever you want with the compound string */
   ...
   XmStringFree (str);  /* must free compound strings from GetValues */
To avoid memory leaks in your application, you must remember to free any compound strings that you retrieve from a widget using XtVaGetValues().

20.3.3 Compound String Conversion

If the Motif routines described in the previous section are inadequate for your needs, you can convert compound strings back into C strings and manipulate them using the conventional C functions. This process can be simple or complicated depending on the complexity of the compound string to be converted. If the compound string only has one tag associated with it and has a left-to-right orientation, the process is quite simple. In this case, which is quite common, you can use the following function to make the conversion:

   Boolean
   XmStringGetLtoR(string, tag, text)
       XmString            string;
       XmStringCharSet     tag;
       char              **text;
XmStringGetLtoR() takes a compound string and a tag and converts it back into a C character string. If successful, the function returns True, and the text parameter points to a newly-allocated string. Since the routine allocates storage for the character string, you must free this pointer when you are done using it, as shown in the following code fragment:
   XmString  string;
   char     *text;

   if (XmStringGetLtoR (string, "MY_TAG", &text)) {
       printf ("Text = %s0, text);
       XtFree (text);
   }

As its name implies, XmStringGetLtoR() only gets left-to-right oriented text. Additionally, the function only gets the first text segment from the compound string that is associated with the specified tag. If the string contains multiple tags or has a right-to-left direction, you need to traverse the compound string and retrieve each segment individually in order to obtain the entire string. Motif defines a new type, XmStringContext, that is used to identify and maintain the position within the compound string being scanned. To cycle through a compound string, you need to use the following sequence of operations:

XmStringInitContext() initializes a string context that allows an application to read the contents of a compound string segment by segment. This routine takes the following form:

   Boolean
   XmStringInitContext(context, string)
       XmStringContext  *context;
       XmString          string;
The function allocates a new XmStringContext type and sets the pointer that is passed by the calling function in the context parameter to this data. If the allocation is successful and the compound string is valid, the function returns True.

Once the string context has been initialized, the contents of the string can be scanned using XmStringGetNextSegment():

   Boolean
   XmStringGetNextSegment(context, text, tag, direction, separator)
       XmStringContext      context;
       char               **text;
       XmStringCharSet     *tag;
       XmStringDirection   *direction;
       Boolean             *separator;
The routine does not take an XmString parameter because the context parameter is used to keep track of the compound string. The function reads the next segment; it stops when it encounters a new tag or a directional change. The values for text, tag, and direction are filled in, and if a separator is found at the end of the segment, separator is set to True. The text parameter points to allocated data that should be freed by the caller using XtFree().

When you are through scanning the compound string, you need to free the string context using XmStringFreeContext() , which takes the following form:

   void
   XmStringFreeContext(context)
       XmStringContext context;
the source code shows a routine that uses these functions to print a compound string used as the label for a widget.
   void
   PrintLabel(widget)
   Widget widget;
   {
       XmString           str;
       XmStringContext    context;
       char              *text, *tag, buf[128], *p;
       XmStringDirection  direction;
       Boolean            separator;

       XtVaGetValues (widget, XmNlabelString, &str, NULL);

       if (!XmStringInitContext (&context, str)) {
           /* compound strings from GetValues still need to be freed! */
           XmStringFree (str);
           XtWarning ("Can't convert compound string.");
           return;
       }

       /* p keeps a running pointer thru buf as text is read */
       p = buf;

       while (XmStringGetNextSegment (context, &text, &tag,
               &direction, &separator)) {
           /* copy text into p and advance to the end of the string */
           p += (strlen (strcpy (p, text)));
           if (separator == True) { /* if there's a separator ... */
               *p++ = '0;
               *p = 0;  /* add newline and null-terminate */
           }
           XtFree (text);   /* we're done with the text; free it */
       }

       XmStringFreeContext (context);
       XmStringFree (str);

       printf ("Compound string:0s0, buf);
   }

20.4 Working With Font Lists

As we have demonstrated, font lists can be set in a resource file. If your application is robust enough to handle any particular font that the user may specify, you are encouraged to use fallback resources and the application defaults files for all font list specifications. This technique simplifies maintenance for your application, as you do not have to open fonts, maintain handles to them, and free them. If you are writing an internationalized application, you should only specify font lists in resource files so that you can specify different fonts and/or font sets in the resource files for different locales.

However, if you specifically don't want the user to override your font specifications, you can hard-code fonts within the application using various Motif routines to create a font list. In this case, you are taking on the responsibility of creating, maintaining, and destroying fonts as necessary. Motif also provides routines that allow you to retrieve information about a font list.

20.4.1 Creating Font Lists

All of the font list creation functions deal with a font list object of type XmFontList. This type is intended to be opaque, so you should not attempt to access the internal fields of the data structure. If you need information about the fonts in a font list, you can use the routines for querying a font list that we are going to describe.

The Motif API for font lists has changed significantly in Motif 1.2 to support the new XFontSet abstraction. The Motif 1.1 routines exist for backwards compatibility, but they are now obsolete. In Motif 1.2, each item in a font list specifies an XmFontListEntry and an associated tag, while in Motif 1.1 each item specifies a font and a character set tag. The XmFontListEntry type is an opaque type that can specify either a font or a font set.

The process for creating a font list involves creating individual font list entries and then appending these entries to a font list. the source code shows a program that produces the same output as the source code but now the font list is hard-coded in the program. XtSetLanguageProc() is only available in X11R5; there is no corresponding function in X11R4. XmFontListEntryCreate() is only available in Motif 1.2; there is no corresponding function in Motif 1.1. XmFontListAppendEntry() is only available in Motif 1.2; XmFontListCreate() and XmFontListAdd() are the corresponding functions in Motif 1.1.

   /* fontlist.c -- demonstrate how to create, add to, and destroy
    * font lists.  The fonts and text displayed are hardcoded in
    * this program and cannot be overriden by user resources.
    */
   #include <Xm/Label.h>

   main(argc, argv)
   int argc;
   char *argv[];
   {
       Widget         toplevel;
       XtAppContext   app;
       XmString       s1, s2, s3, text, tmp;
       XmFontListEntry   entry1, entry2, entry3;
       XmFontList     fontlist;
       String         string1 = "This is a string ",
                      string2 = "that contains three ",
                      string3 = "separate fonts.";

       XtSetLanguageProc (NULL, NULL, NULL);

       toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0,
           &argc, argv, NULL, NULL);

       entry1 = XmFontListEntryLoad (XtDisplay (toplevel),
           "-*-courier-*-r-*--*-120-*", XmFONT_IS_FONT, "TAG1");
       entry2 = XmFontListEntryLoad (XtDisplay (toplevel),
           "-*-courier-bold-o-*--*-140-*", XmFONT_IS_FONT, "TAG2");
       entry3 = XmFontListEntryLoad (XtDisplay (toplevel),
           "-*-courier-medium-r-*--*-180-*", XmFONT_IS_FONT, "TAG3");
       fontlist = XmFontListAppendEntry (NULL, entry1);
       fontlist = XmFontListAppendEntry (fontlist, entry2);
       fontlist = XmFontListAppendEntry (fontlist, entry3);
       XmFontListEntryFree (&entry1);
       XmFontListEntryFree (&entry2);
       XmFontListEntryFree (&entry3);

       s1 = XmStringCreate (string1, "TAG1");
       s2 = XmStringCreate (string2, "TAG2");
       s3 = XmStringCreate (string3, "TAG3");

       /* concatenate the 3 strings on top of each other, but we can only
        * do two at a time.  So do s1 and s2 onto tmp and then do s3.
        */
       tmp = XmStringConcat (s1, s2);
       text = XmStringConcat (tmp, s3);

       XtVaCreateManagedWidget ("label", xmLabelWidgetClass, toplevel,
           XmNlabelString,     text,
           XmNfontList,        fontlist,
           NULL);

       XmStringFree (s1);
       XmStringFree (s2);
       XmStringFree (s3);
       XmStringFree (tmp);
       XmStringFree (text);
       XmFontListFree (fontlist);

       XtRealizeWidget (toplevel);
       XtAppMainLoop (app);
   }
This program creates font list entries for three fonts, appends the entries to a font list, and uses the resulting font list to specify the XmNfontList resource of a Label widget. The compound strings are created using the same tags as the font list entries, so they are displayed in the appropriate fonts.

In the source code we create the font list entries using XmFontListEntryLoad(), which takes the following form:

   XmFontListEntry
   XmFontListEntryLoad(display, font_name, type, tag)
       Display    *display;
       char       *font_name;
       XmFontType  type;
       char       *tag;
This routine loads the font or creates the font set specified by font_name. The function uses Xt resource converters to convert the string name of the font to the appropriate type. The type parameter specifies whether the font name is a font or a font set; it can have the value XmFONT_IS_FONT or XmFONT_IS_FONT_SET . The tag is associated with the font list entry. If the routine can load or create the specified font, it allocates and returns an XmFontListEntry, which the application must free using XmFontListEntryFree(). If the routine cannot find the specified font, it returns NULL.

Once we have created the font list entries, we can use them to make a font list. XmFontListAppendEntry() appends a font list entry to a font list. We call this routine three times to add the three entries. XmFontListAppendEntry() takes the following form:

   XmFontList
   XmFontListAppendEntry(oldlist, entry)
       XmFontList      oldlist;
       XmFontListEntry entry;
The routine adds the specified entry to the old font list and returns a new font list. If oldlist is NULL, the routine simply creates a font list using the font list entry. Motif caches font lists, so when we add a font list entry to a font list, the routine searches the cache for a font list that matches the new font list. If the routine finds a matching font list, it returns that font list and increments its reference count. Otherwise, XmFontListAppendEntry() allocates space for the new font list and caches it. The routine deallocates the storage for the old font list, but the application is responsible for freeing the storage for the new font list using XmFontListFree().

After we add the font list entries to the font list, we don't need the individual entries, so we free them using XmFontListEntryFree(). Notice that this routine takes an address of a font list entry, not the actual font list entry. When Motif creates a font list entry, it does not copy the XFontStruct or XFontSet, so these items must not be freed. If you pass a font list entry, instead of its address, to XmFontListEntryFree(), you end up freeing the font or font set, which results in an X protocol error.

The fontlist.c program creates compound strings just like our previous examples. The strings are associated with the same tags as the font list entries, so the strings are rendered using the appropriate fonts. The program sets the XmNfontList resource of the Label widget, so the fonts are hard-coded in the application and cannot be modified using a resource file. When a font list is assigned to a widget, the widget copies the list using XmFontListCopy(). After the resource has been specified, the program no longer needs the font list, so it frees it using XmFontListFree().

We used XmFontListEntryLoad() to both load the font and create a font list entry. Alternatively, we could have loaded the fonts using a routine like XLoadQueryFont() and then called XmFontListEntryCreate() to create the font list entries. This routine takes the following form:

   XmFontListEntry
   XmFontListEntryCreate(tag, type, font)
       char       *tag
       XmFontType  type;
       XtPointer   font;
The type parameter specifies whether the specified font is an XFontStruct or an XFontSet. You can load a font using XLoadQueryFont(). Use XCreateFontSet() to create a font set. (See Volume One, Xlib Programming Manual, for more information on these routines.) XmFontListEntryCreate() allocates and returns a font list entry; the application is responsible for freeing this entry using XmFontListEntryFree().

It is purely a matter of preference whether you use XmFontListEntryCreate() or XmFontListEntryLoad(). In the source code we could replace our calls to XmFontListEntryLoad() with the following code:

   XFontStruct         *font1, *font2, *font3;
   XmFontListEntry     entry1, entry2, entry3;

   font1 = XLoadQueryFont (XtDisplay (toplevel),
       "-*-courier-*-r-*--*-120-*");
   font2 = XLoadQueryFont (XtDisplay (toplevel),
       "-*-courier-bold-o-*--*-140-*");
   font3 = XLoadQueryFont (XtDisplay (toplevel),
       "-*-courier-medium-r-*--*-180-*");

   entry1 = XmFontListEntryCreate ("TAG1", XmFONT_IS_FONT, font1);
   entry2 = XmFontListEntryCreate ("TAG2", XmFONT_IS_FONT, font2);
   entry3 = XmFontListEntryCreate ("TAG3", XmFONT_IS_FONT, font3);
The functionality of the program is the same in either case, so which method you use really depends on whether you want to load the fonts yourself or let the routine handle it for you.

In Motif 1.1, there are two routines for dealing with font lists. XmFontListCreate() creates a new font list with one entry, while XmFontListAdd() adds a font to an existing font list. These routines take the following form:

   XmFontList
   XmFontListCreate(font, charset)
       XFontStruct      *font;
       XmStringCharSet   charset;
   XmFontList
   XmFontListAdd(oldlist, font, charset);
       XmFontList        oldlist;
       XFontStruct      *font;
       XmStringCharSet   charset;
The routines both take an XFontStruct, so you have to load the font yourself using XLoadQueryFont(). The functions both allocate and return a font list that the application must free when it is done using it. These routines exist for backwards compatibility purposes only, so you should not use them with Motif 1.2.

20.4.2 Retrieving Font Lists

You can retrieve a font list directly from a widget using XtVaGetValues() no matter whether the font list is specified in a resource file or created programatically. The following code fragment demonstrates this technique:

   XmFontList fontlist;

   XtVaGetValues (widget, XmNfontList, &fontlist, NULL);
The font list returned by XtVaGetValues() is a pointer to internal data, so it should be considered read-only. You should not alter this font list or free it. This is in direct contrast to how Motif uses XtVaGetValues() for compound strings, where a copy of the string is returned. If you need to manipulate the font list, you can make a copy of it using XmFontListCopy().

Once you have obtained a font list from a widget, you can use it to specify the font list for another widget, as shown in the following code:

   XtVaSetValues (another_widget, XmNfontList, fontlist, NULL);
Since the font list was obtained through a call to XtVaGetValues() , we do not free it after setting the XmNfontList resource.

20.4.3 Querying Font Lists

The XmFontList type is opaque to the programmer, so if you need to get information about a font list, you have to use Motif-specific functions that access font list information. This internal information can be useful if you need the font handles or tags for any reason. Motif provides a number of routines to cycle through the font list. The XmFontContext type is used to identify and maintain the position within the font list being queried. To query a font list, you need to use the following sequence of operations:

XmFontListInitContext() initializes a font context that lets an application get the individual font list entries from the font list. This routine takes the following form:
   Boolean
   XmFontListInitFontContext(context, fontlist)
       XmFontContext  *context;
       XmFontList      fontlist;
The routine is passed the address of an XmFontContext variable and a font list. It allocates a new font context structure based on the font list and returns True. If the font list is not valid or there is not enough memory available to allocate a new context, False is returned.

Once the font context has been initialized, the entries in the font list can be retrieved using XmFontListNextEntry():

   XmFontListEntry
   XmFontListNextEntry(context)
       XmFontContext context;
This routine cycles through all of the font list entries in the font list. The first call returns the first entry in the font list; repeated calls using the same font context access successive entries. Since the XmFontListEntry type is also opaque, you have to use XmFontListEntryGetFont() and XmFontListEntryGetTag() to retrieve the actual font or font set and tag for the font list entry. These routines take the following form:
   XtPointer
   XmFontListEntryGetFont(entry, type_return)
       XmFontListEntry  entry;
       XmFontType      *type_return;
   char *
   XmFontListEntryGetTag(entry)
       XmFontListEntry entry;
XmFontListEntryGetFont() returns an XFontStruct or an XFontSet depending on the value of type_return . The routine does not copy the data structure, so the application must not free it. XmFontListEntryGetTag() retrieves the tag for the font list entry. This routine allocates storage for the tag, so the application must free it.

In Motif 1.1, you call XmFontListGetNextFont() to cycle through the fonts in a font list. This routine has the following form:

   Boolean
   XmFontListGetNextFont(context, charset, font)
       XmFontContext    context;
       XmStringCharSet *charset;
       XFontStruct     **font;
If the function returns True, the character set and font pointers are set to the appropriate values. The charset returned is a pointer to allocated data that must be freed when no longer needed. The value for font points to the actual XFontStruct data used in the font list, so it must not be freed. If the end of the list has been reached, the function returns False . This routine exists for backwards compatibility and should not be used with Motif 1.2.

When you are done querying the font list, you need to free the font context using XmFontListFreeFontContext(), which takes the following form:

   void
   XmFontListFreeFontContext(context)
       XmFontListFontContext context;
If you are searching through a font list and need to back up, you must restart the entire process by freeing the current font context and creating a new one.

20.5 Rendering Compound Strings

Motif always renders compound strings automatically within its widgets, so you should never find yourself in a situation where you need to render a compound string manually. However, if you are writing your own widget, you may need to incorporate the same type of functionality. Motif provides three functions that render compound strings:

   XmStringDraw()
   XmStringDrawImage()
   XmStringDrawUnderline()
In Motif 1.2, all of these routines use the X11R5 text output routines when necessary, to ensure that the text is rendered correctly for the current locale.

The most basic rendering function is XmStringDraw(), which takes the following form:

   XmStringDraw(display, window, fontlist, string, gc, x, y, width,
                alignment, layout_direction, clip);
       Display       *display;
       Window         window;
       XmFontList     fontlist;
       XmString       string;
       GC             gc;
       Position       x, y;
       Dimension      width;
       unsigned char  alignment;
       unsigned char  layout_direction;
       XRectangle    *clip;
As you can see, the function requires a great deal of information to actually render the string. If you are rendering into a widget, you can specify the display and window using XtWindow() and XtDisplay(). Since a gadget does not have a window, you must use XtWindowOfObject() with a gadget. The fontlist parameter can be constructed using any of the functions described in Section #sfontlist, or you can retrieve a font list from a widget using XtVaGetValues().

The function also requires a graphics context ( GC) so that certain rendering attributes such as color can be applied. A graphics context is generally not available through a widget, so you have to get one at the Xlib level. If you are writing your own widget, you can probably use a GC that is cached by Xt and returned by XtGetGC() (see Volume Four, X Toolkit Intrinsics Programming Manual). Also, if you are writing your own widget, you may want to consider exposing the GC to the programmer in the form of a resource.

The x, y, and width parameters specify the coordinates and width of the rectangle that contains the compound string. The width parameter is used only for alignment purposes. There is no height parameter because the font list may specify fonts that are unknown in size and whose heights are too variable. The clip parameter defines the drawing boundary; you can pass NULL to indicate that the rendering should not be clipped.

The alignment parameter can be set to one of the following values:

   XmALIGNMENT_BEGINNING
   XmALIGNMENT_CENTER
   XmALIGNMENT_END
The value identifies the justification for the text. The effect of the value is modified by the value of the layout_direction parameter, which can be set to XmSTRING_DIRECTION_L_TO_R or XmSTRING_DIRECTION_R_TO_L.

The function XmStringDrawImage() is to XmStringDraw() as XDrawString() is to XDrawImageString(). The difference is that the image routines overwrite the background even in places where the font does not set bits in the character image, while the other routines only render foreground pixels.

The XmStringDrawUnderline() routine takes the same parameters as XmStringDraw() with one addition. The last parameter specifies the portion of the compound string that should be underlined. A compound string can be wholly or partially underlined depending on whether the last parameter specifies the entire compound string or only a substring of the string parameter.

It may be necessary to get dimensional information about a compound string in order to know where to place it within the window when it is drawn. You may also want this data to determine the optimal or desired width and height of a widget in case you have to provide a geometry callback method. When a call to XtQueryGeometry is made, a widget that contains compound strings may need to tell the calling function the dimensions it needs to render its compound strings adequately. Motif provides the following routines to help you determine compound string dimensions:

   XmStringBaseLine()
   XmStringExtent()
   XmStringHeight()
   XmStringWidth()

Each of these functions takes fontlist (XmFontList) and string (XmString) parameters. The font list is dependent on the widget associated with the string, but there is no requirement that you must use a string that is associated with a widget. If you just want to get the dimensions of a particular compound string rendered using an arbitrary font or font set, you can create a font list manually, as described in Section #sfontlist.

XmStringBaseline() returns the number of pixels between the top of the character box and the baseline of the first line of text in the compound string. XmStringWidth() and XmStringHeight() return the width and height, respectively, for the specified compound string. XmStringExtent() takes two additional parameters, width and height . These arguments return the width and height in pixels of the smallest rectangle that encloses the compound string specified in string .

20.6 Summary

Compound strings can be useful for creating multi-line or multi-font text for widgets such as Labels, PushButtons, and ToggleButtons. Compound strings were also designed to help in making internationalized applications, but this functionality has basically been made obsolete by the addition of internationalization features in X11R5. Since Motif applications have to use compound strings to display most textual data, the trick to developing an internationalized application is to use compound strings without interfering with lower-level X internationalization functionality.

The best practice is to specify compound string and font list resources in resource files, so that you can have a separate file for each language that is supported by your application. If you have to create compound strings in an application, you should use XmStringCreateLocalized() or specify the XmFONTLIST_DEFAULT_TAG font list tag to ensure that the strings are interpreted and rendered in the current locale.


Contents Previous Next