Chapter 13. UIL and WML Compatibility

You must provide a way for Motif applications programmers to instantiate your new widget. Chapter 2 explained how to make widgets accessible to C applications. We now explain how to make your new widgets accessible to a User Interface Language (UIL) application. If you have no need of making your widgets accessible to a UIL application, you can skip this chapter.

UIL is a programming language designed for rapid prototyping of Motif interfaces. A UIL program consists mainly of definitions of the application's widget hierarchy. That is, a UIL program tells the Motif Resource Manager (MRM) which widgets to instantiate. (For details on writing UIL applications, see the Motif Programmer's Reference.)

Motif vendors typically provide a UIL compiler that "understands" only those widgets in the Motif toolkit. Fortunately, UIL is an extensible language. This means that, when you write a new widget, you can expand the UIL language to make the compiler understand your new widget.

Strategies

There are three ways to make your new widget accessible to UIL programmers:

  • You can provide your UIL customers with two small header files and one MRM initialization file. These files will contain sufficient information for the UIL compiler to compile your customer's UIL programs. Of the three ways, this is the easiest to implement. However, this way has some liabilities when it comes to type checking. That is, the UIL compiler will not detect certain coding problems in your customers' UIL programs.

  • You can provide your customers with a Widget Meta-Language Database (WMD) file. The UIL compiler reads the WMD file at runtime and processes the new or modified widget definitions dynamically. This is somewhat harder than the previous solution; however, an updated WMD file will allow UIL to do type checking.

  • You can provide your customers with a new UIL compiler that understands your new widget(s) This solution is the hardest of the three ways to implement. Furthermore, it provides no real advantages in performance over the second solution. Therefore, we do not recommend doing this. See the Motif Release Notes for information on building a UIL compiler.

Even if you choose the second or third mechanism, you still have to provide your customers with most of the files described in the first mechanism. For example, if you provide your customers with a WMD file, you must also provide them with one of the header files and an MRM initialization file.

Providing UIL Access Through Three Small Files

Probably the simplest way to make your new widget accessible to UIL applications is to provide the following three files:

  • A UIL creation header file

  • An MRM initialization file

  • A header file for the MRM initialization file

Providing these files permits users to instantiate your new widget from their UIL applications.

One important advantage of this mechanism is that it is portable. That is, the header file should work without modification on any UIL platform. One big disadvantage is that the UIL compiler will not be able to do complete data type checking on UIL applications.

The following subsections detail these files and then explain how a UIL application can access them.

The UIL Creation Header File

The UIL creation header file specifies the names of the new widget(s) and new resources in a format that MRM will be able to read. Typically, you create one UIL creation header file that describes all the widgets that you have created. You can find a sample UIL creation header file for the Exm sample widget set in the demos/programs/Exm directory under the filename Exm.uil. Following are the contents of this file:

!****************************************************************************
!*
!*  Exm.uil -  Exm widgets UIL creation header file
!*
!****************************************************************************


!* Exm Creation API

procedure
    ExmCreateSimple();
    ExmCreateString();
    ExmCreateStringTransfer();
    ExmCreateCommandButton();
    ExmCreateMenuButton();
    ExmCreateGrid();
    ExmCreateTabButton();
    ExmCreatePanner();

!* Exm Resources

value
    ExmNsimpleShape: private argument ('simpleShape', integer);
    ExmNcompoundString: private argument ('compoundString', compound_string);
    ExmNgridMarginWidthWithinCell: private argument ('gridMarginWidthWithinCell',
                                                       integer);
    ExmNgridMarginHeightWithinCell: private argument ('gridMarginHeightWithinCell',
                                                        integer);
    ExmNopenSide: private argument ('openSide', integer);
    ExmNreportCallback: private argument ('reportCallback', callback);
    ExmNrubberBand: private argument ('rubberBand', boolean);
    ExmNcanvasWidth: private argument ('canvasWidth', integer);
    ExmNcanvasHeight: private argument ('canvasHeight', integer);
    ExmNsliderX: private argument ('sliderX', integer);
    ExmNsliderY: private argument ('sliderY', integer);
    ExmNsliderWidth: private argument ('sliderWidth', integer);
    ExmNsliderHeight: private argument ('sliderHeight', integer);

value
    ExmSHAPE_OVAL: 0; ExmSHAPE_RECTANGLE: 1;

The procedure section of the header file lists the convenience creation functions of all eight Exm widgets. The first value section describes the new resources of the Exm widgets. (Resource names already used in the Motif toolkit need not be listed in the value section.) The second value section associates numerical constants with the enumerated constants of the ExmNsimpleShape resource.

If you decide to create your own WMD file, then you do not need to create the UIL creation header file.

See the reference page for UIL(5X)for more information.

MRM Initialization File

Before a UIL application can use one of your new widgets, the widgets must be registered with the MRM. You register a widget by calling the MrmRegisterClass function. The easiest way to register a group of widgets is to pack all the MrmRegisterClass calls into one convenience function. For example, the MRM initialization file for all the Exm widgets is stored online in file demos/lib/Exm/ExmMrm.c. Following are its contents:

#include <stdio.h>
#include <Xm/Xm.h>
#include <Mrm/MrmPublic.h>
#include <Exm/Simple.h>
#include <Exm/String.h>
#include <Exm/StringTrans.h>
#include <Exm/CommandB.h>
#include <Exm/MenuB.h>
#include <Exm/Grid.h>
#include <Exm/TabB.h>
#include <Exm/Panner.h>

/**********************************************************************
 *
 * ExmMrmInitialize - register Exm widget classes with Mrm
 *
 *********************************************************************/

int ExmMrmInitialize()
{
    MrmRegisterClass (MrmwcUnknown, "ExmSimple",
                        "ExmCreateSimple", ExmCreateSimple,
                        exmSimpleWidgetClass);
    MrmRegisterClass (MrmwcUnknown, "ExmString",
                        "ExmCreateString", ExmCreateString,
                        exmStringWidgetClass);
    MrmRegisterClass (MrmwcUnknown, "ExmStringTransfer",
                        "ExmCreateStringTransfer", ExmCreateStringTransfer,
                        exmStringTransferWidgetClass);
    MrmRegisterClass (MrmwcUnknown, "ExmGrid",
                        "ExmCreateGrid", ExmCreateGrid,
                        exmGridWidgetClass);
    MrmRegisterClass (MrmwcUnknown, "ExmCommandButton",
                        "ExmCreateCommandButton", ExmCreateCommandButton,
                        exmCommandButtonWidgetClass);
    MrmRegisterClass (MrmwcUnknown, "ExmMenuButton",
                        "ExmCreateMenuButton", ExmCreateMenuButton,
                        exmMenuButtonWidgetClass);
    MrmRegisterClass (MrmwcUnknown, "ExmTabButton",
                        "ExmCreateTabButton", ExmCreateTabButton,
                        exmTabButtonWidgetClass);
    MrmRegisterClass (MrmwcUnknown, "ExmPanner",
                        "ExmCreatePanner", ExmCreatePanner,
                        exmPannerWidgetClass);
    return (0);
}

This file includes the widget public header files of all eight Exm widgets.

Creating an MRM initialization file is not a requirement; it is only a convenience for UIL applications programmers. The only requirement for a UIL application is that MrmRegisterClass gets called for each new widget.

Header File for the MRM Initialization File

If you create an MRM initialization file, then you should also create a header file for it. The header file will provide a convenient handle for UIL applications programs to access your MRM initialization file. We provide such a header file in file ExmMrm.h. The only code in the file is as follows:

int ExmMrmInitialize(void);

Accessing Widgets from a UIL Application

After creating the three files described earlier in this section, UIL applications programs can access the new widgets. We provide a sample UIL application in the directory demos/programs/Exm/app_in_uil. This code looks very much like any other UIL application. In fact, there are only two differences between this UIL application and a UIL application that uses the standard Motif widget set.

First, the UIL application must include the UIL creation header file; for example:

include file ("Exm.uil");

Second, the UIL callback file must invoke the registration function defined in the MRM initialization file. To make this work, the UIL callback file must also include the appropriate header file. For example, the UIL callback file app_in_uil.c contains the following code:

#include <ExmMrm.h>

MrmInitialize ();  /* standard Motif widget set */
ExmMrmInitialize(); /* Exm widget set */

What is WML?

Another way to make your new widgets accessible to UIL applications is to create a new UIL compiler that understands your new widgets. Motif provides the Widget Meta-Language (WML) facility to help you do this.You can use WML in two ways:

  • Compile a customized WML file into a Widget Meta-Language Database (WMD) file. The UIL compiler reads this file at runtime and processes the new or modified widget definitions dynamically.

  • Build a new UIL compiler by running the WML facility with a customized WML file.

This section explains how to write an appropriate WML file. Later sections of this chapter will explain how to build a new UIL or WMD from your WML file.

The WML facility generates the components of the UIL compiler that can change depending on the widget set. WML adds support in UIL for additional widgets that are not in the Motif standard widget set or for a totally new widget set.

UIL is made up of the following:

  • Static syntax

  • Dynamic syntax

  • Data types

Static Syntax Elements

The static syntax elements are the basic syntax and keywords of UIL. These elements do not change when the widget set is modified. The static syntax elements of UIL are defined in the file Uil.y in the WML source directory.

Dynamic Syntax Elements

The dynamic syntax elements are the parts of UIL that change with the widget set. These elements describe the widget and gadget classes supported by UIL, including their resources and hierarchy. The dynamic elements of UIL are defined in WML files. The motif.wml file (stored in the WML source directory) defines the dynamic elements of the standard Motif widget set. The Exm.wml file (in the demos/lib/Exm/wml directory) defines the dynamic elements of the Exm demonstration widget set.

Data Types

The data type elements describe the allowable data types for each widget and gadget resource. Although the data types do not change, the resources that they are assigned to change with the widget set. The allowable data types for each resource are defined in the same file as the dynamic syntax elements.

The WML facility combines the static syntax, dynamic syntax, and data type elements to produce new source code for UIL. This allows a developer to modify the dynamic elements of UIL, adding resources, widgets, gadgets, or even new widget sets.

For more information on the syntax of WML files, see the WML(5X) reference page in the Motif Programmer's Reference. The next section explores a sample WML file.

A WML File Example

The WML file that describes the standard widgets of the Motif toolkit is stored in the tools/wml directory in filename motif.wml. We also provide an example that demonstrates how to write your own WML file. This example WML file describes the Exm demonstration widget set. You can find this example WML file in the demos/lib/Exm/wml directory as Exm.wml.

WML filenames must have the .wml suffix.

A Sample #include Directive

Our goal is to produce a UIL compiler that can compile requests for two kinds of widgets:

  • The standard widgets and gadgets of the Motif toolkit

  • The Exm widget set

The easiest way to ensure that our new UIL compiler will understand all the standard widgets and gadgets is to specify the following line:

#include "motif.wml"

This line includes the descriptions of all the standard Motif widgets in the Motif toolkit.

A Sample ControlList

Use the ControlList directive to add the names of your widgets to various categories. Although you can specify any categories you want, you should at least place the appropriate widgets in the following four categories:

  • AllWidgetsAndGadgets contains the name of every widget or gadget.

  • AllWidgets contains the name of every widget. (It does not include the name of any gadget.)

  • MenuWidgetsAndGadgets contains the name of any widget or gadget that can be in a menu. In the standard widget set, this includes obvious choices like XmPushButton and somewhat less obvious widgets like XmSeparator. Your widget should go into this list if it can be a child of a RowColumn that has an XmNrowColumnType resource of anything except XmWORK_AREA. In the Exm demonstration widget set, only ExmMenuButton meets this criteria.

  • ManagerWidgets contains the name of every manager widget. In other words, any subclass of XmManager should be in this category.

For example, here is the complete ControlList for Exm.wml:

ControlList
        AllWidgetsAndGadgets
                {
                ExmSimple;
                ExmString;
                ExmCommandButton;
                ExmMenuButton;
                ExmStringTransfer;
                ExmPanner;
                ExmGrid;
                ExmTabButton;
                };
        AllWidgets
                {
                ExmSimple;
                ExmString;
                ExmCommandButton;
                ExmMenuButton;
                ExmStringTransfer;
                ExmPanner;
                ExmGrid;
                ExmTabButton;
                };
        MenuWidgetsAndGadgets
                {
                ExmMenuButton;
                }
        ManagerWidgets
                {
                ExmGrid;
                };

An EnumerationSet Example

If any of your new widget resources require an enumerated value, you must specify them within an EnumerationSet section. The EnumerationSet section defines the set of legal enumerated constants for enumerated resources. For example, the Exm demonstration widget contains a resource, ExmNsimpleShape, which requires enumerated constants. Therefore, the EnumerationSet section appearing in Exm.uil looks as follows:

EnumerationSet
        SimpleShape: integer
                { ExmSHAPE_OVAL; ExmSHAPE_RECTANGLE; };

Resource names should be stripped of their prefix. Therefore, the ExmNsimpleShape resource is specified as SimpleShape. The data type will always be integer. The values inside the braces are the set of legal enumerated constants for the resource.

A Resource Example

Your WML file must contain one or more Resource sections to describe the "new" resources supported by your widget. A new resource is one that is not defined by another Resource section. If your WML file includes the standard Motif WML file ( motif.wml), then all the standard Motif resources will already have been defined for you. In this case, you should not specify any standard Motif resources within the Resource sections of your own WML file.

Consider the resource set of the ExmString widget as defined within the String.c file. Although the resources array of String.c defines five resources, four of these resources are part of the standard widget set. Therefore, the only resource that needs to be described in a Resource section is ExmNcompoundString. Following is the appropriate code:

Resource
        ExmNcompoundString: Argument
                { Type = compound_string; };

Constraints must be labeled with the keyword Constraint. For example, the description of the two constraints of the ExmGrid widget follows:

Resource
        ExmNgridMarginWidthWithinCell: Constraint
                { Type = integer; };
        ExmNgridMarginHeightWithinCell: Constraint
                { Type = integer; };

You should beware of a few resource and constraint naming rules. WML will issue an error message if you attempt to use the same name as both a resource and a constraint. For example, the previous code fragment specified ExmNgridMarginWidthWithinCell as a Constraint. If a subsequent Resource section lists ExmNgridMarginWidthWithinCell as an Argument (that is, as a resource), then WML will issue an error message.

You should also be careful when reusing a name. If you do reuse a name, you must ensure that the Type is the same for both definitions. For example, given that you have already declared ExmNcompoundString as a compound_string, WML will issue an error message if you try to redefine ExmNcompoundString as an integer..

Class Example

The Class definition can supply a rich variety of information about your widget class. At the very least, each Class definition should probably contain definitions for SuperClass, ConvenienceFunctions, WidgetClass, and Resources. For example, following is the Class definition for ExmString:

Class
        ExmString: Widget
                {
                SuperClass = ExmSimple;
                ConvenienceFunction = ExmCreateString;
                WidgetClass = ExmString;
                Resources {
                        XmNtraversalOn;
                        ! New
                        ExmNcompoundString;
                        XmNrenderTable;
                        XmNalignment;
                        XmNrecomputeSize;
                        };
                };

The Resources modifier contains the names of all resources defined in the XtResource array of ExmString. If a resource appears in the XtResource array in order to override the default value established by a superclass, then the resource should appear above the “! New” comment. All other resources should appear below the “! New ” comment. For example, of the five resources in the XtResource array of ExmString, only XmNtraversalOn appears above “ ! New” since it is a resource of a superclass ( XmPrimitive).

Building a WMD File from Your WML File

This section explains how to create a WMD file from your WML file. The easiest way to build a new WMD file is to make minor modifications within the example directory that we provide. This example directory is at pathname demos/lib/Exm/wml. Inside this directory, you will find an Imakefile, our sample WML file (Exm.wml), and several other necessary files.

The Imakefile we provide creates a WMD file named Exm.wmd from the Exm.wml file. If you want to create Exm.wmd, you must issue the following sequence of commands:

cd demos/widgets/Exm/wml
make Makefile
make includes
make depend
make

The resulting Exm.wmd file can now be used by a UIL compiler. (See the next section for details.)

To build a WMD file from your own WML file, you should first copy your WML file into the demos/lib/Exm/wml directory.

Second, change two lines of the Imakefile so that it will build a WMD file from your WML file rather than from Exm.wml. The two lines to change are the definitions of the TABLE and WMDTABLE variables. Before you edit the Imakefile, these definitions look as follows:

TABLE = Exm.wml
WMDTABLE = Exm.wmd

You should change these variable assignments to reflect the names of your WML and WMD files. For example, suppose your WML file was stored in file My.wml. In this case, you would change the TABLE and WMDTABLE assignments as follows:

TABLE = My.wml
WMDTABLE = My.wmd

By convention, the WMDTABLE variable should have the same root name as the TABLE variable, but it should have the suffix wmd instead of wml.

Next, you must modify the wmldbcreate.c file stored inside the demos/lib/Exm/wml directory. The only part of the wmldbcreate.c file that you need to modify is the section containing the names of the user-supplied public header files for your widget. This section currently contains the names of all eight public header files for the Exm widget set, as follows:

#include <Exm/Simple.h>
#include <Exm/String.h>
#include <Exm/StringTrans.h>
#include <Exm/CommandB.h>
#include <Exm/MenuB.h>
#include <Exm/Panner.h>
#include <Exm/Grid.h>
#include <Exm/TabB.h>

All you have to do is to replace these lines with the names of your widget's public header files.

You build a WMD file by issuing the following command sequence:

cd demos/lib/Exm/wml
make Makefile
make includes
make depend
make

The resulting My.wmd file can now be used by a UIL compiler.

UIL Compiling with Your WMD File

By default, the UIL compiler bases its compilation on the default WMD file stored in motif.wmd. An application programmer needing to use your WMD file instead of the default must do one of the following:

  • Specify the −wmd option to the uil command

  • Supply values for the database and database_flag members of the Uil_command_type structure whose address is the first argument to the UIL function

For example, given a WMD file named My.wmd and a UIL application name app.uil, the application programmer can issue a compilation command like the following:

uil -wmd My.wmd -o app.uid app.uil

Note that the application programmer must also have access to the header files and MRM initialization files described earlier in this chapter.