Chapter 6. Inside a Widget

This chapter describes the code inside a basic widget. Much of this code is common to all widgets. You can think of it as a framework that Xt uses to implement a widget's features. After reading this chapter, you should understand the procedure for creating your own widget around this framework.

This chapter reveals the framework of code common to all widgets. As an example, it describes the code for the BitmapEdit widget that was used in versions of the xbitmap applications early in Chapter 4, An Example Application. Later examples in that chapter described how to implement the bitmap editor without a BitmapEdit widget. Therefore, you have already seen the code that is specific to this widget, and can concentrate on the framework and how to place widget-specific code within this framework.

Some applications use only the standard user interface elements defined in the widget set. If you are writing an application like this, you have no need to write your own widgets, and no real need to understand the internals of widgets. However, many applications have at least one custom window which has features not supported by any existing widget. To implement such features, you can add to a Core or Primitive widget from your application code or you can write your own widget.

Placing specialized user-interface code into a widget has several advantages. For one, the widget becomes a self-contained module that can be modified and documented separately and used in other programs. Second, the code will take advantage of Xt's automatic dispatching of events to widgets, and several other features that you can't use from the application. Finally, it is a general premise of Xt that application code should be separated as much as possible from user-interface code.

It is important to remember that widget classes are never written from scratch. People always start from template files that contain the framework for a widget without the specific code, or even better, if possible, from an existing widget that has some similar characteristics. Therefore, you'll never have to type in the framework you're about to see. You'll only have to learn where to insert your code into it. The beauty of the widget framework is that the code within it is very modular--each module has a specific place and purpose. Once you understand the framework, you can locate these modules in existing code and use them as examples. Chapter 7, Basic Widget Methods, shows you how to write the most important modules within the framework.

Writing your own widget is also known as subclassing a widget, because you always build off the features of some existing widget class. It is possible to subclass any existing widget, to add or modify features of the existing widget. However, to subclass a widget with existing features, you have to understand in detail how these features are implemented. Therefore, subclassing a complicated widget such as Text is very difficult. To implement a custom window for an application, it is normally sufficient to subclass Core or Primitive.

By and large, a widget whose superclass is Core can operate under any widget set without modification. However, both the Motif widget set and the OPEN LOOK widget set have Primitive classes beneath Core that add some basic features common to all widgets in each set. In the case of Motif, by subclassing from Primitive you get support for keyboard traversal with highlighting, resolution independence, shadows, Motif's automatic color selection, and a Help key callback. These features are basic to Motif's user-interface conventions, and therefore a widget that doesn't use them would look out of place in a Motif application. In order to inherit these features, BitmapEdit is a subclass of Primitive; it not compatible with other widget sets. Converting it, however, is a trivial matter, and will be described later in the chapter.

Widget Source File Organization

A widget is implemented in two header files and an executable code file. Each of these files contains specific elements. The names of these files are derived from the name of the widget class, which in this case is BitmapEdit:

  • The private header file, BitmapEdiP.h, defines the widget's class and instance structures, including pointers to the widget's methods.

  • The implementation file, BitmapEdit.c, contains the actual code for the widget, including the widget's methods and actions.

  • The public header file, BitmapEdit.h, contains declarations needed by the application to use the widget.

The final “P” in the include file name stands for Private. Only BitmapEdit.c and any modules that implement subclasses of BitmapEdit should include BitmapEdiP.h. If an application includes this file and references any of its contents, it is breaking the rules of encapsulation, and changes to this widget may affect the application.

An application program that uses a widget includes only BitmapEdit.h.

The implementation filenames (and all the filenames used by your application, for that matter) should have 12 or fewer characters so that the code can be copied easily to some System V systems.[43] That's why the “t” in BitmapEdiP.h is left out.

The next three major sections describe the contents of the three files that make up a widget. These files are treated in the order shown in the above list because they are generally developed in this order.

The Private Header File--BitmapEdiP.h

Xt implements classes and instances with two structures, the class structure and the instance structure. By definition of C structures, the fields in both structures are fixed at compile time. Both of these structures are defined in the private include file for the class, in this case BitmapEdiP.h.

There is only one copy of the class structure in the running application, which all instances share. But each instance has its own copy of the instance structure, whose fields are set by the resource database as the instance is created. Fields in the instance structure can also be set by XtVaSetValues() or read by XtVaGetValues().

The class structure's fields contain pointers to methods and pieces of data that control how Xt handles instances of this class.

The instance structure carries a complete widget state, including everything that can be different between one instance and the next. For example, the instance structure of the Core class, which every widget inherits, has fields for x, y, width, and height values, which correspond to the size of the widget and its location relative to the top-left corner of its parent. These particular fields are public; they can be set from the resource database or with XtVaSetValues(), and their values can be read with XtVaGetValues(). Other instance structure fields are private, and are used only for convenience to make data globally available within the widget.

Actually, the instance structure is not global in the normal C sense. When Xt invokes a method, it passes the method a pointer to the widget instance structure. Methods do their work by using the public fields and changing the private fields in this instance structure. For example, a method that draws in the window can get the window's dimensions directly from the widget instance structure. When action functions are called, they also are passed a pointer to the instance structure. Therefore, the fields inside the instance structure are available just about everywhere in the widget code.

Parts and Records

The organization of both class and instance structures is determined by the hierarchy of widget classes from which the current widget class is derived. For example, in the code for the BitmapEdit widget whose class hierarchy is shown in Figure 6-1, the class structure begins with the class fields defined by the Core class, followed by the class fields defined by Primitive, followed by the class fields defined by BitmapEdit.

Figure 6-1. The class hierarchy of the BitmapEdit widget (with other classes shown dotted)


Xt supplies three basic classes that are used as the basis for custom widgets, Core, Composite, and Constraint.[44] Figure 6-1 also shows the relationship of these classes to each other. Core is the class upon which all widgets are based. It defines common characteristics of all widgets, such as their methods, and basic resources such as height and width. Even if your widget is unlike any existing widget, it will still inherit features from the Core widget. The class and instance structures of a subclass of Composite such as Box begin with the fields from Core, continue with fields from Composite, and end with the fields defined by Box. Composite and Constraint are subclasses of Core that have additional methods that allow them to manage children; they are described in Chapter 12, Geometry Management. This chapter concentrates on the features of the Core widget.

Xt requires that you implement each class's new fields as a separate structure called a Part, and then combine this Part structure with each superclass's Part structure in the complete structure called a Rec, or record. In real code, these structures are called widgetnameClassPart and widgetnameClassRec for the class structure, and simply widgetnamePart and widgetnameRec for the instance structure.

The reason for this “structure within a structure” design is primarily to reduce the changes in the code of subclasses that would be required if changes were made to the superclass's structures. As we will see later, only the portion of the .c file that initializes the class structure will need changing if a superclass class structure is changed. A second benefit of this design is that it reduces the amount of typing required to implement a new class.

Class Part and Class Record

Let's make these ideas concrete by showing the class structure for BitmapEdit. The complete class structure is called BitmapEditClassRec, and the partial structure is called BitmapEditClassPart. Their definitions from BitmapEdiP.h are shown in Example 6-1.

Example 6-1. BitmapEdiP.h: the class part and class record

/*
 * BitmapEditP.h - Private definitions for BitmapEdit widget
 */
/* protect against multiple including of this file */
#ifndef _ORABitmapEditP_h
#define _ORABitmapEditP_h
/*
 * Include private header file of superclass.
 */
#include <Xm/PrimitiveP.h>
/*
 * Include public header file for this widget.
 */
#include "BitmapEdit.h"
/* New fields for the BitmapEdit widget class record */
typedef struct {
    int make_compiler_happy;    /* need dummy field */
} BitmapEditClassPart;
/* Full class record declaration */
typedef struct _BitmapEditClassRec {
    CoreClassPart    core_class;
    XmPrimitiveClassPart    primitive_class;
    BitmapEditClassPart    bitmapEdit_class;
} BitmapEditClassRec;

Like most widget classes, BitmapEdit provides no new fields in the class part, but it needs one dummy member to make the C compiler happy. A class defines new class part fields to allow a subclass to choose to inherit a function or to replace it. Most classes don't define fields here because they don't plan to have subclasses. This fine point is described in Section 12.4.5, "The class_part_init Method."

You need to include the private header file of the superclass at the top of your private header file. The CorePart and CoreClassPart structures are defined in Core's private header file <X11/CoreP.h>, which is included by <Xm/PrimitiveP.h>. XmPrimitivePart and XmPrimitiveClassPart are also defined in <Xm/PrimitiveP.h>. The header files begin with an ifndef statement that allows the preprocessor to make sure that no header file is included twice.

If the class structure contains a pointer to an extension structure, you can add to the class structure in later releases of your widget and maintain binary compatibility with subclasses. This feature is used in the basic Intrinsics classes Composite and Constraint, but is unlikely to be useful to you. However, when you do define an extension structure, you do so in the private header file. Extension structures are discussed in Section 14.13, "Class Extension Structures."

Instance Part and Instance Record

The instance record is built exactly like the class structure: by defining new fields in a part structure, and then by combining the instance parts of all superclasses in the instance record. BitmapEditPart defines BitmapEdit's new widget instance fields, and the entire widget instance record is BitmapEditRec. These structures are defined in BitmapEdiP.h (along with the class structures just shown) and are shown in Example 6-2. We've included several likely instance variables, but none of those shown is an essential part of the widget structure.

Example 6-2. BitmapEdiP.h: the instance part and instance record

/* New fields for the BitmapEdit widget record */
typedef struct {
    /* resources */
    Pixel  foreground;
    XtCallbackList  callback;/* application installed callback fns */
    Dimension  pixmap_width_in_cells;
    Dimension  pixmap_height_in_cells;
    int  cell_size_in_pixels;
    char  *cell;             /* for keeping track of array of bits */
    Boolean showAll;         /* whether bitmap should display
                                entire bitmap */
    /* private state */
    int  cur_x, cur_y;       /* pstn of visible corner in big pixmap */
    Dimension  pixmap_width_in_pixels;
    Dimension  pixmap_height_in_pixels;
    Pixmap  big_picture;
    GC  draw_gc;             /* for drawing into pixmap */
    GC  undraw_gc;           /* for undrawing into pixmap */
    GC  copy_gc;             /* for copying pixmap into window */
} BitmapEditPart;
/*
 * Full instance record declaration
 */
typedef struct _BitmapEditRec {
    CorePart            core;
    XmPrimitivePart     primitive;
    BitmapEditPart      bitmapEdit;
} BitmapEditRec;
#endif                       /* _ORABitmapEditP_h */


Unlike the class part, which generally defines no new fields, the instance part of a widget almost always defines new instance variables. These variables control all the configurable elements of the widget, and they hold the widget's state.

Some of these instance variables are resources because they are listed in the resource list in the BitmapEdit.c file. (This resource list has exactly the same format as the resource list you saw in the application code in Section 3.6.2, "The Resource List"). These variables are known as public instance variables (because they are readable and writable from the application). By convention, the public instance variables are placed first in the instance part structure, and comments indicate which fields are public. When the application instantiates the widget, Xt sets the public fields based on the resource databases, the command line (the -xrm form), and the argument list passed to XtVaCreateManagedWidget(). Later on, the application may change these fields using XtVaSetValues().

The private instance structure fields (not listed in the widget's resource list) are either derived from resource values or they hold some aspect of the widget's state (such as whether it is highlighted). As in Example 6-2, graphics contexts (GCs) are always found here as private fields if the widget draws any graphics. Graphics contexts are needed for doing drawing, and are derived from public instance structure fields such as colors and fonts, because it is the GC that actually carries color and font information to the X server. GCs were introduced in Section 4.2.2, "Graphics from the Application."

You may notice that the Part structures of the superclasses are referenced in the BitmapEditRec structure. As mentioned earlier, you get these by including the private include file of the immediate superclass, which in turn includes the private include file of its own superclass, and so on.

Both the class and instance structures defined in BitmapEdiP.h are typedef templates; they do not allocate storage. Xt allocates storage for the instance record when the application calls XtVaCreateWidget() or XtVaCreateManagedWidget() to create the widget. The class record, on the other hand, is initialized statically (at compile time) in the .c file. The compiler makes sure that the definition of the class record (in BitmapEdiP.h) and the initialization of the class record (in BitmapEdit.c) use identical structures, since BitmapEdit.c includes BitmapEdiP.h. If a field is accidentally left out of the class structure in either file, the compiler will catch the problem (but if the same member is left out of both files, the problem won't be caught and Xt will likely dump core).

BitmapEdiP.h contains an extern reference to the class structure initialized in BitmapEdit.c. This reference is shown in Example 6-3.

Example 6-3. BitmapEdiP.h: declaring the external class record

extern BitmapEditClassRec bitmapEditClassRec;

Because the private header file includes the public header file, there is no obvious reason for this extern declaration. But since all widget code seems to have it, we go along with the convention.

The naming conventions for the various structure declarations in the private header file are important, and can be confusing. A table summarizing the conventions for types and variables in the widget implementation files is shown in Section 6.6, "Summary of Conventions" (after the contents of the .c and .h files are shown).

That's all there is in the private header file! If you should need to refer back to the private header file for BitmapEdit, it is listed with the rest of the source for the widget in Appendix E, The xbitmap Application.

The Widget Implementation File--BitmapEdit.c

The central element of the .c file is the initialization of the class record. Remember that the typedef of the class record was declared in BitmapEdiP.h, but the record is allocated and the actual values in each field are set in BitmapEdit.c. When Xt takes over control of the widgets after the application calls XtAppMainLoop(), it is the values in this class record that supply Xt with all the information it uses to manage widget instances.

The organization of the .c file is quite simple. First, it defines everything that will be placed into the class record, and then initializes the class record, setting fields using these definitions. The major things that need defining are the functions that implement each method, the resource list, the translation table, and the actions table. It would be logical to define these four things at the beginning of the source file, and then put the class record last. This is almost the case, except that by convention the methods and actions are declared at the top of the source file and then defined at the end after the class record initialization. This actually makes the widget code clearer because the method declarations provide a complete list of the methods that will be defined later in the file, and the class record remains near the top of the file where it's easier to find. (Not all the methods have to be defined by a class, because some methods can be inherited or not used, as we will discuss in Section 6.3.6, "Description of Core Methods.") Figure 6-2 summarizes the conventional order of code in the .c file.[45]

Figure 6-2. Order of code in widget .c file


Obligatory Include Files

The .c file begins with the standard includes:

  • <stdio.h>, since printf always comes in handy for debugging purposes.

  • <Xm/XmP.h>, which includes <X11/IntrinsicP.h> (which includes <X11/Intrinsic.h>) for the Xt supplied widget classes Core, Composite, and Constraint, declarations of Intrinsics functions, and several useful macros. XmP.h also includes <Xm/Xm.h> for the standard resource names used in defining the resource list.

  • For widget sets other than Motif, include <X11/IntrinsicP.h> and <X11/StringDefs.h>.

  • The private header file for this widget: "BitmapEdiP.h".

Example 6-4. BitmapEdit.c: include files

#include <stdio.h>   
#include <Xm/XmP.h>   
#include "BitmapEdiP.h"   

Remember that BitmapEdiP.h includes the private header file of the immediate superclass (which includes the private header file of its own superclass, and so on), and each private header file includes the public header file for its class, so that all the information in all the public and private header files for all superclasses is available to this .c file as a result of this one include statement.

We'll look at BitmapEdit.c in seven parts, in an order corresponding to Figure 6-2.

Defining the Resource List

A widget inherits the resources defined by its superclasses, and it can also add its own resources by defining a resource list and setting it into the class structure. A widget resource list is identical to an application resource list, which you saw added to xbitmap in Chapter 4. The only difference is that a widget need not call XtGetApplicationResources().

In creating an application resource list, we created a structure called app_data whose fields were to be set through resources. In widget code, the instance part structure is used just like app_data, except that the private instance structure fields will not appear in the resource list. Each member of the instance structure that is to be a resource must have an entry in the resource list.

Example 6-5 shows a resource list for the public instance variables defined previously in Example 6-2.

Example 6-5. BitmapEdit's resource list

#define offset (field) XtOffsetOf (BitmapEditRec, field)
static XtResource resources[] = {
     {
    XtNtoggleCallback,
    XtCToggleCallback,
    XtRCallback,
    sizeof(XtPointer),
    offset(bitmapEdit.callback),
    XtRCallback,
    NULL
     },
     {
    XtNcellSizeInPixels,
    XtCCellSizeInPixels,
    XtRInt, sizeof(int),
    offset(bitmapEdit.cell_size_in_pixels),
    XtRImmediate,
    (XtPointer)DEFAULT_CELL_SIZE
     },
     {
    XtNpixmapWidthInCells,
    XtCPixmapWidthInCells,
    XtRDimension,
    sizeof(Dimension),
    offset(bitmapEdit.pixmap_width_in_cells),
    XtRImmediate,
    (XtPointer)DEFAULT_PIXMAP_WIDTH
     },
     {
    XtNpixmapHeightInCells,
    XtCPixmapHeightInCells,
    XtRDimension,
    sizeof(Dimension),
    offset(bitmapEdit.pixmap_height_in_cells),
    XtRImmediate,
    (XtPointer)DEFAULT_PIXMAP_HEIGHT
     },
     {
    XtNcurX,
    XtCCurX,
    XtRInt,
    sizeof(int),
    offset(bitmapEdit.cur_x),
    XtRImmediate,
    (XtPointer) 0
     },
     {
    XtNcurY,
    XtCCurY,
    XtRInt,
    sizeof(int),
    offset(bitmapEdit.cur_y),
    XtRString,
    (XtPointer) NULL
     },
     {
    XtNcellArray,
    XtCCellArray,
    XtRString,
    sizeof(String),
    offset(bitmapEdit.cell),
    XtRImmediate,
    (XtPointer) 0
     },
     {
    XtNshowEntireBitmap,
    XtCShowEntireBitmap,
    XtRBoolean,
    sizeof(Boolean),
    offset(bitmapEdit.showAll),
    XtRImmediate,
    (XtPointer) True
     },
};


The details of each field in a resource list entry is presented in Chapter 10, Resource Management and Type Conversion.

As mentioned earlier, a widget class inherits all the resources defined in the resource lists of its superclasses. If a resource is given the same name as a superclass resource, it overrides the superclass resource. One reason to create a new resource with the same name as a superclass resource is to give it a new, subclass-specific default value. The Primitive widget does this to replace Xt's default value for XmNbackground and XmNbackgroundPixmap. Another reason is to provide further processing on a resource value. For example, Primitive redefines the XmNx and XmNy resources so that it can add code to calculate these values using the current unit type (to implement resolution independence).

When defining a resource list, use constants defined in <Xm/Xm.h> whenever possible. However, any constant unique to this widget class can be defined in the public include file, as is described in the next section.

Table 6-1 summarizes the conventions for the constants used in the resource list.

Table 6-1. Resource List Constant Conventions

Prefix

First word capitalization

Description

XmN

Lower

Resource name

XmC

Upper

Resource class

XmR

Upper

Representation type

The representation type of a resource is a string that represents the type in which the widget stores the resource value. Xt constants and functions of all types use uppercase letters whenever a word break might otherwise be called for. Two examples of resource names following this convention are XmNborderColor and XmNmappedWhenManaged.

Example 6-6 shows how the resource list is entered into the class structure. You'll see this again in Section 6.3.5, "Initializing the Class Record" where we show the entire class record and describe how to initialize it. The names of the fields in the class structure are shown in the comments at right.

Example 6-6. Setting the resource list into the class structure

BitmapEditClassRec bitmapEditClassRec = {
    {  /* Core class part */
          .
          .
        resources,              /* resources */
        XtNumber(resources),    /* resource_count */
          .
          .
    },
    {  /* Primitive class part */
        .
        .
        .
    },
    {  /* BitmapEdit class part */
        0,                      /* dummy_field */
    }
};

Note that a widget or an application can get the resource list for a class using XtGetResourceList(), which returns a pointer to a resource list of the same form as shown above. This function isn't needed in most applications or widgets.

The Translation Table and Actions Table

You may recall that the translation table maps event sequences into string action names, and the action table then maps string action names into actual action functions. This is done in two steps so that the translation table is a single string that can be specified in the resource databases (since resource databases are always composed entirely of strings). The action table is not configurable through the resource database, but it can be added to the application or with XtAppAddAction(s).

Like the resource list, the default translation table and the actions table have to be defined before the class record can be initialized. They determine which events this widget will respond to, and which functions defined in this source file will be triggered by those events.

The translation table is a resource defined by the Core class. It is a strange resource in several ways. It has no default value in the resource list. Instead, the default translation table is initialized into the class structure. Translations are not inherited like other resources--you do not get the sum of all the translation tables registered by the superclasses. Rather, the translation table you specify in the class structure is the only one (in the code) that matters. However, you can choose to use the immediate superclass's translation table instead of defining one, in which case you initialize the translation table field in the class structure to be XtInheritTranslations.

Each widget has its own action list, and if the application registers an action list, it is kept as a separate list. When an event combination occurs in a widget, Xt translates the event combination into an action string and searches that widget's action list. If the action string is found in the widget's action list, the search stops and that action function is called. If the action string is not found in the widget's action list, then the application's action list is searched. If neither list contains the appropriate action string, Xt prints a diagnostic warning. If the application and the widget both define the same action string in the actions table, the application's function mapped to that string will never be called. Two widget classes, however, may have the same action function name, and they will not conflict.

Example 6-7 shows a default translation table and an action table. Note that, like methods, the action functions are declared before being used in the actions table, but they will actually be defined later in the source file.

Example 6-7. The default translation table and the actions table

static void DrawCell(), UndrawCell(), ToggleCell();
static char defaultTranslations[] =
    "<Btn1Down>:    DrawCell()               \n\
     <Btn2Down>:    UndrawCell()             \n\
     <Btn3Down>:    ToggleCell()             \n\
     <Btn1Motion>:  DrawCell()               \n\
     <Btn2Motion>:  UndrawCell()             \n\
     <Btn3Motion>:  ToggleCell()";
static XtActionsRec actions[] = {
    {"DrawCell", DrawCell},
    {"UndrawCell", UndrawCell},
    {"ToggleCell", ToggleCell},
};

The pointers to the actions table and translation table are placed into the class structure just like the resource list (but of course into different fields), as shown in Example 6-8. The names of the fields in the class structure are shown in the comments at right. (You'll see this again shortly in the section on class structure initialization.)

Example 6-8. Translations in the Core class record

BitmapEditClassRec bitmapEditClassRec = {
    {  /* core class part */
        .
        .
      actions,                /* actions */
      XtNumber(actions),      /* num_actions */
        .
        .
      defaultTranslations,    /* tm_table */
    },
    {  /* Primitive class part */
        .
        .
        .
    },
    {                         /* BitmapEdit class part */
      0,                      /* dummy_field */
    }
};

Note that the default translation table cannot be compiled with XtParseTranslationTable() before being placed in the class structure, since the class structure initialization occurs at compile time. Xt compiles the default translations when the class is initialized.

The translation table and actions table are discussed more fully in Chapter 8, Events, Translations, and Accelerators.

Declaring Methods

Xt calls a method when the application calls a certain Xt function. For example, when an application creates a widget with XtVaCreateManagedWidget() (or any similar call), Xt calls the initialize method of that widget class. There are separate methods called when Expose events occur, when XtVaSetValues() is called, and when XtDestroyWidget() is called. Methods are distinct from actions in that methods are called in response to application function calls and Expose events, while actions are usually called in response to user-selectable events. Methods also each have their own field in the class structure, while actions are listed in a table that is stored in one field of the class structure. Pointers to action functions are placed in an action table which is then entered into the class structure, while a pointer to each method function is entered directly into its field of the class structure.

The widget's methods should be declared near the top of the .c file. This is so that the class structure initialization can appear before the method definitions. As usual in C, they should be declared as the actual type returned, such as void or Bool.[46]They should be declared static so that the scope of these variables is limited to this source file, eliminating possible conflicts with other widget classes. Example 6-9 shows the method declarations from BitmapEdit.

Example 6-9. BitmapEdit.c: function type declarations

/* Declaration of methods */
static void Initialize();
static void Redisplay();
static void Destroy();
static void Resize();
static Boolean SetValues();
static XtGeometryResult QueryGeometry();
/* these Core methods not needed by BitmapEdit:
 *
 * static void ClassInitialize();
 * static void Realize();
 */
/* the following are functions private to BitmapEdit */
static void DrawPixmaps(), DoCell(), ChangeCellSize();
/* the following are actions of BitmapEdit */
static void DrawCell(), UndrawCell(), ToggleCell();


Initializing the Class Record

We've already shown you how to create the resource list, the translation table, and the actions table, and set into the class structure. A major part of the work is done. Now we just need to insert the method names we declared earlier into the class record, and set a few of the miscellaneous data fields.

As we saw in the discussion of the private header file, the BitmapEdit class record includes class parts for BitmapEdit itself and for each superclass of BitmapEdit, in this case Primitive and Core. To initialize the class record, each class part has to be initialized field by field. Fortunately this is easy because the majority of the fields are always initialized to the same values.

The Core Class Part

Since all widget classes are subclasses of Core, all need to initialize the Core class part. Example 6-10 shows the core part initialized as needed by the BitmapEdit widget. The actual name of each field is shown in the comment at left. Each field will be discussed after the example.

Example 6-10. BitmapEdit.c: initialization of Core class record

BitmapEditClassRec bitmapEditClassRec = {
    {        /* core_class fields */
      /* superclass */               (WidgetClass) &coreClassRec,
      /* class_name */               "BitmapEdit",
      /* widget_size */              sizeof(BitmapEditRec),
      /* class_initialize */         NULL,
      /* class_part_initialize */    NULL,
      /* class_inited */             False,
      /* initialize */               Initialize,
      /* initialize_hook */          NULL,
      /* realize  */                 XtInheritRealize,
      /* actions  */                 actions,
      /* num_actions */              XtNumber(actions),
      /* resources */                resources,
      /* num_resources */            XtNumber(resources),
      /* xrm_class */                NULLQUARK,
      /* compress_motion */          True,
      /* compress_exposure */        XtExposeCompressMultiple,
      /* compress_enterleave */      True,
      /* visible_interest */         False,
      /* destroy */                  Destroy,
      /* resize */                   Resize,
      /* expose */                   Redisplay,
      /* set_values */               SetValues,
      /* set_values_hook */          NULL,
      /* set_values_almost */        XtInheritSetValuesAlmost,
      /* get_values_hook */          NULL,
      /* accept_focus */             NULL,
      /* version */                  XtVersion,
      /* callback_private */         NULL,
      /* tm_table */                 defaultTranslations,
      /* query_geometry */           QueryGeometry,
      /* display_accelerator */      XtInheritDisplayAccelerator,
      /* extension */                NULL
    },
    {      /* Primitive class part */
      /* border_highlight */         _XtInherit,
      /* border_unhighlight */       _XtInherit,
      /* translations */             XtInheritTranslations,
      /* arm_and_activate */         NULL,
      /* syn resources */            NULL,
      /* num_syn_resources */        0,
      /* extension */                NULL,
    },
    {     /* BitmapEdit class part */
      /* extension */    0,
    },
};


If you are like most programmers, the core class part structure is the biggest structure you have ever seen! Don't worry, because many of the fields you will never have to worry about, and the rest you will gradually come to know as you need them. We will introduce all the fields here, but you are not expected to absorb all the details in a single sitting. Treat this section both as an introduction and as a summary to which you can turn back when you encounter a field you don't understand. Also, all of the methods and some of the data fields are described in more detail later in the book. These field descriptions reference the section in this book where you will find additional information about the field.

  • The superclass field is set to a pointer to the superclass's class structure. This defines which widgets this class can inherit from. For a subclass of Core this would be &coreClassRec (widgetClassRec was used in previous releases); for a subclass of Composite, &compositeClassRec; for a subclass of Constraint, &constraintClassRec, and so on.

  • The next field, class_name, contains the name that will be used to set resources by class. In other words, this is the string that you want to appear in the resource database when setting resources for all instances of this class.

  • The widget_size field is the size of the instance record. This should always be specified using sizeof with the complete instance record declaration for this class, defined in BitmapEdiP.h, as an argument. In this case the field is initialized to sizeof(BitmapEditRec). Xt uses this field to allocate memory for instance records at run time.

  • The next field, class_initialize, is the first of many pointers to widget methods. There are several issues regarding methods that require separate treatment, so we'll describe these in the next section. The complete list of methods in the Core part structure is as follows: class_initialize, class_part_init, initialize, realize, destroy, resize, expose, set_values, set_values_almost, query_geometry, and accept_focus.

  • The display_accelerator field is used in conjunction with accelerators, which are a way of redirecting events to actions in different widgets, and will be discussed in more detail in Chapter 10, Resource Management and Type Conversion.

  • The class_inited field is used internally by Xt to indicate whether this class has been initialized before. Always initialize it to False.

  • The initialize_hook, set_values_hook, and get_values_hook fields are for use in widgets that have get_values_hook fields is for use in widgets that have subparts that are not widgets. Subparts can have their own resources, and can load them from the resource databases as a widget can. This method is called immediately after the get_values method, and is for performing the same operations except on subparts. This field is described in Chapter 10, Resource Management and Type Conversion. (Most widgets that used subparts have now been converted to use non-widget objects or gadgets instead.)

  • The fields relating to resources, the default translation table, and the actions table have already been described. The only one of these fields without an obvious name is the tm_table field, in which you place the default translation table.

  • The xrm_class field is used internally by Xt, and must always be initialized to NULLQUARK. This is a fixed initialization value.

  • The compress_motion, compress_exposure, and compress_enterleave fields control the Toolkit's event filters. Basically, these filters remove events that some widgets can do without, thus improving performance. Unless a widget performs complicated drawing or tracks the pointer, compress_exposure should usually be XtExposeCompressMultiple and the other two fields should usually be True. These filters are described in Chapter 9, More Input Techniques.

  • The visible_interest field can be set to True if your widget wishes to get VisibilityNotify events, which signal changes in the visibility of your widget. Normally this is set to False, because Expose events cause the widget to be redrawn at the proper times. For some widgets, however, Expose events are not enough. If your widget draws continuously, as in a game, it can stop computing output for areas that are no longer visible. There are other cases where VisibilityNotify events are useful.

  • Xt uses the version field to check the compiled widget code against the library the widget is linked against. If you specify the constant XtVersion and it is different from the version used by the libraries, then Xt displays a run-time warning message. However, if you have intentionally designed a widget to run under more than one version of Xt, you can specify the constant XtVersionDontCheck.

  • The callback_private field is private to Xt, and you always initialize it to NULL.

  • The extension field is for later expansion of the widget while maintaining binary compatibility. When used, it is a pointer to an extension structure containing additional class structure fields.

The fields in the Primitive class part are there so that subclasses like BitmapEdit can inherit or replace certain features of Primitive. We are happy to inherit the relevant ones, and disable the rest by setting them to zero or NULL as appropriate. We inherit the border_highlight, border_unhighlight, and translations fields so that BitmapEdit can highlight itself and be a tab group. However, we turned these features off in the application that uses BitmapEdit (by setting XmNtraversalOn to False in xbitmap1) since there is not yet a keyboard interface for navigating within the bitmap.

Initializing the Core Methods

As you've just seen, there are several places for pointers to methods stored in the Core class structure. We will describe the purpose of each of these methods in the next section. But first, a word about how to set the method fields.

Fortunately, the Core class already defines some of the methods, and you can choose to inherit them instead of writing your own. In general, when you set out to write a widget, you will pick as your superclass the widget that has the most methods that you can inherit instead of writing from scratch.

Broadly speaking, there are two types of methods, self-contained methods and chained methods.

A self-contained method is one that is called alone--the methods of the same name in the widget's superclasses are not called. For example, expose is self-contained. When you write the expose method, you are writing all the drawing code for the widget. The expose method of the superclass will not be called even if it was designed to do drawing. Therefore, by writing an expose method you replace the expose method of the superclass.

A chained method is one that is not called alone--it is called either before or after all the methods of the same name in its superclasses and subclasses. Therefore, you can't replace any of the code in this method in the subclasses or superclasses, you can only add to it by writing your own.

Inheritance works differently for chained and self-contained methods. For self-contained methods, such as realize and expose, inheritance works almost as it does for default translations. When initializing a field in the class record that represents a self-contained method, you have three choices:

  • You can define these methods in your widget code by placing the name of the function in the class record and defining that function somewhere in the source file.

  • You can inherit that method from the immediate superclass by placing a special symbol beginning with XtInherit in that field in the class record.

  • You can use the first technique, but reference the superclass's method in your method. This allows you to add features to the superclass's method without having to completely copy it and modify it.

These techniques are demonstrated in Section 6.3.6, "Description of Core Methods." Chained methods, on the other hand, use one of two flavors of inheritance. Some are downward chained, which means that the Core method is called first, followed by the same method in each subclass. Other methods are upward chained, which means that the Core class method is called last. For both upward and downward chained methods, your choice is whether to specify additional code by defining your own method, or to go with what the other classes have already defined. You cannot prevent the code from the other classes from being executed.

Here is how chaining works for the initialize method in BitmapEdit. Remember that BitmapEdit is a subclass of Primitive and then Core. The Core class record contains only the Core part structure. The Primitive class record contains the Core part structure and the Primitive part structure. The BitmapEdit class record contains the Core, Primitive, and BitmapEdit part structures. The initialize method is present in the Core part structure of all three classes. When an application creates an instance of the BitmapEdit widget, Xt calls the function specified in the initialize field in the Core class record first, followed by the one in the Primitive class record, and finally the one in the BitmapEdit class record. This is an example of downward chaining.

The destroy method, on the other hand, is upward chained. That is, the destroy method in BitmapEdit would be called first, followed by the destroy method for Primitive, and finally the one for Core.

If you specify NULL in your class structure for a method that chains upward or downward, it is equivalent to specifying a function that does nothing. The function for that method of the superclasses and subclasses will still be called normally.

Table 6-2 lists which methods fall into each type of inheritance. It also shows how translations, actions, and resources are chained.

Table 6-2. Inheritance Style of Various Methods

Self-contained

Upward chained

Downward chained

class_initialize

destroy

class_part_init

realize

initialize

resize

set_values

expose

get_values_hook

accept_focus

set_values_hook

set_values_almost

initialize_hook

query_geometry

translations

actions

resources


Description of Core Methods

Here is a brief description of the purpose of each Core method, and where in this book the method will be described in detail. All these methods, except realize, can be set to NULL in the class record and the widget will still function. However, all widgets that draw into their window will also require the expose method, the initialize method, and usually the resize method. And to be good children, widgets should define a query_geometry method. The realize method is shown in Section 6.3.6, "Description of Core Methods," and the rest of the commonly-used methods immediately after that in Chapter 7, Basic Widget Methods.

This list describes all the methods, even those that are rarely used. There is a lot of detail here that you should not expect to absorb in a first reading. Like the list of Core class structure fields, treat this as an introduction and come back to it later for reference when you come across a method you don't know how to use, or if you have something you want to do and you don't remember in which method to do it.

  • initialize sets initial values for all the fields in the instance part structure. This method is responsible for checking that all public fields have been set to reasonable values. This method is downward chained, so each class's initialize method sets the initial values for its own instance part structure. This method is described in Chapter 7, Basic Widget Methods.

  • initialize_hook is called immediately after the initialize method of the same class. It is obsolete as of R4 (though still called for compatibility); its job has been added to that of initialize. It allows the widget to initialize subparts, and is used only in widgets that have subparts. Subparts have their own resources and are described in Section 10.5. initialize_hook, a downward chained method, is also described there.

  • class_initialize is called once, the first time an instance of a class is created by the application. The widget registers type converters here, if it has defined any nonstandard ones. class_initialize is self-sufficient and is described in Chapter 10, Resource Management and Type Conversion.

  • class_part_init is called once the first time an instance of a class is created by the application. It is different from class_initialize only in that it is downward chained. This method resolves inheritance of self-sufficient methods from the immediate superclass. It is needed only in classes that define their own methods in their class part (but is not present in Core, Composite, or Constraint, because Xt handles inheritance in these). This method is described in Chapter 14, Miscellaneous Toolkit Programming Techniques.

  • realize is called when the application calls XtRealizeWidget(). This method is responsible for setting window attributes and for creating the window for the widget. It is self-sufficient, and is described in Section 6.3.6, "Description of Core Methods."

  • expose redraws a widget whenever an Expose event arrives from the server (but note that Xt can coalesce consecutive Expose events to minimize the number of times it is called). This method is responsible for making Xlib calls to draw in the widget's window. The widget's instance variables are often used in the expose method to guide the drawing. This method is self-sufficient. This method is described in Chapter 7, Basic Widget Methods.

  • resize is called when the parent widget resizes the widget. It recalculates the instance variables based on the new position and size of its window, which are passed into the method. This method is self-sufficient and is described in Chapter 7, Basic Widget Methods, and in Chapter 12, Geometry Management.

  • set_values is called whenever the application calls XtSetValues() to set the resources of the widget. This method recalculates private instance variables based on the new public instance variable values. It contains similar code to the initialize method, but is called at different, and perhaps multiple, times. The set_values method is downward chained. This method is described in Chapter 7, Basic Widget Methods.

  • set_values_almost is used to process application requests to change this widget's size. This field should never be NULL. Unless you've written your own set_values_almost method, this field should be set to XtInheritSetValuesAlmost. Most classes inherit this procedure from their superclass. This method is self-contained and is described in Chapter 12, Geometry Management.

  • set_values_hook sets resource values in subparts. It is now obsolete (though still called for compatibility); its job has been added to that of set_values. This method is used only in widgets that have subparts, as described in Section 10.5. It is downward chained and is described in Chapter 10, Resource Management and Type Conversion.

  • accept_focus is NULL for most widgets (or, at least, for all the Athena widgets). When it is present, this method should set the keyboard focus to a subwidget of this widget. This would be used, for example, to allow the application to set the input focus to the Text widget within a Dialog widget. This method is invoked when the application calls XtCallAcceptFocus(). This method is self-contained and is described in Chapter 14, Miscellaneous Toolkit Programming Techniques.

  • get_values_hook is called just after get_values and is used to return the resources of subparts. This method is downward chained and is described in Chapter 10, Resource Management and Type Conversion.

  • destroy deallocates local and server memory allocated by this widget. This is called when an application destroys a widget but remains running. This method is described in Chapter 7, Basic Widget Methods.

  • query_geometry may be called when the parent widget is about to resize the widget. The method is passed the proposed new size, and is allowed to suggest a compromise size, or to agree to the change as specified. This method it self-contained. It is described in Chapter 7, Basic Widget Methods.

Initialization of the Composite and Constraint class parts, including the methods in those structures, is described in Chapter 12, Geometry Management, since this is necessary only in widgets that manage children.

Packaging the Class Record for Application Use

The final requirement of the .c file is a pointer to the class record, called bitmapEditWidgetClass, that applications use as an argument to XtCreateManagedWidget() to create instances of this widget class. This is shown in Example 6-11.

Example 6-11. BitmapEdit.c: declaring the class record pointer

WidgetClass bitmapEditWidgetClass = (WidgetClass) &bitmapEditClassRec;


bitmapEditWidgetClass is set to be a pointer to the bitmapEditClassRec, the complete class record. Remember that since the actual declaration of the class structure is in the private include file, the application cannot access class structure fields, and therefore this pointer is opaque to the application.

A Sample Method

Each method has a particular job to do, and is described in Chapter 7 or in the chapter that discusses that job. However, we'll describe the realize method now, because it is simple and demonstrates the two techniques of inheriting for self-contained fields that were described in Section 6.3.5.2, "Initializing the Core Methods." The realize method is responsible for creating the widget's window, and all widgets have a window.[47] The first technique is to inherit wholesale the method defined by the immediate superclass. The superclass may have its own realize method, or it may also inherit the method from its superclass, and so on. The Core widget's realize method creates a basic window.[48] In your subclasses of any of these widgets, you can inherit the Core realize method without modification by initializing the realize member of the Core class record to the symbolic constant XtInheritRealize, as shown in Example 6-12.

Example 6-12. BitmapEdit.c: inheriting a self-contained method

BitmapEditClassRec bitmapEditClassRec = {
    {                    /* Core class part */
        .
        .
        .
      XtInheritRealize,  /* realize */
        .
        .
        .
    },
    {  /* Primitive class part */
        .
        .
        .
    },
    {  /* BitmapEdit class part */
      0,                 / * dummy_field */
    }
};

Xt also defines symbolic constants for inheriting for every other self-contained method. They all begin with XtInherit, and in general continue with the capitalized name of the method field.

An important part of the process of creating an X window is the setting of window attributes. Therefore, a brief aside on window attributes is necessary. (You can skim the next page if you are already familiar with them.)

Window attributes are basic features of the way the server makes windows look and act. Window attributes can also be changed later if necessary, but the realize method is the place to set them initially. The Core class realize method sets some basic window attributes such as the window background and border colors or patterns, and it gets these values from Core resources. Here is a list of the window attributes that you may wish to set:

Background 

Can be a solid color, pattern, or share bits with parent.

Border 

Can be a solid color or pattern.

Bit Gravity 

Determines how partial window contents are preserved when a window is resized. This is an optimization that can save redrawing.

Backing Store 

Provides hints about when a window's contents should be preserved by the server even when the window is obscured or unmapped. This is useful for widgets that are very time-consuming to redraw. Not all servers are capable of maintaining a backing store. Check the value returned from the Xlib DoesBackingStore macro to determine whether this feature is supported on a particular screen on your server.

Saving Under 

Provides hints about whether or not the screen area beneath a window should be saved while a window such as a popup menu is in place, to save obscured windows from having to redraw themselves when the popup is removed. Not all servers can save under windows. You can find out whether this feature is supported on a particular screen with the Xlib DoesSaveUnders macro.

Colormap 

Determines which virtual colormap should be used for this window. If your widget requires a lot of specific colors--for example, to draw a shaded image, it may need to create its own virtual colormap. In that case, it would set this attribute to the ID of the created colormap. For more information, see Chapter 7, Color, in Volume One, Xlib Programming Manual.

Cursor 

Determines which cursor should be displayed when the pointer is in this window. You must create this cursor before setting this attribute. This can be done with a standard type converter, as described in Chapter 14, Miscellaneous Toolkit Programming Techniques.

It may clarify the picture to describe the features that window attributes do not affect. Setting the window attributes does not determine a window's parent, depth, or visual. These are all set when a window is created, and are permanent. The window attributes are also not used for setting the size, position, or border width of a widget. These are set using XtSetValues(). Window attributes do not determine how graphics requests are interpreted; this is the job of the graphics context (GC).

Note that some of the window attributes are not listed here because they should not be set directly by widgets, in the realize method or anywhere else. These include the event_mask, which controls which events are sent to this widget. Xt itself sets this window attribute based on the translation table. Another is override_redirect, which is handled by the Shell widget.

You could write a realize method that set your desired attributes and then called XtCreateWindow(). But this is slightly wasteful, since Core already has a realize method that creates a window and you can take advantage of it. This is the second inheritance scheme used for self-contained fields. You define your own realize method just as if you were going to write it from scratch, but then you call the superclass's realize method directly, as shown in Example 6-13.

Example 6-13. Inheriting by invoking the superclass method from a widget method

#define superclass      (&coreClassRec)
static void Realize(w, valueMask, attributes)
Widget w;
XtValueMask *valueMask;
XSetWindowAttributes *attributes;
{
    /* this is already set, but just for example */
    *valueMask |= CWBitGravity;
    attributes->bit_gravity = NorthWestGravity;
    /* use realize method from superclass */
    (*superclass->core_class.realize) (w, valueMask, attributes);
}


Xt passes to the realize method a set of window attributes based on Core instance structure values. You update these values as necessary, and then call the superclass's realize method, as shown.

See the XtRealizeProc reference page in Volume Five, X Toolkit Intrinsics Reference Manual, to find out the default settings of the window attributes as passed into the realize method.

You may wonder what happens if the superclass also inherited its realize method--does the code in Example 6-13 crash by assuming the superclass field contains a function pointer when it actually contains the constant XtInheritRealize? No. When the class is initialized, Xt reconciles all the inherited methods and resets the class record fields to be pointers to the right methods.

What is happening behind the scenes when you inherit the realize method is that the Core realize method calls the Toolkit function XtCreateWindow(), which in turn calls the Xlib function XCreateWindow(). You may need to call the Xlib routine yourself in the realize method if you want to use a visual other than the default. See the reference page for XCreateWindow() in Volume Two, Xlib Reference Manual, and Chapter 7, Color, in Volume One, Xlib Programming Manual, for details on depth, visual, and the issue of color in general.

The Public Header File--BitmapEdit.h

The public header file defines the aspects of the widget that can be accessed from the application. Public header files tend to be short. The two obligatory features of the BitmapEdit.h file are:

  • An external declaration of bitmapEditWidgetClass, the class record pointer used by applications in calls to XtCreateWidget() to create an instance of this widget class.

  • A pointer to the widget instance record, in this case BitmapEditWidget. Xt calls all methods and actions with an argument of type Widget. To access any of the fields in the instance structure, this pointer must be cast to type BitmapEditWidget. The easiest way to do this is to declare the argument of the method as type BitmapEditWidget (the other way is to cast in an assignment).

If your resource list uses XmN, XmC, or XmR constants not defined in <Xm/Xm.h> (<X11/StringDefs.h> for standard Xt applications), you must define them in the public include file. The definitions should have only a single space between the definition and the value, with no trailing comment or space. This reduces the possibility of compiler warnings from similar but not identical definitions in multiple classes. If you want, you can use a different convention for the symbol names, to distinguish your constants from those defined by Motif. For example, you could start them all with Xo.

If a widget offers any public functions, they would be declared extern here (and actually defined in the .c file). Public functions allow the application to read or change certain private data in certain more restricted or more convenient ways than is possible with resources. For example, the BitmapEdit widget provides the public function BitmapEditGetArray that applications can call to get the array of bits currently stored as private data in the widget. (This array is an attribute readable with XtGetValues(), but using this function is more convenient because it also returns the dimensions of the array.)

Example 6-14 shows BitmapEdit's public header file.

Example 6-14. BitmapEdit.h: complete public header file

#ifndef _ORABitmapEdit_h
#define _ORABitmapEdit_h
/* BitmapEdit Widget public include file */
/*
 * The public header file for the immediate superclass normally
 * must be included.  However, not in this case because the public
 * header file for Primitive is in Xm.h, which is already included
 * in all Motif applications.
 */
/* #include <Xm/Superclass.h>  */
/*
 * This public structure is used as call_data to the callback.
 * It passes the x, y position of the cell toggled (in units of
 * cells, not pixels) and a mode flag that indicates whether the
 * cell was turned on (1) or off (0).
 */
typedef struct {
    int mode;
    int newx;
    int newy;
} BitmapEditPointInfo;
#define XtNtoggleCallback "toggleCallback"
#define XtNcellSizeInPixels "cellSizeInPixels"
#define XtNpixmapWidthInCells "pixmapWidthInCells"
#define XtNpixmapHeightInCells "pixmapHeightInCells"
#define XtNcurX "curX"
#define XtNcurY "curY"
#define XtNcellArray "cellArray"
#define XtNshowEntireBitmap "showEntireBitmap"
#define XtCToggleCallback "ToggleCallback"
#define XtCCellSizeInPixels "CellSizeInPixels"
#define XtCPixmapWidthInCells "PixmapWidthInCells"
#define XtCPixmapHeightInCells "PixmapHeightInCells"
#define XtCCurX "CurX"
#define XtCCurY "CurY"
#define XtCCellArray "CellArray"
#define XtCShowEntireBitmap "ShowEntireBitmap"
extern char *BitmapEditGetArray(); /* w */
    /* Widget w; */
/* Class record constants */
extern WidgetClass bitmapEditWidgetClass;
typedef struct _BitmapEditClassRec *BitmapEditWidgetClass;
typedef struct _BitmapEditRec      *BitmapEditWidget;
#endif /* _ORABitmapEdit_h */
/* DON'T ADD STUFF AFTER THIS #endif */


The Process of Widget Writing

The process of writing a widget generally begins with the same steps. They are:

  • Copy all three files of an existing widget. If you are subclassing Core or XmPrimitive, it is generally easiest to copy the files of another subclass of Core or XmPrimitive, preferably the one most similar to the one you intend to write; pick one that has many methods defined so that you don't need to type them in. (It's easier to delete than to retype.) If you are subclassing a widget that has no existing subclasses, then you can use any set of widget template files. There is a widget in the Athena widget set called Template that is for this purpose.

  • Globally change the widget class name in the files. The fastest way to do this under UNIX is with sed, using a script similar to the following:

    s/BitmapEdit/NewName/g
    s/bitmapEdit/newName/g
    

    Place this script in the file sedscr, and run the command:

    spike% sed -f sedscr file > newfile
    

    on each file. (Or write a simple for loop to run it on multiple files.)

  • Start from the top of the .c file, and begin by writing the resource list. While writing the resource list, you may need to edit the public header file to define new resource names and classes (XmN and XmC symbols). While writing the resource list (and during the entire widget-writing process), you will also need to edit the private header file in order to add and remove instance part structure fields, as you determine a need for them while writing methods and actions. Later you will probably discover additional parameters that you want to define as resources.

  • Design the output you expect your widget to draw. Your instance part structure fields must hold all the information necessary to redraw everything when the window is exposed--add the necessary fields.

  • Design the user input you expect your widget to accept. Start with as many separate actions as you can--one for each distinguishable user input idiom. For example, BitmapEdit has three actions for changing one bitmap cell: DrawCell, UndrawCell, and ToggleCell. Even though these invoke almost identical underlying code, it is best to keep them as separate actions.

  • Design a default translation table to have these actions called in response to the appropriate events. Help in this area is available in Section 8.1.2, "Selecting the Events to Translate."

  • Write the expose method and actions. How to do this is described in the next chapter. But in summary, both the expose method and actions often draw into the widget. The expose method must always be able to redraw what the actions drew. Therefore, it usually pays to have common elements of code called by both an action and the expose method. Neither the action nor the expose method pass arguments to this common code. Instead, the actions set instance variables that are read in the expose method or common code. The instance variables act as global variables because they are available almost everywhere in the widget code.

  • Add parameters that allow your drawing code to work smoothly in any size window, and add a resize method that sets these parameters.

  • Add the initialize method to check resource values that might have been user supplied and to initialize private instance variables.

  • Add the set_values method to check application-supplied resource values and reset private instance variables based on the new resource values.

  • Declare the methods and actions you have defined, near the top of the .c file.

  • Enter all these functions and tables you have defined into the class structure initialization.

It is useful to have a simple application available for testing your widget as you develop it. One that simply creates the widget under construction and provides a Quit button is quite adequate. Then you can add code to the widget incrementally, assuring at each step that the program compiles, links, and runs without error.

As this list implies, it is a good idea to start simply by completing all the above steps for a small subset of the features you eventually want. Once you have a working widget that you can test, you can add features one at a time by going through the list again. If instead you attempt to write an ambitious widget in one pass, you will spend much longer debugging it.

Once you have learned how to write methods and actions in the next chapter, Basic Widget Methods, you should be ready to write a simple widget.

Summary of Conventions

The naming conventions for the various structure declarations in the widget source files can be confusing. Table 6-3 summarizes these conventions, using the BitmapEdit widget as an example, and describes where each type is used. This table is just to help you read the code. If you create a new class starting from an existing class, and globally change the names as described above, all of the definitions and references listed here will already be done for you.

Table 6-3. Summary of Xt Structure Name Conventions

Structure Name

Description

BitmapEditClassPart

Partial class structure typedef (usually dummy field only) used for defining BitmapEditClassRec in P.h file.

BitmapEditClassRec

Complete class structure typedef, declared extern in P.h file, used for initializing class structure in .c file.

bitmapEditClassRec

Name of complete class structure allocated in .c file, declared extern in P.h file, allocated in .c file, used as superclass in class record initialization of subclasses of this widget (.c file).

BitmapEditPart

Partial instance structure typedef, used for defining BitmapEditRec in P.h file.

BitmapEditRec

Complete instance structure typedef, also used to initialize widget_size field of class record in .c file.

_BitmapEditRec

Type of BitmapEditRec used for defining BitmapEditWidget pointer in .h file.

_BitmapEditClassRec

Type of BitmapEditClassRec used for defining BitmapEditWidgetClass |pointer in .h file.

BitmapEditWidget

Pointer to BitmapEditRec (complete instance structure), used to reference instance structure fields in .c file (passed as argument to methods).

BitmapEditWidgetClass

Pointer to _BitmapEditClassRec used to cast bitmapEditClassRec for superclass in class record initialization of subclasses of this widget (.c file).

bitmapEditWidgetClass

Of type WidgetClass address of bitmapEditClassRec used in XtVaCreateManagedWidget() calls to identify class to be created.




[43] These systems have a 14-character filename limit; the 12-character limit allows files to be placed under source control or compressed. The X Consortium keeps all filenames to a maximum of 12 characters. That limits the actual class name to 9 characters, to leave room for the P.h suffix. If the widget class name is longer than 9 characters, it is truncated in the filename. For example, the private include file for the Constraint class is ConstrainP.h (without the final t).

[44] Shell, the fourth Intrinsics-supplied widget class, is not usually subclassed by application or widget programmers.

[45] Here we use the terminology defined in Kernighan and Ritchie's The C Programming Language (Prentice-Hall 1978). A declaration announces the properties of a variable (its type, size, etc.), while a definition causes storage to be allocated. This distinction is important in the Toolkit because many things are declared and defined in separate steps.

[46] Note that you cannot declare methods using the prototype procedure symbols Xt defines such as XtExposeProc (for the expose method), since these are defined to be pointers to functions that return the right type; they are not return types. Their only use is within Xt.

[47] Gadgets don't have windows, and therefore don't have a realize method. The code to implement a gadget is described in Chapter 13, Menus, Gadgets, and Cascaded Popups.

[48] If you have the source code for Xt, you may discover that the Core widget is actually an amalgamation of several other superclasses called WindowObj, RectObj, and so on. (You might also notice the include files for these classes in /usr/include/X11.) The Core widget actually inherits the realize procedure from WindowObj. These classes are an implementation detail that will affect you only if you want to look in some of these superclasses to find the code that implements features normally attributed to Core. From application code or widget code, it is always safe to assume that Core is the top of the class tree. For gadgets, you need to know more about these “hidden” classes, so we'll discuss them in Chapter 13, Menus, Gadgets, and Cascaded Popups.