[ Previous | Next | Contents | Glossary | Home | Search ]
Motif 2.1 Widget Writer's Guide



The Widget Private Header File

The widget private header file serves the same purpose in Motif that it does for all Intrinsics-based widgets. Follow these steps to create a private header file that is compatible with other Motif widgets:

  1. Ensure that the file is included only once.

  2. Specify the appropriate header files.

  3. Allow for use by a C++ application. (This is an optional step, but is recommended.)

  4. Define inheritance macros for methods that your widget wishes to export to subclasses. Define new data types to support these macros.

  5. Declare the widget class part.

  6. Declare the full widget class record.

  7. Declare the widget instance part.

  8. Declare the full widget instance record.

  9. Declare constraint structures if you are writing a manager widget.

  10. Define the API of any private functions used by other classes.

    The following subsections detail these steps by examining the private header file for the ExmSimple widget. This file is stored online in the demos/widgets/Exm/lib directory at pathname SimpleP.h.

  11. Step 1: Ensure That the File is Included Only Once

    You must encase the contents of the file inside a conditional compilation directive, as follows:

    #ifndef _ExmSimpleP_h
    #define _ExmSimpleP_h
     ...
    #endif /* _ExmSimpleP_h */

    These lines prevent compiler errors by ensuring that the code inside the file will only be included once per compilation.

    Step 2: Include the Appropriate Header Files

    You must include the following two header files:

    1. The widget's public header file

    2. The private header file of your widget's immediate superclass

      For example, the ExmSimple widget derives from the XmPrimitive class. Therefore, the widget private header file of ExmSimple includes the following two files:

      #include <ExmSimple.h>
      #include <Xm/PrimitiveP.h>

    3. Step 3: Allow for C++ Compilation

      You should encase the remaining code of the file inside the following pair of conditional compilation directives:

      #ifdef __cplusplus
      extern "C" {
      #endif
       ...
       
      #ifdef __cplusplus
      }  /* Close scope of 'extern "C"' declaration which encloses file. */
      #endif

      If the application program using your widget is written in C++, then allowing for C++ compilation is mandatory. Otherwise, allowing for C++ compilation is merely a good idea.

      Step 4: Define Inheritable Methods

      You must define inheritance class method macros. Once defined, your widget's subclasses can inherit your widget's methods simply by specifying the inheritance macro in the appropriate field of the class record. For example, the ExmSimple widget creates the following eight inheritance macros:

      #define ExmInheritDrawVisual     ((XtWidgetProc) _XtInherit)
      #define ExmInheritDrawShadow     ((XtWidgetProc) _XtInherit)
      #define ExmInheritCreateGC       ((XtWidgetProc) _XtInherit)
      #define ExmInheritDestroyGC      ((XtWidgetProc) _XtInherit)
      #define ExmInheritSelectGC       ((ExmSelectGCProc) _XtInherit)
      #define ExmInheritCalcVisualSize ((XtWidgetProc) _XtInherit)
      #define ExmInheritCalcWidgetSize ((XtWidgetProc) _XtInherit)
      #define ExmInheritReconfigure    ((ExmReconfigureProc) _XtInherit)

      If an inheritance macro requires a new data type definition, you must provide it here. For example, ExmInheritSelectGC and ExmInheritReconfigure both require new data type definitions, as follows:

      typedef GC   (*ExmSelectGCProc)(
                              Widget);
      typedef void (*ExmReconfigureProc)(
                              WidgetClass,
                              Widget,
                              Widget) ;

      The inheritance macros should follow Motif naming conventions. The inheritance macro names should begin with the widget set prefix (in this case, Exm) followed by the word Inherit. Similarly, the new data type definitions should begin with the widget set prefix and end with Proc.

      Step 5: Define the Widget Class Part

      The widget class part defines the inheritable methods and data members of the widget. For example, following is the widget class part of the ExmSimple widget:

      typedef struct _ExmSimpleClassPart
      {
              XtWidgetProc                   draw_visual;
                 XtWidgetProc                draw_shadow;
                 XtWidgetProc                create_gc;
                 XtWidgetProc                destroy_gc;
                 ExmSelectGCProc                  select_gc;
              XtWidgetProc                   calc_visual_size;
                 XtWidgetProc                calc_widget_size;
         ExmReconfigureProc      reconfigure;
              XtPointer                         extension;
      } ExmSimpleClassPart;

      Motif strongly recommends specifying an extension field as the last field of every widget class part. Providing an extension field helps implement binary compatibility if you add methods to the widget in future releases. The extension field should have the XtPointer data type.

      Step 6: Declare the Full Class Record

      You must define the widget's full class record. This structure specifies the class parts of all widget classes in the current widget's hierarchy. The first field in the full class record specifies the class part of the top widget in the hierarchy (Core). The next field specifies the class part of the widget below the top widget, and so on. The final field specifies the class part of the current widget.

      For example, the ExmSimple widget derives from Core and XmPrimitive. Therefore, the class record structure of ExmSimple is as follows:

      typedef struct _ExmSimpleClassRec
      {
              CoreClassPart           core_class;
              XmPrimitiveClassPart    primitive_class;
              ExmSimpleClassPart      simple_class;
      } ExmSimpleClassRec;

      There are a few conventions that you should follow in the full class record:

      1. The tag name (for example, _ExmSimpleClassRec) should begin with an underscore.

      2. The data type name (for example, ExmSimpleClassRec) should have the same name as the tag except for the leading underscore.

        After defining the data type, you must now declare an externalref variable having this data type; for example:

        externalref ExmSimpleClassRec exmSimpleClassRec;

        By convention, the variable has the same name as the data type except that the first character of the variable is always lowercase.

        All manager widgets derive from Core, Composite, Constraint, and XmManager. Therefore, the class record structure of the sample manager widget ExmGrid is the following:

        typedef struct _ExmGridClassRec
        {
            CoreClassPart       core_class;
            CompositeClassPart  composite_class;
            ConstraintClassPart constraint_class;
            XmManagerClassPart  manager_class;
            ExmGridClassPart    grid_class;
        } ExmGridClassRec;
        externalref ExmGridClassRec exmGridClassRec;

      3. Step 7: Define the Widget Instance Part

        Your private header file typically declares a widget instance record. The widget instance record describes the inheritable data members of your widget. The inheritable data members consist of your widget's resources plus those variables that subclasses may need access to. For example, following is the widget instance record for the ExmSimple widget:

        typedef struct _ExmSimplePart
        {
            unsigned char           simple_shape;
            Dimension               margin_height;
            Dimension               margin_width;
            GC                      normal_gc;
            GC                      insensitive_gc;
            Dimension               pref_width;
            Dimension               pref_height;
            Boolean                 need_to_compute_width;
            Boolean                 need_to_compute_height;
            XRectangle              visual;
            Boolean                 need_to_reconfigure;
            Pixel                   saved_foreground;
        } ExmSimplePart;

        By convention, the top fields in the widget instance part are declarations of the widget's resources. The ExmSimple widget declares three new resources, so the widget instance part declares them in the top three fields.

        The fields below the resource fields declare variables that are accessible to subclasses of ExmSimple. For example, the ExmString widget is a subclass of ExmSimple, so any function in ExmString can access any of these variables. Conversely, a widget that is not a subclass of ExmSimple should not access any of these variables.

        Step 8: Declare the Full Widget Instance Record

        You must declare a full instance record for your widget private header file. The full instance record of ExmSimple appears as follows:

        typedef struct _ExmSimpleRec
        {
            CorePart            core;
            XmPrimitivePart     primitive;
            ExmSimplePart       simple;
        } ExmSimpleRec;

        All Motif manager widgets derive from Core, Composite, Constraint, and XmManager. Therefore, the full instance record of the ExmGrid sample manager widget is as follows:

        typedef struct _ExmGridRec
        {
            CorePart            core;
            CompositePart       composite;
            ConstraintPart      constraint;
            XmManagerPart       manager;
            ExmGridPart         grid;
        } ExmGridRec;

        Step 9 (Optional): Declare Constraints Structures

        Note:

        If you are writing a primitive widget, you can skip this section.

        If you are writing a manager widget, you should always define the following two structures:

        1. A constraint part structure

        2. A full constraint structure

          You should define these structures even if the widget does not currently support any constraint resources. Declaring these constraint structures now will prevent source compatibility problems if a future subclass wants to provide constraint resources.

          The following subsections detail the two constraint structures.

        3. The Constraint Part Structure

          The constraint part structure defines the constraints themselves, one constraint per field. For example, the ExmGrid widget defines two constraints, so its constraint part structure is as follows:

          typedef struct _ExmGridConstraintPart
          {
                  Dimension      grid_margin_width_within_cell;
                  Dimension      grid_margin_height_within_cell;
          } ExmGridConstraintPart, * ExmGridConstraint;

          You should follow these conventions:

          1. The tag name (for example, _ExmGridConstraintPart) should begin with an underscore.

          2. The first data type name (for example, ExmGridConstraintPart) should have the same name as the tag except without the leading underscore.

          3. The second data type name (for example, ExmGridConstraint) should be a pointer having the same name as the first data type except for the Part suffix.

          4. The Full Constraint Structure

            The full constraint structure specifies the widgets in the current widget hierarchy that define constraints. The last field in the structure specifies the current widget (assuming it defines constraints). For example, the ExmGrid widget defines the following full constraint structure:

            typedef struct _ExmGridConstraintRec
            {
                    XmManagerConstraintPart manager;
                    ExmGridConstraintPart   grid;
            } ExmGridConstraintRec, *ExmGridConstraintPtr;

            You may be wondering why we specified XmManagerConstraintPart as one of the fields. After all, the XmManager widget does not currently have any constraints. However, in order to allow for the future possibility of constraints in XmManager, the XmManager widget defines a constraint part structure. The first field of your full constraint structure should always contain XmManagerConstraintPart.

            The Constraint Access Macro

            If you are writing a manager widget that provides constraints, your private header file should define a constraint access macro. The constraint access macro should be named widget_nameCPart. For example, the ExmGrid widget defines a macro named ExmGridCPart. Here is its definition:

            #define ExmGridCPart(w) \
              (&((ExmGridConstraintPtr)
            (w)->core.constraints)->grid)

            Step 10 (Optional): Declaring Private Functions

            In the course of writing your own widgets, you can create convenience functions that could help other widget writers (or perhaps yourself when writing a subsequent widget). Prototypes for these convenience functions must appear in the widget private header file.

            Think about it this way. If you want a function to be accessible to application programmers, define it in the widget public header file. If you want a function to be inaccessible to application programmers, define it in the widget private header file.


            [ Previous | Next | Contents | Glossary | Home | Search ]