Movatterモバイル変換


[0]ホーム

URL:


ContentsPreviousNext

25 Creating a User Interface With UIL


This chapter expands upon the overview of UIL and Mrm presented earlier. The syntax and usage of UIL are described in detail, along with the Mrm functions associated with the various UIL constructs.

Now that you have a basic understanding of how UIL and Mrm are used to define and create a user interface, we can turn to the details of using UIL and Mrm. Recall that a UIL module can contain five different types of sections: theobject section for defining widgets; thevalue section for defining resource values and callback arguments; theidentifier section for declaring application variables exported to UIL; theprocedure section for declaring callbacks; and thelist section for defining lists of widgets, resource settings, callback settings, and callback routines.

An application accesses UIL definitions using the Mrm library. Mrm functions serve three basic purposes: file handling, importing information from UIL, and exporting information to UIL. Examples of each of these types of functionality appear in the hello_world.c program in Chapter 22, Introduction to UIL. The functions that import information create widgets that are defined inobject sections and retrieve data that is defined invalue sections. The functions that export information register callbacks that are declared inprocedure sections and application data that is declared inidentifier sections. There are no Mrm functions that work with UIL lists, because unlike other UIL entities, lists are strictly internal to a module.

In this chapter, we describe the role of UIL in each major step of creating an application:

We also talk about two other related topics:
The vast amount of information that is covered in this chapter makes it impractical to illustrate all of the UIL and Mrm concepts with a single UIL module or application. Such an application would be quite large and unrealistic. Therefore, we demonstrate the features of UIL and Mrm with many small, self-contained examples. To facilitate this approach, we've put together a small C program that you can use to try out the various UIL modules and callback functions we discuss.

25.1 Viewing UIL Examples

The showuid.c program is designed to display a portion of a user interface that is defined in a UID file. The idea is to allow you to examine the output of different UIL modules without needing a separate program for every module. The complete source code of this program appears in the source code

   /* showuid.c --    * Program to show the interface defined in a UID file.    */   #include <stdio.h>   #include <Mrm/MrmAppl.h>   void quit();   void print();   static MrmRegisterArg callback_list[] = {       { "quit",     (XtPointer) quit },       { "print",    (XtPointer) print },     /* Add additional callback procedures here... */   };   typedef struct {       String root_widget_name;   } app_data_t;   static app_data_t app_data;   static XtResource resources[] = {       { "root", "Root", XmRString, sizeof(String),           XtOffsetOf (app_data_t,root_widget_name), XmRString,           (XtPointer) "root" },   };   static XrmOptionDescRec options[] = {       { "-root", "root", XrmoptionSepArg, NULL },   };   void   quit (w, client_data, call_data)   Widget    w;   XtPointer client_data;   XtPointer call_data;   {       exit (0);   }   void   print (w, client_data, call_data)   Widget    w;   XtPointer client_data;   XtPointer call_data;   {       char *message = (char *) client_data;       puts (message);   }   main (argc, argv)   int   argc;   char *argv[];   {       XtAppContext  app_context;       Widget        toplevel;       Widget        root_widget;       Cardinal      status;       MrmHierarchy  hierarchy;       MrmType       class_code;       XtSetLanguageProc (NULL, NULL, NULL);       MrmInitialize();       toplevel = XtVaAppInitialize (&app_context, "Demos", options,           XtNumber(options), &argc, argv, NULL, NULL);       XtGetApplicationResources (toplevel, &app_data, resources,           XtNumber(resources), NULL, 0);       /* Check number of args after Xt and App have removed their options. */       if (argc < 2) {           fprintf (stderr,               "usage: showuid [Xt options] [-root name] uidfiles ...0);           exit (1);       }       /* Use argc and arv to obtain UID file names from the command line.          (Most applications use an internal static array of names.) */       status = MrmOpenHierarchyPerDisplay (XtDisplay (toplevel), argc - 1,           argv + 1, NULL, &hierarchy);       if (status != MrmSUCCESS) {           XtAppError (app_context, "MrmOpenHierarchyPerDisplay failed");           exit (1);       }       MrmRegisterNames (callback_list, XtNumber (callback_list));       status = MrmFetchWidget (hierarchy, app_data.root_widget_name,           toplevel, &root_widget, &class_code);       if (status != MrmSUCCESS) {           XtAppError (app_context, "MrmFetchWidget failed");           exit (1);       }       MrmCloseHierarchy (hierarchy);       XtManageChild (root_widget);       XtRealizeWidget (toplevel);       XtAppMainLoop (app_context);   }
This program is similar to the hello_world.c program in Chapter 22, Introduction to UIL. However, we've made a few small changes to make the program flexible enough to accommodate our needs in this chapter. Themain() procedure follows the steps required of any Mrm program except that the UID files containing the interface description and the name of the widget to be created are not hard-coded in the program. This information is now specified on the command line, so the program can be used to display different UID files and widget trees.

The list of UID files passed toMrmOpenHierarchyPerDisplay() is taken directly from the command line. Sinceargv is in the format expected by the routine, we pass it directly to the routine, after adding1 to skip the name of program inargv[0]; We then subtract1 fromargc to account for the difference.MrmOpenHierarchyPerDisplay() is called after the other command-line arguments have been removed byXtVaAppInitialize() andXtGetApplicationResources().

You can specify the name of the widget hierarchy created by the program with the-root option. Xt takes care of parsing the command-line switch and putting the value into theapp_data structure. (See Volume Four, X Toolkit Intrinsics Programming Manual, for detailed information on this process.) If you do not specify the-root option, the application usesroot as the default name. In most of the modules in this chapter, we use the default nameroot for the top-level widget.

The following command compiles the showuid program:

   cc -o showuid showuid.c -lMrm -lXm -lXt -lX11
To display a UID file, use the following command:
   showuid -root form hello_world.uid
The command-line options tell the command to open a description file named hello_ world.uid and create the widget hierarchy rooted at the widget namedform. Mrm searches for the UID files specified on the command line using the UIDPATH environment variable if it is set, or the default path described in Chapter 22. It is easy to run the command on a file in the current directory, since the current directory is included in the default search path. Remember that you can also specify an absolute path to a UID file.

We recommend that you use the showuid program for trying out our examples as well as experimenting on your own. In addition, the program is an excellent starting point for your own Mrm programs. The basic Mrm framework is already in place. You only need to add the callbacks that implement your application's functionality and provide an array of UID files, instead of taking them from the command line.

25.2 Defining and Creating Widgets

As you know, the main purpose of a UIL module is to define the widgets of a user interface. We mentioned earlier that widget definitions always occur in anobject section of a UIL module, which begins with the keywordobject followed by one or more widget definitions. The complete form of a widget definition is shown in the figure.

figs.eps/V6a.24.01.eps.png
Structure of a widget definition

The figure may seem a little imposing at first, but if we ignore all the optional parts of the definition, it is really quite simple. the source code defines a PushButton widget namedroot using only the required parts of a definition. This module, along with theshowuid.c program, comprise all the source necessary for a complete application.

   /* trivial.uil -- Illustrate a minimal widget declaration. */   module trivial   object root : XmPushButton { };   end module;
The widget definition in the source code consists of three parts, not including theobject keyword. The definition begins with the widget name, which is a programmer-defined identifier. The name of the widget in this example isroot. The type of widget follows the name; a colon separates the name and the type. Legal widget types include all of the standard Motif widgets as well as the names of specific instances of Motif widgets, such asXmMenuBar (a RowColumn) orXmQuestionDialog (a MessageBox). You can find a complete list of widget type names in Appendix D, Table of UIL Objects, of Volume Six B, Motif Reference Manual. UIL also supports non-Motif widgets with theuser_defined type, which we explain later in Chapter 26, Advanced UIL Programming. The last, and usually largest, part of a widget definition is made up of the widget attributes. In our simple definition, we do not specify any attributes, but even so, we must include the curly braces that would surround them. Widget definitions always end with a semicolon.

After compiling the module, we can display its output with the showuid program. The following two commands accomplish these steps:

   uil -o trivial.uid trivial.uil   showuid trivial
You don't need to use the-root option because the PushButton uses the default widget nameroot. The output of the program appears in the figure.

figs.eps/V6a.24.02.eps.png
User interface of trivial.uil


25.2.1 Specifying Widget Attributes

A bare-bones widget definition like the one in the previous example is rare in even the simplest Motif applications. To create a useful interface, you need a hierarchy of customized widgets, which is where widget attributes enter the picture. When you define a widget in UIL, you can specify children and resources in subsections of its attribute section. Thecontrols subsection contains a list of a manager widget's children, and thearguments andcallbacks subsections contain lists of the widget's resource and callback settings. Each subsection begins with the subsection name followed by a list of children, resources, or callback settings. Each subsection can occur only once in a single widget definition, but they can occur in any order. Thecontrols subsection of a widget definition is where you specify the children of the widget. The name of this subsection was chosen because the parent widget manages, or controls, the child widgets. The module in the source code shows a typical usage of thecontrols subsection.

   /* kids.uil -- Simple demonstration of the controls subsection. */   module kids   object top : XmTextField { };   object bottom : XmPushButton { };   object root : XmPanedWindow {     controls {       XmTextField top;       XmPushButton bottom;     };   };   end module;
In this example, we define three widgets: a TextField, a PushButton, and a PanedWindow. Thecontrols subsection of the PanedWindow specifies that the TextField and the PushButton are its children. The example illustrates the form of an entry in acontrols subsection, where the widget type is followed by the name of a widget and a semicolon. Even though the type of the widget has already been specified in a separate widget definition, you must specify it again here. In this example, we define the children before their parent, but widgets referenced in acontrols subsection can be defined anywhere in the UIL module because UIL allows forward references. The output of the module is shown in the figure, where we typed some text in the TextField.

figs.eps/V6a.24.03.eps.png
User interface of kids.uil

Each of the three widget definitions begins withobject, which means that each of them is in a separate object section. Technically, you only need theobject keyword before the first widget in consecutive definitions. Although the convention of placing anobject keyword before each definition requires a bit more typing, it makes definitions easier to recognize and move around in a module.

One advantage of defining your interface with UIL is that the compiler always makes sure that the children listed in acontrols subsection are allowed for that parent. If you try to use an unsupported widget, the UIL compiler issues an error message, and the compilation fails. Appendix D, Table of UIL Objects, in Volume Six B, Motif Reference Manual, contains a complete listing of the Motif widgets and the children that they support. In contrast, when you create widgets directly with Xt, there is no compile-time checking that makes sure the widget hierarchy is valid. Many of the Motif manager widgets provide some form of run-time checking, but we don't recommend relying on this behavior.

The presence of a widget definition in a UIL module does not necessarily mean that the widget is created at run-time. A widget is not created until you fetch it directly withMrmFetchWidget() orMrmFetchWidgetOverride(), or it appears in a widget hierarchy fetched with one of these routines. By referencing a widget in acontrols subsection, you make it part of a widget hierarchy. When the hierarchy is fetched, all of the widgets in the hierarchy are created. (The widget creation process is described in Section #suilcreate.) If a widget is defined, but never referenced or fetched from an application, it is never created.

When a widget is created based on a UIL definition, you are not limited to creating a single instance of it. Every call toMrmFetchWidget() orMrmFetchWidgetOverride() results in the creation of a new widget, assuming a definition is found. In addition, each widget reference in acontrols subsection results in the creation of an instance of that widget when the enclosing hierarchy is fetched. This behavior lets you reuse a widget as often as necessary. You can reuse widgets at any level, from a single widget to an entire hierarchy. You can place a complete widget definition inside acontrols subsection, instead of referencing a widget defined elsewhere. For example, we can move the child widget definitions from the source code into the body of the PanedWindow definition, as shown in the following fragment:

   object root : XmPanedWindow {     controls {       top : XmText { };       bottom : XmPushButton { };     };   };
The form of an in-line widget definition is the same as a widget definition in anobject section. In-line definitions are most useful for specifying widget children that have few or no attributes. While larger definitions are allowed, they tend to clutter up the parent definition, which makes both reading and editing the module more difficult.

Unlike widgets defined in an object section, the name of an in-line widget is optional. This feature is most frequently used in menu definitions, as the following fragment illustrates:

   object file_menu : XmPulldownMenu {     controls {       XmPushButton open;       XmPushButton print;       XmSeparator { };       XmPushButton quit;     };   };
This definition contains an unnamed Separator, along with references to three PushButtons that are defined elsewhere. In this situation, it is worthwhile to create a stand-alone definition for the Separator because it doesn't have any attributes. The UIL compiler automatically generates a name when you don't provide one. The format of these names is not documented and can vary from one compilation to the next. If a widget does not have a well-defined name, neither you nor the users of your application can customize it using X resource files. If you want to allow such customizations, you must explicitly name the widget. When you define an object of a class that has both a widget and a gadget variant, you can specify in the definition which type is created. Motif supports widget and gadget variants of the Label, PushButton, ToggleButton, ArrowButton, CascadeButton, and Separator objects. As we explained in Section #suiloptions, you can specify the default type for each class in theobjects option setting at the top of a module. If you do not set this option, widgets are used by default. The following code fragment demonstrates how to define a PushButtonGadget, regardless of the default PushButton type setting:
   object push_me : XmPushButton gadget { };
UIL also supports the type names withGadget appended, so the following definition is also legal:
   object toggle_me : XmToggleButtonGadget { };
You can use thewidget keyword to ensure that the widget version of an object is created, as shown in the following fragment:
   object this_way : XmArrowButton widget { };
This syntax is the only way to specify a widget variant; the UIL compiler does not recognize type names such asXmArrowButtonWidget. Several Motif widgets are compound objects, which means that they are composed of one or more simpler widgets. For example, the FileSelectionBox is a complete dialog box packaged as a widget; it contains Lists, TextFields, Labels and PushButtons. As of Motif 1.2, UIL lets you access and customize the automatically-created children of a compound object. Like other child widgets, you reference automatically-created children in thecontrols subsection of their parent, although the syntax is slightly different. The following code fragment illustrates this syntax:
   object yes_or_no : XmQuestionDialog {     controls {       Xm_OK {         arguments {           XmNlabelType = XmPIXMAP;           XmNlabelPixmap = xbitmapfile ('thumb_up.xbm');         };       };     };   };
This fragment shows how to make the OK PushButtons in a QuestionDialog display an icon instead of the usual text string. The name of this button isXm_OK. The name is followed by attribute settings, just like any other widget definition. lists the names of all the automatically-created children of each of the Motif composite widgets. tab(@), linesize(2); lp9 | lp9 lp9 | lp9.
Widget@Child Names
_
XmScale@Xm_Title XmScrolledWindow@Xm_VertScrollBar, Xm_HorScrollBar XmOptionMenu@Xm_OptionLabel, Xm_OptionButton XmPopupMenu@Xm_TearOffControl XmPulldownMenu@Xm_TearOffControl XmMainWindow@Xm_Separator1, Xm_Separator2, Xm_Separator3 XmMessageBox@T{ Xm_Symbol, Xm_Separator, Xm_Message, Xm_OK, Xm_Cancel, Xm_Help T} XmSelectionBox@T{ Xm_Items, Xm_ItemsList, Xm_Selection, Xm_Text, Xm_Separator, Xm_OK, Xm_Apply, Xm_Cancel, Xm_Help T} XmFileSelectionBox@T{ Xm_Items, Xm_ItemsList, Xm_Separator, Xm_OK, Xm_Cancel, Xm_Help, ­Xm_FilterLabel, Xm_Filter, Xm_FilterText, Xm_DirList, Xm_Dir T}
_ Remember that the Motif compound objects provide resources that allow you to set the commonly-used resources of their children. For example, theXmNmessageString resource of the QuestionDialog is the same as theXmNlabelString resource of itsXm_Message child. It is better to set the resource on the compound object rather than on the child, so we suggest that before you set a resource on an automatically-created child, you make sure that the resource cannnot be set in thearguments subsection of the parent. Mrm automatically manages all of the widgets that you fetch with the exception of dialogs, menus, and the widget at the top of the fetched hierarchy. You can prevent Mrm from managing individual widgets by preceding theircontrols subsection entry with theunmanaged keyword, as shown in the following fragments:
   object panel : XmRowColumn {     controls {       XmPushButton visible;       unmanaged XmPushButton invisible;     };   };   object error : XmErrorDialog {     controls {       Xm_Help unmanaged { };     };   };
When thepanel RowColumn widget is created, both PushButtons are created, but only the first one is managed. When you want to manage the other button, your application code must handle it, just like it must manage dialogs and popup menus. You can also unmanage automatically-created children as shown in the second object definition above. In this case, theunmanaged keyword follows the name of the automatically-created widget instead of preceding it. In UIL, you specify resources (with the exception of callbacks) in thearguments subsection of a widget definition. The UIL module in the source code shows several examples of resource settings.
   /* resource.uil - Basic example of setting widget resources. */   module resource   object root : XmPushButton {     arguments {       XmNlabelString = "Candy-Gram!";       XmNmarginWidth = 350;       XmNmarginHeight = 350;       XmNunitType = Xm100TH_MILLIMETERS;       XmNforeground = color ('SlateGrey');       XmNbackground = color ('LemonChiffon');       XmNfontList = font ('*times-bold-r-normal*180-100-100*');     };   };   end module;
In this example, we set several PushButton resources. These settings demonstrate the use of a number of UIL data types. However, we're not going to discuss the data types right now, as they are covered later in Section #suiltypes. The basic format of each setting is the same. Each consists of a resource name, an equal sign (=), a value, and a semicolon. the figure shows the output of this example, which is quite different from the simple PushButtons in our earlier examples.

figs.eps/V6a.24.04.eps.png
User interface of resource.uil

Creating a PushButton with the same resource settings in application code requires a lot more work. You need to declare variables for theXmString,Color, andXmFontList values and then you must create or allocate each of these values by calling various Xm, Xt, and X routines. After the values are created, you can create the widget. Any values copied by the widget should be freed. When you use UIL and Mrm, all of this work boils down to the much simpler widget definition shown above and a single call toMrmFetchWidget().

The UIL compiler checks resource names, so if you specify a resource that is not supported by a widget, the compiler generates an error message. In contrast, if you try to set an unsupported resource withXtSetValues(), Xt ignores the resource and does not generate an error. By using UIL, you can also avoid setting a resource to the wrong type of value because the UIL compiler ensures that the type of resource matches the type of the value you assign to it. (Appendix C, Table of Motif Resources, in Volume Six B, Motif Reference Manual, contains a complete list of Motif widget resources and their associated types.) Once again, this type of error is not caught in C code when you useXtSetValues() orXtVaSetValues(). Unrecognized resource names are also ignored in X resource files.

The disadvantages of specifying resource values in code and in resource files may give you the impression that you should always set resources from a UIL module. However, there are also disadvantages to setting resources in UIL. The main disadvantage is that users of your application cannot override UIL settings with their own resource settings. In Section #suilres we take a closer look at the issues involved in deciding whether to set a resource in UIL, application code, or an X resource file. The type of a value you assign to a resource must match the type of the resource. However, there are a few cases in which the UIL compiler automatically converts a value to the appropriate type. The supported conversions are shown in lp9 | lp9 lp9 | lp9. Value Type Automatically Converted To
_
stringcompound_stringasciz_string_tablecompound_string_tablefontfont_listfontsetfont_listiconpixmapxbitmapfilepixmaprgbcolor _ This feature is most useful when you are working with string and font values. the source code relies on thestring tocompound_string (XmString) conversion for setting the­XmN­labelString resource. Several of the Motif widgets have array resources for which there is an associated count resource that indicates the size of the table. These resource pairs are given special treatment by the UIL compiler. Whenever you set one of the resources listed in UIL automatically sets the corresponding count resource for you. lp9 | lp9 | lp9 lp9 | lp9 | lp9. Widget Table Resource Coupled Count Resource
_
XmListXmNitemsXmNitemCount XmListXmNselectedItemsXmNselectedItemCount XmSelectionBoxXmNlistItemsXmNlistItemCount XmCommandXmNhistoryItemsXmNhistoryItemCount XmFileSelectionBoxXmNdirListItemsXmNdirListItemCount XmFileSelectionBoxXmNfileListItemsXmNfileListItemCount XmTextXmNselectionArrayXmNselectionArrayCount XmTextFieldXmNselectionArrayXmNselectionArrayCount _ The following code fragment illustrates this feature:

   object toppings : XmScrolledList {     arguments {       XmNitems = string_table ("Anchovies", "Extra Cheese", "Ham",         "Mushroom", "Pepperoni", "Peppers", "Pineapple", "Sausage");       XmNselectedItems = string_table ("Ham", "Pineapple");       XmNvisibleItemCount = 6;     };   };
This fragment sets twoXmStringTable resources in a List widget. We do not have to set theXmNitemCount orXmNselectedItemCount resources because the UIL compiler sets them automatically. Although callbacks are really just another type of resource, you specify them separately in thecallbacks subsection of a widget definition. Since callback functions are implemented in application code, the process of setting up callbacks involves a few more steps than the specification of other attributes. We explained the basics of this process in Chapter 22, Introduction to UIL. In this section, we describe how to add a callback procedure to a widget. In Section #suilproc, we discuss declaring callbacks, specifying callback arguments, and registering callbacks with Mrm.

Setting a callback in a UIL module requires two steps. First, you declare the callback in aprocedure section, and then you specify the callback in a widget definition. The module in the source code illustrates this process.

   /* cb.uil - Plain and simple callback setting example. */   module cb   procedure     print (string);     quit();   object Hello : XmPushButton {     callbacks {       XmNactivateCallback = procedure print ("hello!");     };   };   object Goodbye : XmPushButton {     callbacks {       XmNactivateCallback = procedures {         print ("goodbye!");         quit();       };     };   };   object root : XmRowColumn {     controls {       XmPushButton Hello;       XmPushButton Goodbye;     };   };   end module;
The callback declarations in theprocedure section tell the UIL compiler that the procedures are defined externally in the application program. A callback setting looks similar to a resource setting; it always begins with the name of a callback, such asXmNactivateCallback, and is followed by an equal sign. The right-hand side of the setting varies depending on the number of callback procedures you are specifying. A single callback is specified with the keywordprocedure followed by the callback invocation. Multiple callbacks are specified with the keywordprocedures followed by a list of callback invocations.

In the source code theXmNactivateCallback of the Hello PushButton is set to the single callback procedureprint(), while theXmNactivateCallback of the Goodbye PushButton is set to the two callbacksprint() andquit(). You cannot specify multiple callbacks by setting the same callback more than once because when you set the same resource or callback multiple times, only the last setting is used. The Xt specification doesn't guarantee the order in which callbacks are called, as widgets can reorder callback lists internally. In nearly all cases, however, callbacks are called in the order that they are listed.

As with resource settings in anarguments section, the UIL compiler makes sure that the callbacks you set in acallbacks subsection exist and are supported by the widget. When you add callbacks in application code, there is nothing to prevent you from setting a callback on a widget that does not support it. This problem is not caught at compile-time or run-time by Xt.

25.2.2 Sharing Widgets Among Modules

When the source code for an application grows beyond a certain size, you normally split it into multiple source files. You can use the same technique to divide an interface description among multiple UIL modules. When you use this technique, one module must often reference a widget that is defined in another module. UIL supports this technique by allowing you to export a widget definition from one module and import, or reference, the definition in another module. A widget definition is exported by using the optionalexported storage specifier before the widget type name in the definition, as shown in the source code

   /* first.uil - First half of a two-module interface description. */   module first   object top : exported XmText { };   end module;
Anexported definition looks and acts just like a regular definition. The difference is that you can access an exported widget in another module by declaring it with theimported storage class specifier. This technique is illustrated in the source code which imports thetop widget from the source code
   /* second.uil - Second half of a two-module interface description. */   module second   object top : imported XmText;   object bottom : XmPushButton { };   object root : XmPanedWindow {     controls {       XmText top;       XmPushButton bottom;     };   };   end module;
Since theimported declaration refers to a widget defined elsewhere, you cannot specify attributes for the widget and must end the declaration immediately after the type name, as shown in this example. You can think of animported widget declaration as having the same meaning as anextern variable declaration in C. Collectively, the two modules describe the same interface as the source code After compiling these two modules, you can view the interface with the following command:
   showuid first second
Placing a single widget in a separate file, as we've done in this example, is clearly a trivial example of sharing widgets. This technique makes more sense when you are creating a larger interface for a real application. You can see a more realistic example of sharing widgets in Chapter 25, Building an Application With UIL.

Widget definitions, like top-level variable definitions in C, are global by default, which means that you really don't need to use theexported storage specifier. However, we recommend using it when you plan to reference a widget in another module because it clearly indicates which widget definitions you expect to use elsewhere. When you import a widget, UIL assumes that the widget class in theimported declaration matches the class of the widget definition. If you make a mistake and import a widget that is different from its declared class, you defeat the compiler's type-checking of the imported widget and may run into problems at run-time. Although some of the Motif managers can detect an attempt to create an unsupported child, you should ensure that your widget definitions and declarations match rather than relying on possible run-time detection.

UIL also supports theprivate storage specifier. This specifier allows you to restrict the use of a widget definition to the module in which it occurs. Thestatic storage class specifier in C has the same effect on C functions and variables. As of Motif 1.2, however, widgets defined asprivate can still be accessed from other modules. Although theprivate storage specifier is rarely used, you can specify it if you want to protect access to private widgets (assuming the problem will be fixed), or if you want to explicitly indicate that a widget should not be referenced elsewhere.

25.2.3 The Widget Creation Process

Now that you know how to define widgets in a UIL module, we can take a closer look at how to create widgets at run-time usingMrmFetchWidget(). In Chapter 22, Introduction to UIL, we showed you the basics of usingMrmFetchWidget() to create a widget or a widget hierarchy. As a reminder, this function takes the following form:

   Cardinal   MrmFetchWidget(hierarchy,widget_name,parent,widget_return,class_return)       MrmHierarchyhierarchy;       Stringwidget_name;       Widgetparent;       Widget        *widget_return;       MrmType       *class_return;
Thehierarchy argument is anMrmHierarchy that has been opened withMrmOpenHierarchyPerDisplay(). Thewidget_name parameter is the name of the widget to fetch. Theparent argument is the parent of the widget that is to be created. On success,widget_return contains the widget ID of the widget andclass_return contains the internal UIL class code for the widget.

You can also fetch a widget by callingMrmFetchWidgetOverride(), which lets you override resource settings in the application. This routine takes the following form:

   Cardinal   MrmFetchWidgetOverride(hierarchy,widget_name,parent,override_name,arg_list,num_args,widget_return,class_return)       MrmHierarchyhierarchy;       Stringwidget_name;       Widgetparent;       Stringoverride_name;       ArgListarg_list;       Cardinalnum_args;       Widget        *widget_return;       MrmType       *class_return;
Theoverride_name argument lets you specify a name for the widget that differs fromwidget_name.widget_name is used only to look up the widget definition. Ifoverride_name isNULL,widget_name is used for the name. Thearg_list andnum_arg parameters specify a standard array of Xt resource name-value pairs. Any resources specified in this list override those specified in the widget definition from the UIL module. The rest of the parameters are the same as forMrmFetchWidget().

For each of these functions, Mrm first makes sure that thehierarchy specified is valid and open. If you supply an invalidhierarchy to a function, it immediately fails and returnsMrmBAD_HIERARCHY. Assuming thehierarchy is valid, the two routines use the widget creation algorithm illustrated in the figure and described in the following sections.

figs.eps/V6a.24.05.eps.png
Widget creation algorithm

Mrm begins the widget creation process by searching for the widget definition in the UID files associated with the hierarchy. The files are searched in the same order as they appear in the array passed toMrmOpenHierarchyPerDisplay(). The search order matters when two widgets with the same name are defined in different files, as Mrm uses the first definition that it finds. Once Mrm locates the widget definition, it reads it from the UID file and moves on to the next step. If Mrm cannot find the widget after looking in each file, it prints a warning message by callingXtAppWarning().

If the missing widget is at the root of the hierarchy that the application is fetching,MrmFetchWidget() returns a status ofMrmNOT_FOUND. But if the missing widget is one of its descendents, the widget hierarchy creation process continues, minus one widget. While a failure to create a child widget is bound to cause problems for your application,MrmFetchWidget() unfortunately returnsMrmSUCCESS as long as the top-level widget is created. Before Mrm creates a widget, any resource or callback settings are put into anArgList. Many resource settings, such as colors and fonts, are created and maintained by the X server, which means that they cannot be stored in a UID file. Instead, descriptions of these values are stored in the UID file. Mrm creates the actual values at run-time based on these descriptions. Other values, such as integers, strings, andXmStrings, are read from the UID file and placed directly into theArgList. Mrm also converts callback names stored in the UID file to function pointers that the application registered by callingMrmRegisterNames() orMrmRegisterNamesInHierarchy().

If for any reason Mrm cannot create a resource value or cannot find a the specific resource or callback and prints a warning message usingXtAppWarning(). This type of failure does not prevent Mrm from creating the widget, and the status returned byMrmFetchWidget() orMrmFetchWidgetOverride() does not indicate that a problem occurred.

If you are fetching a widget withMrmFetchWidgetOverride(), callback function pointer to match a callback name, it does not set theArgList you pass to this function is appended to the internally generatedArgList of the top-level widget. Override arguments do not affect any widgets further down in the hierarchy. Since Xt uses the last occurrence of a resource or callback setting in anArgList to set the value, the settings from the application program override any settings specified in the widget definition. You can also override widget resource settings after a widget is created by usingMrmFetchSetValues(), which is described in Section #suilfetch. Now Mrm calls the widget creation function corresponding to the class of the widget. For the built-in Motif widgets, Mrm uses the Motif convenience functions, such asXmCreatePushButton(). Some widgets, like the FileSelectionBox, create their own children at the time they themselves are created. Mrm is aware of these children, but is not responsible for their creation. Foruser_defined widgets, Mrm calls the creation procedure that you specified when registering the widget. (User-defined widgets are described in Section #suiluserdef.) Mrm does not manage the widget at this point in the procoess. In addition to the callbacks that are part of each widget, Mrm and UIL support a special creation callback,MrmNcreateCallback, which is invoked by Mrm immediately after the widget is created. In the case of an automatically-created child, the callback is invoked after its resources are set. The widgets are not aware of the callback, since it is handled directly by Mrm. TheMrmNcreateCallback takes the same form as any other callback and is specified in thecallbacks subsection of a widget definition. Theclient_data argument is anXmAnyCallbackStruct, which is defined in <Xm/Xm.h> as follows:

   typedef struct {       int     reason;       XEvent *event;   } XmAnyCallbackStruct;
Thereason field is always set toMrmCR_CREATE, and theevent pointer is alwaysNULL. You can use this callback to handle almost anything you would normally do in a standard Xt program after creating a widget. At this point, the widget creation process becomes recursive. If the newly-created widget has any children specified in itscontrols subsection, Mrm creates them now. Mrm uses the process just described to create each of the children. Automatically-created children are also processed recursively so that Mrm can handle any resources or callbacks specified in the UIL file. Instead of creating an automatically-created child in the widget creation step, Mrm just sets the resources and callbacks using theXtArgList for the child.

The recursive nature of the widget creation process allows you to create, with a single function call, a user interface that consists of an arbitrarily large widget hierarchy. This behavior is what makesMrmFetchWidget() andMrmFetchWidgetOverride() so powerful. As we mentioned earlier, if Mrm cannot create a child widget, it prints a warning message usingXtAppWarning() and continues with the next child. In general, both fetch functions continue working through just about any type of failure, short of not finding the definition of the top-level widget in the hierarchy. If any children have been created, Mrm now manages them. Mrm manages all non-Shell children that are part of thecontrols subsection of the parent widget, unless they are declared asunmanaged. Since the creation process is recursive, any children of the widgets that are being managed have been managed previously. The top-level widget that is being fetched is not managed by Mrm because the management step only applies to the children of a widget. After all of the widgets in the hierarchy have been created, there may still be some resources that Mrm needs to set because UIL allows you to make forward references to widgets. As a result, you can specify widgets in resource settings and as callback arguments without worrying about the creation order of the widgets involved. If you reference a widget before it is defined, Mrm cannot resolve the reference when it is encountered. To handle this situation, Mrm remembers the reference and resolves it once all of the widgets in the hierarchy have been created.

The ability to use forward references makes UIL quite flexible. One situation where this feature is useful is when you create an interface that uses the Form widget. With UIL, you can specify complex Form attachments without having to worry about the creation order of the widgets. The one limitation to this feature is that it only works within a single call toMrmFetchWidget(). During a call toMrmFetchWidget(), Mrm maintains a list of the widgets that have been created, which means that you can only reference a widget that is part of the hierarchy created by the current call. If you need to set a resource to a widget in another hierarchy, you can set it using theMrmNcreateCallback or set it after the hierarchy has been created. After the entire hierarchy has been created, Mrm returns the widget ID of the top-level widget to the application. The top-level widget is the one that you name in theMrmFetchWidget() orMrmFetchWidgetOverride() function call. Remember that Mrm does not manage this widget, so an application must explicitly callXtManageChild() on the widget. Although the widget creation process is rather involved, all you really need is a general understanding of the process. If you encounter problems with Mrm widget creation, you can return to this section to brush up on the details.

25.3 Defining and Fetching Values

UIL supports over 20 different data types, which gives you the ability to specify values for nearly every Motif resource. In addition, most types of values can be passed as theclient_data argument to your callbacks or retrieved on demand from a UID file by Mrm. Each of the types has its own syntax so that the UIL compiler can distinguish between them. But before we describe the syntax of each value, we need to look at defining symbolic variables and retrieving variables at run-time using Mrm.

Variables provide a convenient and descriptive way to refer to values. Variables are defined in avalue section of a UIL module. This section begins with the keywordvalue and consists of one or more variable definitions. Mostvalue sections define variables for familiar values like integers and strings, as shown in the following fragment:

   value     spacing : 10;     warning : "Aviso";
A value definition consists of an identifier followed by a colon, the value assigned to the identifier, and a semicolon.

UIL supports forward references to variables, so you don't need to declare or define a variable before you reference it. However, we recommend that you avoid forward references for a couple of reasons. The first reason is purely stylistic. Programmers expect to see a definition or declaration before a reference, since it is required by many programming languages. A module is also easier to read if variables are defined or declared before they are used. Another reason has to do with the UIL compiler. While forward references tend to work most of the time, problems with the compiler may cause unexpected errors depending on the context in which you use a forward-referenced variable.

25.3.1 Sharing Values Between Modules

Like widgets, you can share most values between modules by defining anexported value in one module and declaring itimported in another module. This feature is commonly used to maintain strings in a separate module from the interface definition for internationalization purposes. (This style of internationalization is illustrated in Section #suili18n.) You can specify a storage class ofprivate (the default) orexported just before the value in a declaration, as shown in the following fragment:

   value     ducks : private 7;     swans : 3;     geese : exported 5;
The variablesducks andswans are accessible only in the module in which they are defined, while the variablegeese is accessible from any module. Unlikeprivate widget definitions,private variables really are private, so you cannot access them from another module. You can also retrieveexported values from an application, as you'll see shortly. You can use a variable from another module by declaring it as animported variable. The syntax is similar to animported widget declaration, as shown below:
   value     geese : imported integer;
Likeimported widgets, you need to make sure that the type of animported variable matches the type in its definition. If they do not match, there's a good chance you'll run into problems when you create a widget that references theimported value.

25.3.2 Fetching Values

As we mentioned earlier, an application can read all types ofexported variables from a UIL module, with the exception ofcharacter_set andcolor_table values. You retrieve mostexported variables usingMrmFetchLiteral(). However,pixmap andcolor are retrieved with special routines that we'll describe later. Fetching values from a UIL module is useful for obtaining internationalized strings or widget resource values that change dynamically based on the state of the program.MrmFetchLiteral() takes the following form:

   Cardinal   MrmFetchLiteral(hierarchy,name,display,value_return,type_return)       MrmHierarchyhierarchy;       Stringname;       Display       *display;       XtPointer     *value_return;       MrmCode       *type_return)
Mrm looks for the variable specified byname in the UID files associated with thehierarchy parameter. The files are searched in the same order as they appeared in the array of files passed toMrmOpenHierarchyPerDisplay(), so if two variables with the same name occur in separate files, you'll get the value from the first file in the list. When a value is fetched successfully, the function returnsMrmSUCCESS, fills invalue_return with a pointer to the value, and fills intype_return with a constant from <Mrm/MrmPublic.h> indicating the type of value. IfMrmFetchLiteral() cannot find the variable in any of the UID files, it returnsMrmNOT_FOUND.

Thevalue_return parameter usually contains a pointer to the value that you fetched, even for types such asinteger andboolean. You can check the type by examiningtype_return. lists each UIL data type, the type of the value placed invalue_return, and the associated type identifier placed intype_return.

tab(@), linesize(2); lp9 | lp9 | lp9 | lp9 lp8w(1i) | lp8 | lp8 | lp8.
Type@Mrm Return Type@C Return Type@Free Routine
_
asciz_table@MrmRtypeChar8Vector@String*@XtFree()boolean@MrmRtypeBoolean@int*@XtFree()class_rec_name@MrmRtypeClassRecName@WidgetClass@N/Acolor@N/A@Pixel@XFreeColors()compound_string@MrmRtypeCString@XmString@XmStringFree() T{compound_
string_table
T}@MrmRtypeCStringVector@XmStringTable@XtFree()float@MrmRtypeFloat@double*@XtFree()font@MrmRtypeFont@XFontStruct*@N/Afontset@MrmRtypeFontSet@XFontSet@N/Afont_table@MrmRtypeFontList@XmFontList@XmFonyListFree()icon@N/A@Pixmap@XFreePixmap()integer@MrmRtypeInteger@int*@XtFree()integer_table@MrmRtypeIntegerVector@int*@XtFree()keysym@MrmRtypeKeysym@char@N/Argb@N/A@Pixel@XFreeColors()single_float@MrmRtypeSingleFloat@float*@XtFree()string@MrmRtypeChar8@String@XtFree()translation_table@MrmRtypeTransTable@XtTranslations@N/Awide_character@MrmRtypeWideCharacter@wchar_t*@XtFree()xbitmapfile@N/A@Pixmap@XFreePixmap() _ Mrm allocates anint forboolean values, so you cannot use the XtBoolean type because on some machines it is defined as achar. However, you can still assign theint that is returned to aBoolean. The specialized routinesMrmFetchBitmapLiteral(),MrmFetchColorLiteral(), andMrmFetchIconLiteral() do not have anMrmType argument. If the named value is not the right type, a status ofMrmWRONG_TYPE is returned. Mrm allocates storage for most of the values returned byMrmFetchLiteral(). The application is responsible for freeing the storage; it uses the routine indicated in However, note that you should not freefont orfontset values because they are cached by Mrm and are reused as needed. There is no need to freeclass_rec_name orkeysym values because they are returned by value, and you cannot freetranslation_table ­values because Xt does not provide a way to free them. In addition, Mrm allocatesasciz_string_table,compound_string_table, andinteger_table values in a single chunk of memory, which means you should free them with a single call, rather than freeing the individual elements.

The following code fragment illustrates usingMrmFetchLiteral() to fetch astring and aninteger:

   extern MrmHierarchy hierarchy;   extern Widget toplevel;   Cardinal status;   MrmCode type;   String animal;   int *count;   status = MrmFetchLiteral (hierarchy, "animal", XtDisplay(toplevel),       (XtPointer) &animal, &type);   if (status != MrmSUCCESS || type != MrmRtypeChar8)       error ();   status = MrmFetchLiteral (hierarchy, "count", XtDisplay(toplevel),       (XtPointer) &count, &type);   if (status != MrmSUCCESS || type != MrmRtypeInteger)       error ();   printf ("There are %d %s0, *count, animal);   XtFree (count);   XtFree (animal);
Mrm fills in the string pointer and the integer pointer with the values from the UID file. Theinteger value is returned as a pointer to an integer. We check the types of the values returned just in case the values are not astring and aninteger as expected. The two values can be defined in a UIL module as follows:
   value     animal : exported "frogs";     count  : 7;
WithMrmFetchLiteral(), you can retrieve values from a UIL module that are not necessarily part of the user interface, such as printed error messages and program configuration values.

Since values fetched from a UIL module are often used to set resources of existing widgets, Mrm provides a function that handles this situation. If you useMrmFetchLiteral(), you still have to callXtVaSetValues() to set the values.MrmFetchSetValues() handles both fetching the values and setting the resources. This routine takes the following form:

   Cardinal   MrmFetchSetValues(hierarchy,widget,args,num_args)       MrmHierarchyhierarchy;       Widgetwidget;       ArgListargs;       Cardinalnum_args;
Thehierarchy argument specifies the Mrm hierarchy, andwidget specifies the widget on which to set the values. Theargs parameter is an array of resource settings, andnum_args specifies the size of the array. Each array element is anArg structure, which is defined as follows:
   typedef struct {       String    name;       XtArgVal  value;   } Arg, *ArgList;
This structure is the same one used withXtSetValues(), but it is used in a slightly different way. When you callMrmFetchSetValues(), thename field still specifies the name of a resource, but thevalue field names a UIL variable that contains the value instead of specifying the value directly. The function and its structure are demonstrated in theMessage() routine shown in the source code
   extern Widget message_dialog;   ...   void   Message(hierarchy, name)   MrmHierarchy hierarchy;   String name;   {       char msg_buf[33], type_buf[3];       Arg args[2];       sprintf (type_buf, "%s_type", name);       sprintf (msg_buf, "%s_msg", name);       args[0].name = XmNdialogType;       args[0].value = (XtArgVal) type_buf;       args[1].name = XmNmessageString;       args[1].value = (XtArgVal) msg_buf;       MrmFetchSetValues (hierarchy, message_dialog, args, XtNumber (args));       XtManageChild (message_dialog);   }
This function uses itsname argument to form two UIL variable names and callsMrmFetchSetValues() to fetch the values and set the resources of a MessageDialog. The string buffers are only 33 characters long because a UIL variable name can be at most 32 characters long. The corresponding variable definitions in a UIL module might look like the following:
   value     fnf_msg  : exported compound_string ("File not found!");     fnf_type : exported XmDIALOG_ERROR;     dsl_msg  : exported compound_string ("Almost out of disk space.");     dsl_type : exported XmDIALOG_WARNING;
An application could use the following function calls to display the MessageDialog with these messages:
     Message (hierarchy, "fnf");     Message (hierarchy, "dsl");

Each message string is explicitly defined as acompound_string in the UIL module. The UIL compiler only converts aNULL-terminatedstring to acompound_string when it is assigned to anXmString resource.

25.3.3 Numeric Values

UIL supports several numeric value types, specifically integers, booleans, floating point values, and integer arrays. In addition, UIL understands C-like numeric expressions and lets you explicitly convert numeric values from one type to another. Let's begin by looking at UIL integer values. The following fragment illustrates how you can define integer variables and set widget resources to integer values:

   value     spacing : 5;     font_size : exported -2;   object rc : XmRowColumn {     arguments {       XmNmarginWidth = 3;       XmNspacing = spacing;     };   };

Unlike in C, theboolean type is built into UIL. You representboolean values with the the reserved keywordstrue,false,on andoff, as shown in the following code fragment:

   value     alive : true;     debug : exported true;   object button : XmPushButton {     arguments {       XmNwidth = 100;       XmNrecomputeSize = false;       XmNsensitive = alive;       XmNtraversalOn = off;     };   };
The keywordstrue andon both represent true values, whilefalse andoff are both false values.

Although none of the Motif widgets use floating point resources, UIL provides support for floating point values. Floating point values must contain a decimal point so that the UIL compiler can distinguish them from integers. The following code fragment shows avalue section that defines several floating point variables:

   value     pi : 3.14159;     Avogadro: exported 6.023e23;     slope : -3.3337;     millisecond: 1e-3;

Floating point values can be defined both with and without exponents. A floating point value defined in UIL is stored as a Cdouble. Although you probably won't use floats very often, some potential uses include setting resources of user-defined widgets, exporting them back to the application, and passing them as callback arguments. Even though UIL is a static description language, you can use numeric expressions that are very similar to C expressions. Expressions in UIL are evaluated at compile-time, not at run-time. UIL supports the standard operators for use with integer, floating point, and boolean values. summarizes these operators and their precedence order. As with C, you can add parentheses to change the order of evaluation.

lp9 | lp9 | lp9 | lp9 | lp9 lp9 | lp9 | lp9 | lp9 | lp9. Operator Type Operand Types Operation Precedence
_
~ unary boolean NOT 1 (highest) integer One's complement 1- unary integer Negation 1 float Negation 1+ unary integer None 1 float None 1* binary integer Multiplication 2 float Multiplication 2/ binary integer Division 2 float Division 2+ binary integer Addition 3 float Addition 3- binary integer Subtraction 3 float Subtraction 3>> binary integer Shift right 4<< binary integer Shift left 4& binary boolean AND 5 integer Bitwise AND 5| binary boolean OR 6 integer Bitwise OR 6^ binary boolean XOR 6 integer Bitwise XOR 6 (lowest)
_

You can use a numeric expression just about anywhere that a numeric value is expected. In early releases of Motif 1.2, if you use an expression in anrgb definition, the result is always zero. However, the UIL compiler does place some restrictions on expressions. An expression must evaluate to a known value when you compile a module, which means that you cannot use imported numeric values in an expression since the unknown value prevents the compiler from evaluating the expression.

Like C, UIL lets you mix values of different types in an expression. In this situation, the result of the expression is the type of the most complex type in the expression. The order of complexity, from lowest to highest, isboolean,integer, andfloat. For example, the result of the expression2 * 2.71828 is thefloat value5.43656, and the result of the expression15&true is theinteger value1.

You can explicitly cast any numeric value or numeric expression to a specific type. UIL allows casts tointeger,float, andsingle_float values, but not toboolean values. The UILfloat type is a Cdouble, while the UILsingle_float type is a Cfloat. Here are several examples of casting:

   value     one     : integer (true);     zero    : integer (false);     result  : integer (2 * 2.71828);     five_oh : float (5);     g       : single_float (9.8);     round   : float (integer (2.71828 + 0.5));
When you cast afloat value to aninteger, the fractional part is always truncated, so the value ofresult is5. A cast tofloat simply converts aninteger or aboolean into a Cdouble. A cast to asingle_float is the only way you can define a Cfloat value, since a floating point literal is always stored as a Cdouble. You must use asingle_float to set a user-defined resource that is a Cfloat. In addition to individual integer values, UIL supports integer arrays. The compiler does not currently support boolean or floating point arrays, however. The following code fragment illustrates an array definition:
   value     primes : exported integer_table (2, 3, 5, 7, 11, 13);
An integer array consists of the keywordinteger_table followed by a list of integer values. Like most other UIL values, you can export integer arrays from a UIL module or pass them as callback arguments. UIL does not provide a way to indicate the end of an integer array, so an application must know the length or obtain it somehow. You can define integer arrays asexported values and fetch them from your application or use them to set the Text and TextFieldXmNselectionArray resource. Unfortunately, setting this resource does not work in early releases of Motif 1.2 because the possible values for the array elements are not defined. Even if you define the values yourself, based on the definitions in <Xm/Xm.h>, an incompatibility between the two widgets and Mrm prevents anXmN­selectionArray setting from working properly. This problem has been fixed as of Motif Release 1.2.3.

25.3.4 Text-related Values

Text is almost always an important part of a graphical user interface. UIL supports string, character set, and font values, all of which are related to the display of text in your interface. A string consists of displayable text. A string only makes sense in the context of a character set, which defines the supported characters in a string and the encoding (or mapping from values to glyphs) of the string. A font contains the actual glyphs that visually represent a character on the screen or on paper. These three elements are closely related as all are necessary to display text. the figure illustrates the relationships among these types under UIL.

figs.eps/V6a.24.06.eps.png
Relationships among strings, character sets, and fonts in UIL


This figure may look complicated, but UIL and Motif provide default values for character sets and fonts. You don't have to worry about these values unless you are customizing or internationalizing an application. Of course, you must always provide the strings, but that's the easy part. Before we can explain strings or fonts, you need to understand character sets, because both strings and fonts depend on them. The character set of a string determines the string's parsing direction, writing direction, and the number of bytes per character. For example, character sets for Latin-based languages like English are read from left to right, are written from left to right, and are typically encoded using one byte per character.

When a string is displayed, it must be drawn with a font that uses the same character set as the string because a character set defines a mapping from character codes to character glyphs. For example, in the ISO 8859-1 character set (ISO Latin-1), the value 65 represent anA, the value 66 represents aB, etc. In a font for ISO 8859-1, the symbolA occupies position 65,B occupies position 66, and so on. If the character set of a string doesn't match the character set of the font with which it is drawn, there's a good chance that the rendered text will be gibberish.

UIL provides a number of built-in character sets that should meet the needs of most applications. lists the built-in UIL character sets and specifies the UIL name, the official name, and the attributes of each. lp9 | lp9 | lp9 | lp9 | lp9 lp9 | lp9 | lp9 | lp9 | lp9. UIL Name Character Set Parse Direction Writing Direction 16 Bit
_
iso_latin1 ISO8859-1 L to R L to R Noiso_latin2 ISO8859-2 L to R L to R Noiso_latin3 ISO8859-3 L to R L to R Noiso_latin4 ISO8859-4 L to R L to R Noiso_latin5 ISO8859-5 L to R L to R Noiso_cyrillic ISO8859-5 L to R L to R Noiso_arabic ISO8859-6 L to R L to R Noiso_greek ISO8859-7 L to R L to R Noiso_latin8 ISO8859-8 R to L R to L Noiso_latin8_lr ISO8859-8 L to R R to L Noiso_hebrew ISO8859-8 R to L R to L Noiso_hebrew_lr ISO8859-8 L to R R to L Nogb_hanzi GB2313.1980-0 L to R L to R Yesgb_hanzi_gr GB2313.1980-1 L to R L to R Yesjis_kanji JISX0208.1983-0 L to R L to R Yesjis_kanji_gr JISX0208.1983-1 L to R L to R Yesjis_katakana JISX0201.1976-0 L to R L to R Noksc_hangul KSC5601.1987-0 L to R L to R Yesksc_hangul_gr KSC5601.1987-1 L to R L to R Yes _ If you need to use a character set that is not built into UIL, you can define your own character set. UIL allows user-defined character sets anywhere a built-in is expected, except in thecharacter_set option at the beginning of a module. The specification of a user-defined character set takes the following form:

   character_set ('string_expression'                  [, right_to_left =boolean_expression]                  [, sixteen_bit =boolean_expression] )
The string_expression that is used to name a user-defined character set is the key that links a string to a font, as you'll see shortly. The name is followed by two optional character set properties that only affect string values. When theright_to_left property is set tofalse, strings that use the character set are parsed and written from left to right. When the property is set totrue, strings are parsed and written from right to left. When thesixteen_bit property is set tofalse, each character in a string that uses the character set is one byte long, but when it is set totrue, each character is two bytes long. Since both properties default tofalse, you do not need to specify them in most cases. Here are a few specifications of user-defined character sets:
   character_set ('bold');   character_set ('italic');   character_set ('hieroglyphic', sixteen_bit = true);   character_set ('xnaye, right_to_left = true);
UIL does not allow the definition of character set variables. You can only specify a character set by using thecharacter_set option in the module header or by explicitly specifying the character set of a string. We describe how to specify the character set for a string in the next section. While a character set traditionally represents the characters of a language, you can also represent different font styles with user-defined character sets. UIL supports several different types of strings so that it can represent the various string values used for Motif widget resources. Theasciz_string_table type is the only type that is not associated with a widget resource. lists all of the UIL string types and their corresponding C types. lp9 | lp9 lp9 | lp9. UIL Type Name C/Xt/Motif Type Name
_
stringchar *,Stringcompound_stringXmStringwide_characterchar_t *asciz_string_tablechar **,String *asciz_tablechar **,String *compound_string_tableXmString *,XmStringTablestring_tableXmString *,XmStringTable
_ The basic representation of all strings in UIL is a sequence of zero or more characters within single or double quotes. In Motif 1.1, quoted strings are limited to 2000 characters, but later releases allow greater lengths. The exact type of a string can be determined implicitly by the context in which it appears or explicitly when it is used in a named-string definition. All of the string types exceptstring have an explicit form.

Both single and double-quoted strings can contain any of the printable single-byte characters. These are the characters with decimal values in the ranges 32 to 126 and 160 to 255. Characters with values outside of the ranges can only be entered using the\value\ escape sequence, where value represents the character code desired. In addition, you must escape a single quote (') in a single-quoted string and a double quote (") in a double-quoted string. To allow the easy specification of commonly used nonprinting characters, UIL recognizes the escape sequences shown in l | l c | l.
Escape Sequence Meaning
_
\b Backspace\f Formfeed\n Newline\r Carriage return\t Horizontal tab\v Vertical tab\\ Backslash\' Single quote\" Double quote
_ The following code fragment shows some examples of quoted string variable definitions that include escape sequences:

   value     bell : 'Beep\7\';     quote : "\"You don't believe me?\" asked the lawyer.";
The first string includes some normal text and an escaped control character, decimal 7, which is the bell character on most terminals. The second string contains a couple of double quotes that must be escaped because the string itself is double-quoted. Alternatively, we could have made it a single-quoted string, thereby eliminating the need for escaping the double quotes within it. In general, non-printable escape characters only make sense in the context ofNULL-terminated strings and may produce strange results if you use them within compound strings (which we'll discuss shortly).

You can continue a single-quoted string over multiple lines by adding the backslash character as the last character on a continued line. The string continues with the first character on the following line and does not include a newline. If you want a newline in a string, you must use the\n escape sequence. Double-quoted strings cannot span multiple lines. The following definition shows an example of a multi-line single-quoted string:

   value     sentence : 'TRUE! -- NERVOUS -- VERY, very dreadfully nervous \   I had been and am; but why will you say that I am mad?';
UILNULL-terminated strings are the same as C strings. While most Motif text resources areXmString values, there are a few strings that areNULL-terminated. The most common is theXmNvalue resource of the Text and TextField widgets. You also useNULL-terminated strings in the literal syntax of many UIL variable definitions, and you can use aNULL-terminated string as the argument to a callback. The following fragment demonstrates the use ofNULL-terminated strings:
   procedure     verify (string);   object phone : XmTextField {     arguments {       XmNvalue = '(512) 555-1212';       XmNbackground = color ('wheat');     };     callbacks {       XmNmodifyVerifyCallback = procedure verify ('(###) ###-####');     };   };
In this widget definition, we assign aNULL-terminated string to theXmNvalue resource, we use one in the definition of a UILcolor value, and we pass one as a callback argument. The callback is declared as taking astring value, which is the UIL type forNULL-terminated strings. We recommend using the convention of writingNULL-terminated strings as single-quoted strings. This distinguishes them from compound strings, which we recommend writing as double-quoted strings.

You can concatenate two or moreNULL-terminated strings with the ampersand (&), which is the UIL string concatenation operator. It is a binary operator that creates a new string consisting of the left operand followed by the right operand. You can use this operand withNULL-terminated strings that are used for resource settings, callback arguments, and variable definitions. However, using string concatenation in the literal syntax of a UIL value definition may crash the UIL compiler or result in an incorrect definition. The following fragment shows an example of string concatenation:

   value     first : 'Bilbo';     last  : 'Baggins';     full  : first & ' ' & last;
Thefull variable is defined as the concatenation of the variablesfirst andlast, separated by a space. The resulting string is'Bilbo Baggins'. You can use both variables andNULL-terminated string literals as the operands for string concatenation. Most text values in the Motif widget set are handled asXmString values, or compound strings. Compound strings differ fromNULL-terminated strings in that they contain information about the character set and writing direction of the string along with the textual information. This additional information is necessary for displaying text in different languages and fonts. Essentially, a compound string is a string that comes with all of the information that is needed to render it. In most situations, you can simply specify the text, and the UIL compiler provides the character set, as in the following familiar example:
   object hello : XmLabel   {     arguments {       XmNlabelString = "Hello, World!";     };   };
XmNlabelString is anXmString resource, but in this definition we only specify the text portion of the compound string. This specification works because there is a default character set associated with every UIL module. As we explained in Chapter 22, Introduction to UIL, you can specify the default character set by setting the LANG environment variable or by setting thecharacter_set option at the beginning of the module. If you do not specify the default character set, the UIL compiler uses a built-in default which is vendor specific. In any event, you can use a single or double-quoted string wherever a compound string is expected, and the UIL compiler will automatically convert it to a compound string. the figure illustrates how the UIL compiler determines the character set for compound strings.

figs.eps/V6a.24.07.eps.png
Character set determination for compound strings

The character set of an individual string can also be specified explicitly. You do so by preceding a string with the pound sign (#) and specifying the name of a built-in or user-defined character set. This syntax only works with double-quoted strings, however, which is why we recommend using double-quoted strings to represent compound strings. In early releases of Motif 1.2, the UIL compiler does not generate an error if you specify a character set for a single-quoted string. The compiler silently ignores the specification, so you should be careful to always use double-quoted strings when specifying a character set. The following code fragment demonstrates how to set the character set of a string explicitly:

   object hello : XmLabel {     arguments {       XmNlabelString = #iso_greek"[[chi]][[alpha]][[iota]][[rho]][[epsilon]]";     };   };
In this example, we explicitly set the character set toiso_greek, which is one of the built-in UIL character sets. At run-time, the string is displayed in Greek as long as the font list of the Label is set correctly. (We explain font lists later in this section.) It is rare for an application to specify a character set explicitly, as most applications only display text using one language for a given invocation, although the language may vary between invocations.

You can also specify different font styles using character sets, although that is not their primary purpose. You can define your own character set to represent a different style, as shown in the following fragment:

   object title : XmLabel {     arguments {       XmNlabelString = #character_set('italic')"Elsinore";     };   };
TheXmNlabelString resource is set to a compound string that contains the text "Elsinore" and uses the character set nameditalic. Displaying the string in italics requires that the font list of the Label contain anitalic character set.

Unlike other UIL values, you cannot define a character set variable, which means that you must always specify a user-defined character set explicitly, as shown in this example. Specifying font styles with character sets is most useful when you want to display a compound string that contains text in several different styles, as we'll show you in an example later in this section.

Although automatic string conversion can handle the creation of most compound strings, there are still a few situations when you need to define compound strings explicitly. If you want to declare an exported compound string variable or override one of the properties of a compound string, you need to use the compound string literal syntax. An explicit compound string definition takes the following form:

   compound_string (string_expression,                    [, right_to_left =boolean_expression ]                    [, separate =boolean_expression ] )
Acompound_string literal begins with thecompound_string keyword and is followed by a single or double-quoted string and the optional properties. You can set the writing direction of the compound string with theright_to_left property; the default value of this property is taken from the writing direction string's character set. Theseparate property specifies whether or not a separator component is added to the end of the compound string. The default value isfalse, which means that a separator is not added.

Unlike withNULL-terminated strings, placing a newline character in a compound string does not produce a multi-line string. A line break in a compound string is indicated by a separator component, which you add by setting theseparate property totrue in an explicit compound string definition. You can create a multi-line compound string by concatenating compound strings with the& operator, as shown in the source code

   module multiline   value     file  : compound_string ("/vmunix", separate=true);     owner : compound_string ("root", separate=true);     desc  : compound_string ("The UNIX kernel.");     all   : "File: " & file & "Owner: " & owner & "Desc: " & desc;   object root : XmLabel {     arguments {       XmNlabelString = all;     };   };   end module;
Bothfile andowner are defined as compound string values that contain a compound string separator. The concatenation of the strings in this example produces a three-line compound string, which is shown in the figure.

figs.eps/V6a.24.08.eps.png
User interface of multiline.uil

As the source code shows, you can mixNULL-terminated strings and compound strings with the& concatenation operator. When you concatenate two strings, the result is a compound string if either one of the strings is a compound string, or if the character sets of the two strings are different. Thewide_character string type was added in Motif 1.2 to support the definition of user interfaces that contain Asian language text. Unfortunately, the UIL compiler flags a wide-character definition as an error in early releases of Motif 1.2. The form of a wide-character definition is:

   wide_character (string_expression)
The string_expression contains a multibyte string. Asian language text must be represented with multibyte or wide-character strings because the number of different characters in these languages cannot be encoded in single bytes. In a multibyte character string, the length in bytes of each individual character varies, but in a wide-character string, the length of each character is the same. Most programs, including the Motif widgets, work with wide-character strings internally because the fixed character size makes them easier to use than multibyte characters.

Thewide_character type converts a multibyte character string into an equivalent wide-character string. The conversion is based on the locale that is set when you run the UIL compiler. When compiling a module that contains wide-character strings, you must use the-s compiler option or multibyte string conversions may be incorrect. See Section #suilcomps for more information about this option.

The only wide-character resource in the Motif widget set is theXmNvalueWcs resource of the Text and TextField widgets. In addition to setting this resource, you can also fetch exported wide-character strings from an application program and use them as callback arguments. In addition to single,NULL-terminated strings and compound strings, UIL supports arrays of both types. TheXmNitems andXmNselectedItems resources of the List widget are bothXmStringTable values, or compound string arrays. Even though there are noNULL-terminated string array resources in the Motif widget set, you can still pass these arrays as callback arguments and fetch exported arrays withMrmFetchLiteral(), just as you can with compound string arrays. The form of each type of array is similar, as shown in the following fragment:

   value     seasons : asciz_string_table ('winter', 'spring', 'summer', 'autumn');     fruits : compound_string_table ("apple", "banana", "grape", "cherry");
You can also use the keywordsasciz_table andstring_table when definingNULL-terminated and compound string tables, respectively. The UIL compiler terminates both types of arrays with aNULL pointer. Quoted strings in thecompound_string_table are converted automatically to compound strings by the UIL compiler. However, unlike with individual string values, the UIL compiler does not convert anasciz_string_table to acompound_string_table. Remember that when you set a MotifXmStringTable resource, the UIL compiler sets the associated count resource automatically. Fonts are the last piece of the textual information picture that we need to examine. As we explained earlier, you cannot display a compound string without an associated font; a character set links a string to a particular font. You specify the fonts used by Motif widgets with font list resources. The simplest case involves setting theXmNfontList resource of a widget to a single font. A font list can also specify a list of fonts or font sets and their associated character sets. For more information on Motif font lists, see Chapter 19, Compound Strings.

UIL provides support for font, font set, and font list values. These types correspond to theXFontStruct,XFontSet, andXmFontList types in C. The UIL font set type was added in Motif 1.2 to support theXFontSet type that was added in X11R5. A font list can contain both fonts and font sets, so we'll look at these two types first. The following code fragment shows a value of each type:

   value     menu_font : font       ('-adobe-times-bold-r-normal--12-120-75-75-p-67-iso8859-1');     label_font : fontset ('-*-fixed-medium-r-normal-*-*-130-*');
As these definitions illustrate, fonts and font sets are defined using X Logical Font Description (XLFD) names and patterns. Xlib may load one or more fonts in a font set, which is why you must always specify a pattern instead of a single font name. Xlib determines the exact fonts that are needed based on the locale setting. For example, drawing Japanese text typically requires a Kanji font, a Kana font, and a Latin font. For additional information about fonts and font sets, see Volume One, Xlib Programming Manual, and Volume Two, Xlib Reference Manual.

Fonts and font sets are loaded at run-time because they are resources maintained by the X server. UIL simply stores the font names or patterns that you specify in the UID output file without checking to see if the fonts exist. Thefont andfontset types are typically used to set a Motif font list resource. Mrm also creates aXFontStruct orXFontSet value for you when you pass afont orfontset value as a callback argument or when you fetch one of the values from an application program.

Each Motif widget that displays text has aXmFontList resource associated with it. UIL provides thefont_table type so you can define font lists directly in UIL. A font list is simply an array of font and/or font set values, each of which has an associated character set. The following fragment illustrates the definition of a font list:

   value     latin1 : font ('*-iso8859-1', character_set = iso_latin1);     hebrew : font ('*-iso8859-8')     fonts : font_table (latin1, iso_hebrew = font ('*-iso8859-8'));
You define a font list using thefont_table keyword followed by a list of fonts. This example demonstrates the two ways of associating a character set with a font or font set. You can specify the character set in the font or font set definition by adding acharacter_set property setting, or you can specify the character set directly in thefont_table definition. The character set specified in a font table definition takes precedence over a character set specified in a font or font set definition.

If you do not specify a character set for a font or a font set, it defaults to the codeset portion of the LANG environment variable if it is set, or toXmFALLBACK_CHARSET otherwise. Unlike with string definitions, the default character set of the module has no effect on the character set used for font and font set definitions. If a font list contains only a single font or font set, you can set theXmNfontList resource to the font or font set directly, and the UIL compiler creates a font list that contains the entry automatically. Motif obtains the font or font set needed to render a compound string by matching the character set of the string with a font or font set in a font list that has the same character set. Now we can take a look at an example that uses strings, character sets, fonts, and font lists. The module in the source code shows how these values work together. In early releases of Motif 1.2, the user-defined character set in this module may cause a compilation error or it may crash the UIL compiler.

   /* joel.uil - Example of strings, character sets, and fonts, and font sets. */   module joel   value     artist : #iso_latin1 "Billy Joel";     title  : #iso_cyrillic "186\222\221\230\213\224\226     album  : #character_set('latin1-bold') "Album";   value     normal  : font ('*fixed-medium-r-normal-*-*-140-*-iso8859-1');     bold    : font ('*fixed-medium-r-bold-*-*-140-*-iso8859-1');     russian : font ('*fixed-medium-r-normal-*-*-140-*-iso8859-5');   value     styles : font_table (iso_latin1 = normal,                          iso_cyrillic = russian,                          character_set('latin1-bold') = bold);   object root : XmLabel {     arguments {       XmNlabelString =  album & " : " & artist & " - " & title;       XmNfontList = styles;     };   };   end module;
The module begins with the definition of three strings, each with a different character set. Two of the character sets are built-in and one is user-defined. The built-in ones represent two different languages, while the user-defined character set represents both a language and a font style. The characters in the second string are shown in their decimal form, as we are unable to print the corresponding characters in this book. You could enter the actual characters directly with a Cyrillic editor, as they are not control characters. We've specified the character sets explicitly because we are using more than one language and don't want to worry about the setting of the LANG environment variable.

The font definitions for the text come next. We define three fonts, one for each string. Each font is defined using an XLFD font name. We combine the fonts in thestyles font list definition, which is where we establish the connection between the character sets used by the strings and the fonts. The character set names in the compound string definitions must match the character set names used in thefont_table. Finally, we define a Label that displays the concatenated string. The output of this module is shown in the figure.

figs.eps/V6a.24.09.eps.png
User interface of joel.uil

In early releases of Motif 1.2, the user-defined character set in the font list definition may cause a compilation error, or it may cause the UIL compiler to crash. You can work around this problem by specifying the font list in an X resource file. In this case, you must specify the character set names of the built-in character sets using the names shown in the second column of The proper resource specification for this module is:

   Demos*XmLabel.fontList:   *fixed-medium-r-normal-*-*-140-*-iso8859-1=ISO8859-1,   *fixed-medium-r-normal-*-*-140-*-iso8859-5=ISO8859-5,   *fixed-bold-r-normal-*-*-140-*-iso8859-1=latin1-bold
The name of the user-defined character set is the same as the name we used in the module. In general, placing font list definitions in an app-defaults file is a good idea anyway, since it lets the users of an application customize the font settings.

25.3.5 Colors

The UIL compiler supports color values, which means that you can set all of the different Motif color resources in a UIL module. In addition, UIL color values play an important role in the specification of color pixmaps in UIL. Color values in UIL can be specified by the name of the color or by the amount of red, green, and blue (RGB) that compose the color. Both types of color values are easy to define, as shown in the following fragment:

   object button : XmPushButton {     arguments {       XmNbackground = color ('wheat');

XmNforeground = rgb (500, 0, 65535); }; }; A named color value is specified with the keywordcolor followed by a color name. Mrm converts the color name to the corresponding RGB value at run-time using Xlib. On most UNIX systems, Xlib converts colors from names to values using the mappings defined in the file /usr/lib/X11/rgb.txt. You can find more details on this process in Volume Two, Xlib Reference Manual. You specify RGB values with the keywordrgb and the amount of red, green, and blue present in the color. Each amount can range from 0 to 65535, which represents 0 to 100 percent of a color.

Like other values, you can assign bothcolor andrgb values to UIL variables, pass them as arguments to callback functions, and use them to specify resources. If Mrm cannot allocate a color at run-time for setting a resource, the resource is simply not set, and Mrm prints a warning message by callingXtAppWarning(). If Mrm cannot allocate a color that you use as a callback argument, your application may crash when the callback is invoked. As a result, you should avoid passing color arguments from UIL and allocate or fetch colors directly in application code instead.

For the most part, we recommend that you avoid setting color resources in a UIL module because users cannot override UIL resource settings using a resource file. Color is one of the most frequently customized aspects of an application, so you should not hard-code color values. However, colors do have their place in UIL. They are still useful for defining color pixmaps, where you don't have to worry about allowing users to change the colors. Color values are one of the types that cannot be fetched withMrmFetchLiteral() because Mrm requires a colormap in which to allocate the color. TheMrmFetchColorLiteral() function exists to allow the retrieval of color values. This function takes the following form:

   Cardinal   MrmFetchColorLiteral(hierarchy,name,display,colormap,pixel_return)       MrmHierarchyhierarchy;       Stringname;       Display       *display;       Colormapcolormap;       Pixel         *pixel_return;
As withMrmFetchLiteral(), thehierarchy andname arguments specify the Mrm hierarchy to search and the exported color variable to fetch. Mrm allocates the color in the colormap specified by thecolormap parameter. If this argument isNULL, Mrm allocates the color in the colormap returned by theDefaultColormap() macro.

When Mrm successfully fetches the color, thepixel_return argument contains the allocated color, and the function returnsMrmSUCCESS. If Mrm cannot find a variable by the specified name, the routine returnsMrmNOT_FOUND. If Mrm finds a variable with the specified name, but it isn't a color value, the function returnsMrmWRONG_TYPE. The routine can also returnMrmBAD_HIERARCHY if thehierarchy argument is invalid orMrmFAILURE if anything else goes wrong. As usual, you should check the return value againstMrmSUCCESS before testing for a specific failure. IfMrmFetchColorLiteral() fails to allocate a color, Mrm should substitute black or white and print a warning message by callingXtAppWarning(). However, in early releases of Motif 1.2 this fallback mechanism does not take place, and the function returnsMrmNOT_FOUND instead. When you are finished using a color retrieved with this function, you must free it with a call toXFreeColors().

25.3.6 Pixmaps

The UIL compiler supports pixmap values so that the various pixmap resources can be set in a UIL module. These resources include icon-type resources such asXmNsymbolPixmap and shading-type resources such asXmNbackgroundPixmap. There are two different forms of pixmap values that you can use in a UIL module. The first type is anxbitmapfile, which is a reference to a bitmap defined in a separate file. For details on the X bitmap file format, see Volume One, Xlib Programming Manual. The second type is anicon, which is defined entirely within a UIL module. Thexbitmapfile type is used to specify a bitmap file. The contents of the file are used to create the actual bitmap. The module in the source code shows the use of this type.

   /* bomb.uil -- Example using xbitmapfile type */   module bomb   procedure quit;   object root : XmMessageDialog {     arguments {       XmNmessageString = compound_string ("Segmentation Fault", separate=true) &                          compound_string ("(Dumping Core)");       XmNsymbolPixmap = xbitmapfile ('bomb.xbm');       XmNdialogTitle = "Fatal Error";     };   };   end module;
This example creates a MessageDialog that uses a customized icon instead of one of the standard Motif symbols. The output of the module is shown in the figure.

figs.eps/V6a.24.10.eps.png
User interface of bomb.uil

Thexbitmapfile value is a bitmap whose contents are defined in the file bomb.xbm. X bitmaps are a convenient format since they can be edited and created with the standard bitmap client. Bitmaps are monochrome images, which means that each pixel is either on or off. When you use a bitmap in a Motif widget, the "on" pixels are set to the foreground color of the widget, and the "off" pixels are set to the background color. You can only adjust the colors of a bitmap by changing the foreground and background color of a widget.

When you compile a module that contains anxbitmapfile value, the UIL compiler does not verify the contents or existence of the file. The file name is saved in the UID file, and Mrm handles loading the bitmap at run-time by callingXmGetPixmap(). (For details on this routine, see Section #spixmaps in Chapter 3, Overview of the Motif Toolkit.) If Mrm cannot find or load a bitmap file, it prints a warning message by callingXtAppWarning(). If the bitmap file is used as a resource setting, the resource is not set. When anxbitmapfile is used as a callback argument in early releases of Motif 1.2, the application crashes when the callback is invoked. We recommend that you only usexbitmapfile values for resource settings. You can also represent pixmaps with the UILicon type, which supports full color images. You defineicon values entirely within a UIL module, instead of referencing an external file. The UILicon type is a useful feature, as neither Motif, Xt, or Xlib provides any support for defining color pixmaps. The only drawback is that you may have to edit theicon manually using a text editor. Several third-party vendors sell color pixmap editors that can save images using the UIL icon format; many of these editors are part of a user interface builder (UIB) tool. The Hello, World example in Chapter 22, Introduction to UIL, used anicon value to create the earth image. We've taken the image from that example and colorized it to illustrate the complete syntax of anicon, as shown in the source code

   /* globe.uil --  colorize the world icon */   module globe   value     world_colors : color_table (background color = ' ',                    color ('black') = '*',                    color ('blue') = '.',                    color ('green') = 'x',                    color ('white') = '=');     world_icon : icon (color_table = world_colors,       '     ******     ',       '   **.===..**   ',       '  *xx.==..x..*  ',       ' *xxx....xxx..* ',       ' *.xxxxxxxxx.x* ',       '*.xxxxxx.xxx.xx*',       '*.xxxxxxxxx...x*',       '*.xxxxxxxxx...x*',       '*..xxxxxxxx...x*',       '*...xxxx..x....*',       '*....xx.....x..*',       ' *....xx......* ',       ' *....xxxxx...* ',       '  *..xxxxxxx.*  ',       '   **xxxxxx**   ',       '     ******     '  );   object root : XmLabel {     arguments {       XmNlabelType = XmPIXMAP;       XmNlabelPixmap = world_icon;       XmNmarginWidth = 10;       XmNmarginHeight = 10;     };   };   end module;
Anicon definition specifies a UILcolor_table, which maps characters to color values, and a number of strings, where each character represents an individual pixel in the resulting pixmap. The output of this module is shown in the figure. Obviously, the output is not in color, but you can tell from the different degrees of shading that the pixels are different colors.

figs.eps/V6a.24.11.eps.png
User interface of globe.uil


Althoughcolor_table is a distinct UIL type, acolor_table value is only useful in the context of anicon definition. Acolor_table value consists of the keywordcolor_table followed by a parenthesized list of color mappings. The form of each mapping is acolor orrgb value followed by an equal sign and a single character, which is used to represent that color in an icon definition. You can also use the special colorsforeground color andbackground color. These colors are taken from the widget in which anicon appears. We use thebackground color around the earth so that it blends in smoothly with the Label.

When you use acolor value in acolor_table, you can specify how the color appears on a monochrome display, or when Mrm cannot allocate color, you can specify eitherforeground orbackground after the color name. For example, we can ensure that our earth icon looks reasonable on a monochrome display by using the followingcolor_table definition:

   world_colors : color_table (background color = ' ',                  color ('black', foreground) = '*',                  color ('blue', background) = '.',                  color ('green', foreground) = 'x',                  color ('white', foreground) = '=');
Without these attributes, each color is mapped to white on a monochrome display. We recommend specifying theforeground andbackground attributes in acolor_table, as they ensure that an icon looks reasonable. These attributes only affect colors that are allocated as part of acolor_table; if you specify the attribute in a color that is used as a resource or a callback argument, the UIL compiler quietly ignores the attribute.

The UIL compiler does not support theforeground andbackground attributes withrgb values. However, unlike acolor value, anrgb value maps predictably to black or white based on its intensity. The mapping of a monochromecolor value depends on the X server's color database, which varies from server to server. If you must usergb values in acolor_table, you should usergb values for all of the mappings and be sure to view the resulting icon on a monochrome screen. Avoid mixingcolor andrgb values in the samecolor_table, since the mapping of acolor value depends on the foreground and background colors of a widget, while the mapping of anrgb value is always the same.

Anicon definition consists of the keywordicon followed by an optionalcolor_table setting and a list of equal-length strings that define the rows of the pixmap. If you leave out thecolor_table setting, as we did in the originalworld_icon definition, the following defaultcolor_table is used:

   color_table (background color = ' ',                foreground color = '*');
If you specify acolor_table, it must be the first entry in theicon definition, as in the source code The pixmap definition consists of an arbitrary number of comma-separated strings that correspond to the rows of the pixmap. Each pixel in the pixmap is defined using one of the characters from thecolor_table. Any other characters are illegal and are flagged as such by the UIL compiler. The compiler also verifies that all of the strings in anicon definition are the same length. Pixmap values are another one of the types that cannot be fetched withMrmFetchLiteral() because Mrm needs aScreen pointer, as well as background and foreground colors, in order to create a pixmap. Mrm provides two specialized functions for fetching pixmap values:MrmFetchBitmapLiteral() andMrmFetchIconLiteral().MrmFetchBitmapLiteral() is new in Motif 1.2; it takes the following form:
   Cardinal   MrmFetchBitmapLiteral(hierarchy,name,screen,display,pixmap_return,width_return,height_return)       MrmHierarchyhierarchy;       Stringname;       Screen        *screen;       Display       *display;       Pixmap        *pixmap_return;       Dimension     *width_return;       Dimension     *height_return;
This routine fetches anicon value in the form of aBitmap, which is aPixmap with a depth of 1. Thehierarchy argument specifies the Mrm hierarchy that contains the exportedicon value specified by thename argument. If Mrm finds theicon, it creates the bitmap on thescreen of thedisplay and returns it inpixmap_return. The width and height of the pixmap are returned inwidth_return andheight_return. A return value ofMrmSUCCESS indicates that the pixmap has been fetched and created successfully. In this case, the application is responsible for freeing the pixmap withXFreePixmap().

Theicon that is specified can only use the colorsforegroundcolor andbackgroundcolor. These colors represent the values of1 and0, respectively, in the resulting bitmap. If you use any other colors, the function fails and returnsMrmNOT_VALID. The function can also returnMrmBAD_HIERARCHY if thehierarchy argument is not a valid Mrm hierarchy,MrmNOT_FOUND if Mrm cannot find theicon in the hierarchy,MrmWRONG_TYPE if Mrm finds a value but it is not anicon, orMrmFAILURE if anything else goes wrong.

You can use the bitmap returned byMrmFetchBitmapLiteral() anywhere that a bitmap or a bit mask is needed. Common uses include setting the window manager icon of an application or defining a cursor by callingXCreatePixmapCursor().

You can also fetch pixmaps withMrmFetchIconLiteral(). This function can retrieve bothicon andxbitmapfile values. The routine returns aPixmap whose depth is the default depth of the screen as returned by theDefaultDepthOfScreen() macro. It takes the following form:

   Cardinal   MrmFetchIconLiteral(hierarchy,name,screen,display,foreground,background,pixmap_return);       MrmHierarchyhierarchy;       Stringname;       Screen        *screen;       Display       *display;       Pixelforeground;       Pixelbackground;       Pixmap        *pixmap_return;

The first four arguments are the same as forMrmFetchBitmapLiteral(). Theforeground andbackground arguments specify the colors of the pixmap. When you fetch anxbitmapfile, "on" pixels are set to the foreground color and "off" pixels are set to the background color. When you fetch a UILicon, it specifies the colors forforegroundcolor andbackgroundcolor pixels. Mrm allocates the other colors that are used in the default colormap of thedisplay. For this reason, you should not use this function to fetch icons for a visual class other than the default.

On success,MrmFetchIconLiteral() fills inpixmap_return with the pixmap and returnsMrmSUCCESS. An application is responsible for freeing the pixmap usingXFreePixmap(). The routine can also returnMrmBAD_HIERARCHY if the specifiedhierarchy is not valid,MrmNOT_FOUND if Mrm cannot find theicon orxbitmapfile in the hierarchy,MrmWRONG_TYPE if Mrm finds a value but it is not anicon orxbitmapfile, orMrmFAILURE if anything else goes wrong.

WhenMrmFetchIconLiteral() cannot allocate a color for anicon, it should substitute either black or white. However, in early releases of Motif 1.2 this substitution does not take place, and the function returnsMrmNOT_FOUND when a color allocation fails. To avoid this problem, you should set pixmap-type widget resources, such asXmNlabelPixmap, in a UIL module widget definition or by callingMrmFetchSetValues().

25.3.7 Widget Classes

The widget class type, orclass_rec_name as it is called in UIL, is new in Motif 1.2. (This feature may have been present in earlier versions, but was not documented until Motif 1.2) The type mainly supports theXmNentryClass resource, which restricts the allowable children of a RowColumn widget. You can also use a widget class value as a callback argument or with a user-defined widget. TheXmNentryClass resource is usually set automatically by RowColumn when you create a MenuBar or RadioBox. You can also set the resource manually, as the following fragment illustrates:

   object root_widget : XmRowColumn {     arguments {       ! Must set isHomogeneous for entryClass to take effect.       XmNisHomogeneous = true;       XmNentryClass = class_rec_name ('XmLabel');     };   };
You specify a widget class value with the keywordclass_rec_name followed by the name of the widget class. You can use the name of an actual widget class, such as XmPushButtonGadget, or the name of a compound object, such as XmPulldownMenu. When you use a name that is not really a widget class, theclass_rec_name value represents the name of the actual class. For example, the real class of an XmPulldownMenu object is XmRowColumn.

25.3.8 Keysyms

In UIL, you define key mnemonics with thekeysym type. TheXmNmnemonic resource is the onlykeysym resource in the Motif widget set. The following widget definition illustrates the use of this type:

   object open : XmPushButton {     arguments {       XmNlabelString = "Open...";       XmNmnemonic = keysym ('O');     };   };
Akeysym definition is specified with thekeysym keyword followed by a single character. In early releases of Motif 1.2, the UIL compiler does not report an error if you specify more than one character, but Mrm does catch the mistake at run-time.

25.3.9 Translation Tables

The UILtranslation_table type corresponds to theXtTranslations type. A translation table maps events to action procedures. The format of a UIL translation table looks like anasciz_table in that it contains a list of strings, but the individual entries in the table must contain valid translations. (See Volume Four, X Toolkit Intrinsics Programming Manual, for a description of the Xt translation table syntax.) The following fragment shows the definition of atranslation_table value:

   value     actions : translation_table ('#override',                                  'Ctrl<Key>A: beginning-of-line()',                                  'Ctrl<Key>E: end-of-line()',                                  'Ctrl<Key>space: set-anchor()');
The first entry of atranslation_table can be used to control how the table affects the existing translations of a widget. If the first entry is not a translation, the string must be one of#augment,#override, or#replace. Each of the remaining entries in the table must be aNULL-terminated string containing a single translation. If you specify a translation in a UIL module, a user cannot override it from a resource file. You should consider placing translations in an app-defaults file so that users can customize them if they wish.

25.4 Working With Callbacks

Setting up a callback with UIL and Mrm involves four steps: writing the callback in application code, registering the callback with Mrm, declaring the callback in the UIL module, and setting the callback in a UIL widget definition. A callback that you write for use in an Mrm application is no different from a callback in a plain Xt application. However, as we explained in Chapter 22, Introduction to UIL, you need to register the routine with Mrm before creating any widgets that call it. The following code fragment from showuid.c shows how to register callbacks:

   static MrmRegisterArg callback_list[] = {     { "quit",     (XtPointer) quit },     { "print",    (XtPointer) print },     /* Add additional callback procedures here... */   };   ...   MrmRegisterNames (callback_list, XtNumber (callback_list));   ...
You're already familiar with the basics of declaring a callback procedure in a UIL module and using it in thecallbacks subsection of a widget definition. Now we are going to look at how you can pass a UIL value as a callback argument. As you know, callbacks are declared in aprocedure section of a UIL module. The purpose of the declaration is to let the compiler know that the callback exists and to give it enough information to verify that the callback is being used correctly. A callback declaration consists of the name of the callback followed by an optional argument type enclosed in parentheses. The parentheses are optional as well, but because the compiler does not perform argument type-checking when this style of declaration is used, we recommend against using it. Here are the procedure declarations that correspond to the callback functions from showuid.c:
   procedure     quit();     print (string);
When no argument type is specified, as with thequit() declaration, the callback is declared as taking no arguments. When a UIL type name is present, as with theprint() declaration, you must specify a value that matches the type when you use the callback. You can use any of the built-in UIL types. In addition, you can also specify the name of a Motif widget class such asXmPushButton orXmForm in a callback declaration. Finally, you can indicate that an argument is expected, but not restrict its type, by specifying the special type-nameany. If you use theany specifier, you should take extra care to ensure that references to the procedure elsewhere in the UIL module do not pass values to the callback that might crash your application. This problem has been fixed as of Motif Release 1.2.3.

The UIL compiler makes sure that the use of a callback is consistent with its declaration. If you declare a callback as taking no arguments, you cannot pass an argument to the callback when you use it. Likewise, when a callback does take an argument, you must provide one when you use the routine, and the argument must match the type in the declaration. You can pass an argument whose type does not match the declaration if the UIL automatic type conversions described earlier provide for it. For example, you can pass astring to a callback that is declared as taking acompound_string. If the use of a callback does not agree with its declaration, the UIL compiler generates an error, and the module is not compiled successfully. The following code fragment shows how you might use theprint andquit callbacks:

   object close : XmPushButton {     callbacks {       XmNarmCallback = procedure print ("Armed!");       XmNactivateCallback = procedure quit();     };   };

When a callback specified in a UIL module is invoked at run-time, the argument, if any, is passed to the callback as theclient_data parameter. In this example, the string "Armed!" is passed to theprint() callback when the PushButton is armed. The callback simply prints the argument that is passed to it. This routine is shown in the following code fragment:

   void   print (w, client_data, call_data)   Widget    w;   XtPointer client_data;   XtPointer call_data;   {       char *message = (char *) client_data;       puts (message);   }
You should always cast theclient_data argument to the appropriate type before using it, as this fragment illustrates. The argument type depends on the UIL value passed to a callback. contains a complete listing of the UIL data types and the corresponding C types. In particular, you should note that theinteger,float, andboolean numeric types are passed as pointers to the values, not the values themselves.

25.5 Using Lists

A UIL list is a group of widget children, resource settings, callback settings, or callback procedures. Although we didn't mention it earlier, thecontrols,arguments, andcallbacks subsections of a widget definition are all in-line lists. Theprocedures syntax for specifying multiple callbacks is also an in-line list. You can also define a list outside of a widget definition and name it, so that you can use the list later in a widget definition or in another list.

You define named lists in alist section of a UIL module, which begins with thelist keyword. A list definition consists of the name of the list, followed by a colon, the type of the list, and its contents. Unlike variable and widget definitions, list definitions are always private to a module, so you cannot export them. If you want to use a list in more than one module, you should place its definition in a UIL include file. The list name is a programmer-specified identifier; the list type is one ofcontrols,arguments,callbacks, orprocedures. The content of a list depends on its type.

Lists ofcontrols,arguments, andcallbacks, like the widget subsections by the same name, contain widget children, resource settings, and callback settings, respectively. The format of each of these lists is the same as the format of the corresponding subsection of a widget definition. Aprocedures list is used to specify multiple callback routines for a particular callback reason, as we explained earlier. Once you define a named list, you can use it in a widget definition. the source code shows a UIL module that uses all four types of lists.

   /* simple_lst.uil -- simple example of lists */   module simple_lst   procedure     quit();     print (string);   list buttons : controls {     XmPushButton OK;     XmPushButton Help;   };   list size : arguments {     XmNwidth = 50;     XmNheight = 50;   };   list funcs : callbacks {     XmNactivateCallback = procedure print ("Help!");     XmNhelpCallback = procedure print ("Help!");   };   list ok_cbs : procedures {     print ("Okee-dokee");     quit();   };   object OK : XmPushButton {     arguments size;     callbacks {       XmNactivateCallback = procedures ok_cbs;     };   };   object Help : XmPushButton {     arguments size;     callbacks funcs;   };   object root : XmRowColumn {     controls buttons;   };   end module;
As with the object definition, we use the convention of placing each list definition in its ownlist section, even though it is not necessary for consecutive definitions. This example defines thebuttons,size,funcs, andok_cbs lists, and then uses the lists in defining the widget hierarchy. To use a list in a widget definition, you specify the subsection followed by the name of a list. The named list replaces the in-line list definition that you have seen previously. The UIL compiler makes sure that the type of each named list matches the name of the subsection, which means that you cannot specify a namedcontrols list for anarguments subsection, for example.

A namedlist definition lets you separate the contents of each list type from a widget definition. One advantage of this approach is that you can abstract commonly-used settings and define them in one place. However, the ability to factor out duplicate widget subsections and procedure lists is not that big of an advantage. The feature of lists that makes them more useful is the ability to reference other lists of the same type. You include one list in another by including an entry that consists of the type of the included list, followed by the name of the list to include.

Each reference to a list includes a copy of that list, which has different results depending on the type of the list. When you include acontrols orprocedures list in another list, the widgets or callbacks in the included list are added to the existing list, even if the same widget or callback is already there. Therefore, you can create multiple instances of the same widget or call the same callback multiple times. When you include anarguments orcallbacks list in another list, the resources or callback settings in the included list are added to the existing list, but any duplicate setting supersedes the earlier setting. The advantage of this behavior is that you can selectively override resource or callback settings that have already been specified. When you override a resource or callback setting, the UIL compiler generates an informational message. You can turn off these messages with the-w compiler option, but you should be careful to do so only if you know that a module does not generate any other warnings. the source code illustrates the use of nested lists.

   /* station.uil -- Example of using lists in lists */   module dialog   list basic_buttons : controls {     OK     : XmPushButton { };     Cancel : XmPushButton { };   };   list extended_buttons : controls {     controls basic_buttons;     Help : XmPushButton { };   };   list attach_all : arguments {     XmNtopAttachment    = XmATTACH_FORM;     XmNbottomAttachment = XmATTACH_FORM;     XmNleftAttachment   = XmATTACH_FORM;     XmNrightAttachment  = XmATTACH_FORM;   };   object stations : XmRadioBox {     controls {       WAQY : XmToggleButton { };  KLBJ : XmToggleButton { };       WPLR : XmToggleButton { };  KRCK : XmToggleButton { };       WHCN : XmToggleButton { };  KPEZ : XmToggleButton { };     };     arguments {       XmNorientation = XmHORIZONTAL;       XmNnumColumns = 3;       XmNmarginWidth = 20;       arguments attach_all;       XmNbottomAttachment = XmATTACH_NONE;     };   };   object panel : XmRowColumn {     controls extended_buttons;     arguments {       XmNorientation = XmHORIZONTAL;       XmNentryAlignment = XmALIGNMENT_CENTER;       XmNpacking = XmPACK_COLUMN;       arguments attach_all;       XmNtopAttachment = XmATTACH_WIDGET;       XmNtopWidget = stations;     };   };   object root : XmFormDialog {     controls {       XmRadioBox stations;       XmRowColumn panel;     };     arguments {       XmNdialogTitle = "Station Chooser";     };   };   end module;
This module describes a simple user interface that uses two different types of lists. The output of the module is shown in the figure.

Thebasic_buttons list is acontrols list that consists of two PushButtons: OK and Cancel. Theextended_buttons list builds on the first list by adding a Help PushButton. This list is used as part of the dialog later in the module. Theattach_all list is anarguments list that contains several Form attachment resource settings. We reference this list in the definition of both the RadioBox and the RowColumn, instead of reproducing the same settings in both widget definitions. In both cases, we override one of the resource settings from the list in the widget definition. This section only covers the basic use of lists in a UIL module. For more information on using lists, see Section #suiladvlist in Chapter 26, Advanced UIL Programming.

figs.eps/V6a.24.12.eps.png
User interface of station.uil


25.6 Exporting Application Data

A value that is created or defined in an application can be used in a UIL module by declaring the value as a UILidentifier and registering it with Mrm. In this context, the term identifier means a value that is imported from the application, not a programmer-defined symbol. You can use an identifier as the value of a resource or as a callback argument. Unlike other values in a UIL module, identifiers are not typed, which means that the UIL compiler cannot perform type checking on identifiers. You should be careful to avoid type-mismatch problems in your use of identifiers.

25.6.1 Declaring Identifiers in UIL

You can use a registered application-defined value in a UIL module by declaring it anidentifier section. This section begins with the keywordidentifier and is followed by a list of declarations. Identifiers can be used like any other values, as illustrated in the following code fragment:

   identifier     home_directory;     complex_data;   procedure     open (any);   object fsb : XmFileSelectionBox {     controls {       Xm_OK {         callbacks {           XmNactivateCallback = procedure open (complex_data);         };       };     };     arguments {       XmNdirectory = home_directory;       XmNpattern = '*.uil';     };   };
In this fragment, we use the identifiercomplex_data as the argument to theopen() callback. The identifier represents a value of typeComplexStructure that is declared in the application program, as you will see shortly. We declare the callback as taking an argument of typeany because there is no UIL type that correspondes to theComplexStructure type. Theany type indicates that the callback function takes an argument, but that the argument can be of any type. We recommend that you only use theany type for identifier arguments, since the UIL compiler cannot perform type-checking when you use a callback that takes anany argument. We also use the identifierhome_directory as the value of a resource setting. This setting is not type-checked either, so it is up to the application to make sure thathome_directory is a string.

25.6.2 Exporting Identifiers From Application Code

When Mrm creates a widget that uses an identifier, it must convert the identifier name to the corresponding application-defined value. Similar to callback procedures, you must register the names of identifiers and their associated values with Mrm by callingMrmRegisterNames() orMrmRegisterNamesInHierarchy() before fetching widgets that reference the exported values. These routines are the same ones that are used to register callback procedures, as described in Chapter 22, Introduction to UIL. These functions take the following form:

   Cardinal   MrmRegisterNames(identifier_list,num_identifiers)       MrmRegisterArglistidentifier_list;       MrmCountnum_identifiers;

   Cardinal   MrmRegisterNamesInHierarchy(hierarchy,identifier_list,num_identifiers)       MrmHierarchyhierarchy;       MrmRegisterArglistidentifier_list;       MrmCountnum_identifiers;
A call toMrmRegisterNames() makes the identifiers accessible from any open Mrm hierarchy, while a call toMrmRegisterNamesInHierarchy() makes the identifiers accessible from the Mrm hierarchy passed to the function. Identifiers that are registered in a specific Mrm hierarchy take precedence over those that are registered globally. Theidentifier_list argument specifies an array ofMrmRegisterArg structures, andnum_identifiers indicates the size of the array. TheMrmRegisterArg structure indicates the mapping from an identifier name to an application value and is defined as follows:
   typedef struct {       String     name;       XtPointer  value;   } MrmRegisterArg, *MrmRegisterArglist;
Thename field is the name of the identifier as used in the UIL module. The name is case-sensitive, unless it is being referenced from a module that has thenames option set tocase_insensitive, in which case the name must be upper case. Thevalue field is a pointer to an application variable. Sincevalue is anXtPointer, you should only register values that are pointers. If you use a value whose size is not the same as anXtPointer, part of the value may be lost or corrupted. The size of certain C values, such asint andfloat, are not necessarily the same size as anXtPointer on all architectures. You can ensure the portability of an application by using the address of non-pointer types as the value of an identifier.

The following code fragment shows howMrmRegisterNames() can be used to register the identifier values used in the UIL fragment of the previous section:

   char *home_directory;   ComplexStructure complex_data;   static MrmRegisterArg identifiers[] = {       { "home_directory", (XtPointer) directory },       { "complex_data", (XtPointer) &complex_data },   };   ...   MrmRegisterNames (identifiers, XtNumber (identifiers));   ...
This code registers thehome_directory andcomplex_data identifiers globally, so that they are accessible from any Mrm hierarchy. Thehome_directory identifier is a string, whilecomplex_data is a value of typeComplexStructure. Identifiers provide a mechanism for exporting arbitrarily complex data to a UIL module.

25.7 Summary

A UIL module may contain five types of sections, whose usage and syntax we have explained and demonstrated in detail.

Anobject section contains definitions of the widgets in an interface. An application creates these widgets at run-time by using theMrmFetchWidget() routine in the Mrm library.

Avalue section consists of declarations and definitions of UIL values that are used as resource settings and callback arguments in the widget definitions. Values can be fetched from a UIL module using a number of different Mrm routines.

Aprocedure section contains the declarations of callback procedures that are defined in the application program. Any callbacks that are specified in a UIL module must be declared in this section. Callback procedures are registered with the application program usingMrmRegisterNames().

Alist section is used to definecontrols,arguments, orcallbacks lists that are used in the corresponding subsections of widget definitions, and to defineprocedures lists that specify the functions that are called when a single callback is invoked. The use of lists is private to a UIL module.

Anidentifier section contains declarations of application variables that are exported to UIL. The names and values of identifiers are registered with the application program usingMrmRegisterNames().


ContentsPreviousNext

[8]ページ先頭

©2009-2025 Movatter.jp