Widgets 101

by John Cwikla

Copyright © 1995,1996,1997,1998,1999,2000,2001,2002 John Cwikla, All Rights Reserved.

One of the things I do in my spare time is maintain the Widget FAQ. The Widget FAQ is a 100+ item list of various widgets of different flavors and functionality. We are all familiar with widgets, many of us use them in our work, but what is going on in there? What are these resources that I'm setting? Why do I have to call XtRealizeWidget()? In the next 2 months' columns I will answer these questions and more as we crack some light into this black-box. For any of you who have peeked at widget source, or even just the private header file of a widget, this column is for you. This months discussion will remain limited to what can be discovered by searching header files. Next month, in Widgets 201, I will dissect an actual widget, but the header files make an excellent primer.

To keep things simple (ok -- relatively simple) we explore the ancestor of most widget classes, the Core widget class. (I say most since the widget-like objects gadgets are not subclassed from the Core widget class, but instead are subclassed from RectObj -- but that's a topic for another column.) The Core widget class, coreWidgetClass, contains the basic code that most widgets need: creating windows, handling resources, setting up translation tables and actions, etc. Since most widgets need some or all of these properties, the Core is a good place to start. Plus, as we continue, you should begin to see why the Core is the only place to start.

When a programmer runs into a widget, it is usually in two places, either in a declaration like:

Widget myWidget;

or at widget creation time:

	myWidget = XtCreateManagedWidget("Widget1", coreWidgetClass, parentWidget, NULL, 0);

So what is a Widget? And what is a coreWidgetClass? And how are they related?


A Peek into the Header Files

The first two questions can be answered by taking a peek into header files. Widgets, by convention, have two header files, one that is for public use (for the programmer) and one that is for private use (for the widget writer). The Core widget's header files are Core.h and CoreP.h. Before we look into them however, we need to dive into Intrinsic.h which has the following typedefs:

typedef struct _WidgetRec *Widget;
typedef Widget *WidgetList;
typedef struct _WidgetClassRec *WidgetClass;

Whenever we are referring to a Widget we actually mean a struct _WidgetRec*. So we now know that a Widget is actually a pointer to a structure defined somewhere. I say somewhere because this typedef is taking advantage of C's opaque type declaration. The compiler knows that a Widget should be the size of the pointer, and the definition of a struct _WidgetRec can be kept elsewhere. A WidgetList is an array of Widget's, (an array of pointers to struct _WidgetRec). And a WidgetClass is another opaque type, a pointer to a struct _WidgetClassRec. Keep this last one in mind, as we will use it later on.

Now we can take a look at Core.h


/*
* $XConsortium: Core.h,v 1.10 89/12/12 19:30:32 swick Exp $
* $oHeader: Core.h,v 1.2 88/08/18 15:54:32 asente Exp $
*/

/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

#ifndef _XtCore_h
#define _XtCore_h

typedef struct _WidgetClassRec *CoreWidgetClass;
typedef struct _WidgetRec *CoreWidget;
externalref WidgetClass coreWidgetClass;

#ifndef _XT_CORE_C
externalref WidgetClass widgetClass;

#endif

#endif /* _XtCore_h */
/* DON'T ADD STUFF AFTER THIS #endif */

Besides the intimidating warning at the end of the first comment, there does not seem to be much going on here. Except of course the thing the programmer does need, the definition of the widget class coreWidgetClass of type WidgetClass. Above we discovered that a WidgetClass was an opaque pointer to some structure. The other two typedef's, CoreWidgetClass and CoreWidget I'll skip over for now, and just note quickly that they have to do with subclassing.

You now know that a Widget is an opaque pointer. You also know that coreWidgetClass is of type WidgetClass another opaque pointer. Excited yet? I didn't think so. We seem to still be outside the black box. Looking into CoreP.h will help:


/*
* $XConsortium: CoreP.h,v 1.16 89/10/04 12:22:50 swick Exp $
* $oHeader: CoreP.h,v 1.2 88/08/18 15:54:37 asente Exp $
*/

/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
and the Massachusetts Institute of Technology, Cambridge, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the names of Digital or MIT not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

******************************************************************/

#ifndef XtCoreP_h
#define XtCoreP_h

#include 

externalref int _XtInheritTranslations;

#define XtInheritTranslations  ((String) &_XtInheritTranslations)
#define XtInheritRealize ((XtRealizeProc) _XtInherit)
#define XtInheritResize ((XtWidgetProc) _XtInherit)
#define XtInheritExpose ((XtExposeProc) _XtInherit)
#define XtInheritSetValuesAlmost ((XtAlmostProc) _XtInherit)
#define XtInheritAcceptFocus ((XtAcceptFocusProc) _XtInherit)
#define XtInheritQueryGeometry ((XtGeometryHandler) _XtInherit)
#define XtInheritDisplayAccelerator ((XtStringProc) _XtInherit)

/***************************************************************
 * Widget Core Data Structures
 *
 *
 **************************************************************/

typedef struct _CorePart {
    Widget	    self;		/* pointer to widget itself	     */
    WidgetClass	    widget_class;	/* pointer to Widget's ClassRec	     */
    Widget	    parent;		/* parent widget	  	     */
    XrmName         xrm_name;		/* widget resource name quarkified   */
    Boolean         being_destroyed;	/* marked for destroy		     */
    XtCallbackList  destroy_callbacks;	/* who to call when widget destroyed */
    XtPointer       constraints;        /* constraint record                 */
    Position        x, y;		/* window position		     */
    Dimension       width, height;	/* window dimensions		     */
    Dimension       border_width;	/* window border width		     */
    Boolean         managed;            /* is widget geometry managed?       */
    Boolean	    sensitive;		/* is widget sensitive to user events*/
    Boolean         ancestor_sensitive;	/* are all ancestors sensitive?      */
    XtEventTable    event_table;	/* private to event dispatcher       */
    XtTMRec	    tm;                 /* translation management            */
    XtTranslations  accelerators;       /* accelerator translations          */
    Pixel	    border_pixel;	/* window border pixel		     */
    Pixmap          border_pixmap;	/* window border pixmap or NULL      */
    WidgetList      popup_list;         /* list of popups                    */
    Cardinal        num_popups;         /* how many popups                   */
    String          name;		/* widget resource name		     */
    Screen	    *screen;		/* window's screen		     */
    Colormap        colormap;           /* colormap                          */
    Window	    window;		/* window ID			     */
    Cardinal        depth;		/* number of planes in window        */
    Pixel	    background_pixel;	/* window background pixel	     */
    Pixmap          background_pixmap;	/* window background pixmap or NULL  */
    Boolean         visible;		/* is window mapped and not occluded?*/
    Boolean	    mapped_when_managed;/* map window if it's managed?       */
} CorePart;

typedef struct _WidgetRec {
    CorePart    core;
 } WidgetRec, CoreRec;



/******************************************************************
 *
 * Core Class Structure. Widgets, regardless of their class, will have
 * these fields.  All widgets of a given class will have the same values
 * for these fields.  Widgets of a given class may also have additional
 * common fields.  These additional fields are included in incremental
 * class structures, such as CommandClass.
 *
 * The fields that are specific to this subclass, as opposed to fields that
 * are part of the superclass, are called "subclass fields" below.  Many
 * procedures are responsible only for the subclass fields, and not for
 * any superclass fields.
 *
 ********************************************************************/

typedef struct _CoreClassPart {
    WidgetClass     superclass;		/* pointer to superclass ClassRec   */
    String          class_name;		/* widget resource class name       */
    Cardinal        widget_size;	/* size in bytes of widget record   */
    XtProc	    class_initialize;   /* class initialization proc	    */
    XtWidgetClassProc class_part_initialize; /* dynamic initialization	    */
    XtEnum          class_inited;       /* has class been initialized?      */
    XtInitProc      initialize;		/* initialize subclass fields       */
    XtArgsProc      initialize_hook;    /* notify that initialize called    */
    XtRealizeProc   realize;		/* XCreateWindow for widget	    */
    XtActionList    actions;		/* widget semantics name to proc map */
    Cardinal	    num_actions;	/* number of entries in actions     */
    XtResourceList  resources;		/* resources for subclass fields    */
    Cardinal        num_resources;      /* number of entries in resources   */
    XrmClass        xrm_class;		/* resource class quarkified	    */
    Boolean         compress_motion;    /* compress MotionNotify for widget */
    XtEnum          compress_exposure;  /* compress Expose events for widget*/
    Boolean         compress_enterleave;/* compress enter and leave events  */
    Boolean         visible_interest;   /* select for VisibilityNotify      */
    XtWidgetProc    destroy;		/* free data for subclass pointers  */
    XtWidgetProc    resize;		/* geom manager changed widget size */
    XtExposeProc    expose;		/* rediplay window		    */
    XtSetValuesFunc set_values;		/* set subclass resource values     */
    XtArgsFunc      set_values_hook;    /* notify that set_values called    */
    XtAlmostProc    set_values_almost;  /* set_values got "Almost" geo reply */
    XtArgsProc      get_values_hook;    /* notify that get_values called    */
    XtAcceptFocusProc accept_focus;     /* assign input focus to widget     */
    XtVersionType   version;	        /* version of intrinsics used	    */
    XtPointer       callback_private;   /* list of callback offsets       */
    String          tm_table;           /* state machine                    */
    XtGeometryHandler query_geometry;	/* return preferred geometry        */
    XtStringProc    display_accelerator;/* display your accelerator	    */
    XtPointer	    extension;		/* pointer to extension record      */
 } CoreClassPart;

typedef struct _WidgetClassRec {
    CoreClassPart core_class;
} WidgetClassRec, CoreClassRec;

externalref WidgetClassRec widgetClassRec;
#define coreClassRec widgetClassRec

#endif /* _XtCoreP_h */
/* DON'T ADD STUFF AFTER THIS #endif */

Ahh! Now we are excited! We come across the definition:

typedef struct _WidgetRec {
    CorePart    core;
 } WidgetRec, CoreRec;

A Widget is a pointer to this structure. Which contains another structure, the CorePart, which contains many things, some which should look familiar, x, y, width, etc. (For educational purposes, you might want to try including your favorite widget's private file into your source code and dereferencing the now unopaque Widget pointer. This is now where the CoreWidget typedef comes in. To see the innards of a core widget, you can cast a widget to a CoreWidget type, and include the CoreP.h file. If you do this, you should only examine and not change any of these values. As you will probably be bypassing some code inside the widget that sets/retrieves these values and will quite possibly break your program!)

A WidgetClass is defined as:

typedef struct _WidgetClassRec {
    CoreClassPart core_class;
} WidgetClassRec, CoreClassRec;

Once again, the innards, the CoreClassPart contains some fields that should look familiar. The only connection between the Widget and the WidgetClass seems to be in the CorePart where ther is:

    WidgetClass     widget_class;   /* pointer to Widget's ClassRec      */

At least we have found a connection, but what is it?


OO Time

A widget has two parts, its class and its instance. The class contains the things that a widget shares amongst its instances. An instance is a uniquely created object that has the properties of its class. For example, each widget has an x and y position that is independent of any other widget, an instance attribute, while the action table is a class attribute, each instance having (sharing) that action table. You may have many instances of a widget, but only one of the class. When you do:

	myWidget = XtCreateManagedWidget("Widget1", coreWidgetClass, parentWidget, NULL, 0);

myWidget is the instance. coreWidgetClass is the class.

In terms of a Core widget, everything within the CorePart belongs to the instance of a widget. Everything within the CoreClassPart belongs to the class, shared among each instance, thus the:

    WidgetClass     widget_class;   /* pointer to Widget's ClassRec      */

in the CorePart. This pointer allows each instance access to the class part of the widget.

Subclasses

Browsing other widgets' private header files you could see structures of the form:

typedef struct _CompositeRec {
    CorePart      core;
    CompositePart composite;
} CompositeRec;

typedef struct _CompositeClassRec {
     CoreClassPart      core_class;
     CompositeClassPart composite_class;
} CompositeClassRec;

Each of these structs include a part from the Core widget. This is known as subclassing. By having the Core parts in the structure, the new widget inherits all the functionality of both the Core instance and class parts. For example, the new widget will automatically have x and y position resources, since the Core instance has them. The same is true for the class record.

For each new subclass, a widget writer decides which superclass has the functionality that resembles closest what will be needed in the new widget class. The Shell widget uses:

typedef struct _ShellClassRec {
    CoreClassPart      core_class;
    CompositeClassPart composite_class;
    ShellClassPart  shell_class;
} ShellClassRec;

typedef  struct {
    CorePart    core;
    CompositePart   composite;
    ShellPart   shell;
} ShellRec, *ShellWidget;

Note two things here. First widgets are subclassed by chaining the parts of previous widget subclasses. Since the Shell is a subclass of Composite, and Composite is a subclass of Core, the Shell widget records must contain parts from both. Second, since the Xt library, where widgets are defined, is written in C, the superclass parts must be in super-to-subclass order. This is due to how the fields are stored in memory. To be able to access fields in superclass parts, they must be at the same offset in the record structure. This also means that a ShellWidget can be casted to a CompositeWidget or a CoreWidget without any problems. Since the CoreWidget is the superclass of all widgets, and a Widget is the same as a CoreWidget, any new subclass can also be passed around as a Widget.

So ends Widget 101, more properly called "Widget Header Diving." Just by looking through header files we can learn alot about widgets -- but not everything. Next month, the Core source file will be explored and the answers to such questions like, "Why do I have to call XtRealizeWidget()?" will be presented. For now, you should be able to see how Widget can be the generic type for so many different widgets, and also a first look at the differences between a widget and its class.


References

Asente, Paul J., and Swick, Ralph R., X Window System Toolkit, The Complete Programmer's Guide and Specification, Digital Press, 1990.