A Very Quick Tutorial on the use of Wcl for Application Development
-------------------------------------------------------------------

	This will eveolve over time.  It was originally the
	response I posted to comp.windows.x to the following:

mitzel@caldera.usc.edu (Danny Mitzel) writes:
>
>I just built Wcl-2.3 and am planning on using it to prototype some 
>displays and applications.  I've looked through some of the sample
>resource files and played with the interpreters [Ari, Mri], but I
>was wondering if someone could point me to a simple program to look
>at the code side of initializing/interacting with the Wcl created
>widgets.  I appreciate any comments.

Here is exactly how I do it.

First, I make a new sub-directory under the Wcl source directory,
along side, say, Ori.  Then I copy the basic resource interpreter
and Imakefile, MakeByHand, Makefile, whatever.  I rename the interpreter,
and change the CLASS and CLIENT values in the Imakefile and MakeByHand
files.

Second, I prototype the interfaces.  I try to do as much as possible
without touching the resource interpreter, just using the Wcl provided
callbacks and actions, and the standard widgets.  This prototype
gets shown to the necessary people.  The basic dynamics should be
worked out (which menu pops up which dialog, any modality should be
set up).

You should try to keep each topLevelShell in a different resource file, and
probably each complex dialog too.  Use wclResFiles to name each of the
files in the initial resource file.

Delaying coding and concentrating on the interface makes it possible
to whip up highly complex prototypes quickly, and these can be changed
easily and dramatically due to comments you get.

Third, I look at the prototype and try to identify the objects I'll
need to implement.  As a rule of thumb, each top level shell will
be a fairly significant object, each subservient shell will be a
sub-object.  Callbacks, actions, and event handlers bound to the variou
widgets will invoke methods on the objects, and the variable information
displayed on the interface represents the external or public attributes
of the object.

If you are pragmatic and actually want to get something accomplished,
you will probably choose C to implement your objects.  If you have a
huge budget, no time constraints, no intention of maintaining the final
application, and like to do things the hard way, you can choose C++.  ;^)

Assuming you are practical, your objects get declared something like
this:  First, always do forward opaque declarations (usually in an
include file):

typedef struct _SomeObject *SomeObject;
typedef struct _AnotherObject *AnotherObject;

Then, usually with more limited scope (often file scope) define
the internals of the objects:

typedef struct _SomeObject {

    SomeObject    self;		/* not needed, but a darn good idea */
    AnotherObject another;	/* note: this is a pointer! */
    int           someAttribute, someOtherAttribute, ...;
    Widget        toDisplaySomething, toDisplaySomethingElse,
                  anEditableWidgetToProvideInformationToTheObject,
                  simpleDialog, ...;

} SomeObjectStruct;

Declare your methods.  Public ones should go in an include file.  I
use the `_' macro in WcCreate.h to provide a portable way of doing
prototyping.  ALWAYS prototype:

extern SomeObject SomeObject_Create  _(());
extern void       SomeObject_Destroy _((SomeObject));
extern void       SomeObject_Popup   _((SomeObject));

and so on.

Now, here is the key principle:  You generally want to have user
interfaces for your objects, and objects for your user interfaces.
That is why its so important to first do the interface prototype.  At
first, I used to use WcCallback resources to specifiy the application
object constructor (like SomeObject_Create) to be invoked when a new
widget tree was created.  Now, I more commonly do things the other way
around: inside my object constructor functions, I call WcCreatePopup to
get the new portion of the widget tree.  This allows the widget
creation to be deferred more easily.

As an aside, I almost always define a class for each object.  The
class keeps data and sometimes function pointers which are shared
by all instances of some type.  Some of the things nearly all my
classes keep is a list of object instances, and a list of widget
trees used by the object instances.  When a new instance is required,
the list of instances and/or the list of widget tree is searched
looking for an already existing one.  When an object and especially
when a widget tree is no longer needed, it is NOT DESTROYED, but
kept in a cache (you might have to clear cached entries at certain
times).

A second key principle: Each method must leave the object in a
consistent state.  This includes constructors: when the constructor is
finished, a complete ready-to-use object must exist.

Lets go through an example.  In this case, the application shell is
created but is never realized.  It is used as a root of the resource
heirarchy, and because Xt needs exactly one.  One of the interfaces is
distinct from the others, and there is always exactly one instance,
which can therefore be statically allocated.  The object is declared
like this:

======================================================================
typedef struct _MailCheckStruct {

    int                 initialized;    /* state of MailCheck   */
    MailCheckState      state;          /* state of shell       */

        /* If open, ring bell.
        */
    int  bellRepeat;            /* number of times to ring bell or flash icon */
    int  bellInterval;          /* milliseconds between bells or flashes      */
    int  bellCount;             /* number of times remaining to ring or flash */
    int  bellVolume;            /* -100 = nothing, 0 = base, 100 = maximum    */

...

        /* Important widgets in the interface
        */
    char*  shellName;           /* we create the top-level shell        */
    Widget shell;               /* top-level shell of the interface     */
    Widget newMail;             /* manage/unmanage                      */
    Widget newMailTime;         /* XmLabel, show time of mail arrival   */
    Widget newNotice;           /* manage/unmanage                      */
    Widget newNoticeTime;       /* XmLabel, show time of notice arrival */
    Widget noMailNorNotice;     /* replaces newMail and newNotice       */

...
} MailCheckStruct, *MailCheckStructPtr;

/* Single, statically allocated instance
*/
static MailCheckStruct check;

/* Methods
*/
static int  MailCheck_Initialize();
...
======================================================================

The MailCheck_Initialize() method is called from main() which was 
derived from Mri.c:

======================================================================
Widget       appShell;
XtAppContext app;

main ( argc, argv )
    int     argc;
    String  argv[];
{
    /*  -- Intialize Toolkit creating the application shell
    */
    appShell = XtInitialize (
        argv[0], WcAppClass( argc, argv ),      /* application name and class */
        options, XtNumber(options),             /* descr of cmd line options  */
        &argc, argv
    );
    app = XtWidgetToApplicationContext(appShell);

    /*  -- Register all Motif classes, constructors, and Xmp CBs & ACTs
    */
    XmpRegisterMotif ( app );

    /*  -- Initialize Wcl
    */
    WcInitialize( appShell );

    /* Initialize all static object instances, including event sources
    */
    ...
    if ( MailCheck_Initialize() )   exit(1);
    ...

    /*  -- and finally, enter the main application loop
    */
    XtAppMainLoop(app);
}
======================================================================

Note: if your application uses an "invisible" application shell
which has no children, then you probably need to do the following
to it:
	XtVaSetValues( invisibleAppShell,
			XtNheight,		(XtArgVal)1,
			XtNwidth,  		(XtArgVal)1,
			XtNmappedWhenManaged,	(XtArgVal)False,
			NULL );
	XtRealize( invisibleAppShell );


Here is the "constructor" for this object, which is statically allocated.

======================================================================
static int MailCheck_Initialize()
{
    int retval = 0;

    check.initialized = 1;

    /*  -- Register all MailCheck specific callbacks, actions, and widgets.
    */
    MailCheck_RegisterWithWcl();

    /* Get MailCheck shell name from appShell, and create new top-level shell.
    */
    XtGetApplicationResources(  appShell, (XtPointer)(&check),
                                initResources, XtNumber(initResources),
                                NULL, 0 );
======================================================================

Do you know what XtGetApplicationResources does in the above case?  I
am looking in the Xrm database for some application specific
resources.  The resources will look like resources specified for the
appShell, the values will be written into the "check" object.  The
initResources are declared like this:

======================================================================
#ifdef XtOffsetOf
#define FLD(n)  XtOffsetOf(MailCheckStruct,n)
#else
#define FLD(n)  XtOffset(MailCheckStructPtr,n)
#endif

/* Fetched from application shell
*/
XtResource initResources[] = {
 { "mailCheckShellName", "MailCheckShellName", XtRString, sizeof(String),
    FLD(shellName), XtRString, (XtPointer)"Mck"
 },
};
======================================================================

The default name of the shell we will create is Mck.  This default
can be overridden in a resource file like this:

	*mailCheckShellName:	AnotherName

After this call to XtGetApplicationResources(), we have the name of
the shell we will create as a popup of the application shell:
 
======================================================================
    check.shell = WcCreatePopup( appShell, check.shellName );

    /* Realize the new shell, and catch close from Mwm frame menu.
    */
    XtRealizeWidget(        check.shell );
    XtVaSetValues(          check.shell, XmNdeleteResponse, XmDO_NOTHING, NULL);
    XmpAddMwmCloseCallback( check.shell, MailCheck_CloseCB, NULL );
    XtPopup(                check.shell, XtGrabNone );

    /* Get other MailCheck resources from MailCheck shell.
    */
    XtGetApplicationResources(  check.shell, (XtPointer)(&check),
                                checkResources, XtNumber(checkResources),
                                NULL, 0 );
======================================================================

Again, we are going to the resource database to fill in fields in the
MainCheck instance.  This time, we get the resources under the instance
shell.  I.e., resource specifications like:

	*Mck.bellInterval:	100
	*Mck.newNotice:		this*newNotice

This time, we are going to cause string-to-desired-type converters
to be invoked.  In the above example, we want the *Mck.bellInterval
resource value to be stored in the MailCheck object as an integer,
and the *Mck.newNotice resource value to be converted into a
Widget handle.  The control for all of this magic is the checkResources
array, defined like this:

======================================================================
/* Fetched from MailCheck shell
*/
XtResource checkResources[] = {
 { "bellInterval", "BellInterval", XtRInt, sizeof(int),
    FLD(bellInterval), XtRString, (XtPointer)"200" /*milliseconds*/
 },
...
 { "newNotice", "NewNotice", XtRWidget, sizeof(Widget),
    FLD(newNotice), XtRString, (XtPointer)"*newNotice"
 },
...
};
======================================================================

After the call to XtGetApplicationResources( ..., checkResources, ... )
we have all the object members initialized either due to specifications
in the resource files, or by default values given in the definition
of checkResources.

Notice what this means w.r.t widgets: after a single call to 
WcCreatePopup, followed by a single call to XtGetApplicationResources(),
we can have handles to all the widgets we need.  The user interface
is fully specified in the Xrm database.  The mapping between widgets
in the interface and specific logical widgets needed by the object
are NOT hardcoded anywhere in C.  If you change the widget heirarchy
specified in the resource file, then you also (must) change the
names of the widgets you provide as special object resources.

This really, really works.

Just today, I got a change request on a very complex interface I
prototyped this spring, and developed last summer.  I had to change
the widget heirarchy to get better resizing.  I just changed the
heirarchy, and changed the names of the newly relocated and renamed
widgets, and everything worked: no recompiles, no bugs.  Like this:

    *Pak1_popup.toWidget:           this.Pak1*anKopT*orig.text
    *Pak1_popup.ccWidget:           this.Pak1*anKopT*copy.text
    *Pak1_popup.toAddrWidget:       this.Pak1*anKopT*orig*list
    *Pak1_popup.ccAddrWidget:       this.Pak1*anKopT*copy*list

Back to the example.  After fetching all the widget handles, you
really must make certain that all the widgets exist (the names
actually mapped to existing widget), and that they are of appropriate
types.  If some don't exist, or the types are wrong, its a resource
file error, and generally fatal.  Give messages, dump core, or something
so you can figure out which resources have bogus values:

======================================================================
    /* Sanity checks
    */
    if ( check.bellRepeat > 20 || check.bellRepeat < 0 )
        check.bellRepeat = 2;
    if ( check.bellInterval > 2000 || check.bellInterval < 10 )
        check.bellInterval = 200;
    if ( check.bellVolume < -100 || 100 < check.bellVolume )
        check.bellVolume = 0;

    if ( check.newMail   == (Widget)0
      || check.newNotice == (Widget)0
      || check.noMailNorNotice == (Widget)0 )
    {
        fprintf(stderr, "%s\n", check.missingWidgetsMsg );
        retval = 1;
    }

    if ( check.newMailTime   == (Widget)0
      || check.newNoticeTime == (Widget)0
      || !XtIsSubclass( check.newMailTime,   xmLabelWidgetClass )
      || !XtIsSubclass( check.newNoticeTime, xmLabelWidgetClass ) )
    {
        fprintf(stderr, "%s\n", check.needLabelsMsg );
        retval = 1;
    }
    return retval;
}
======================================================================

The end result is that after the constructor returns, the object
is fully initialized, and has handles to all the widgets it needs.

The methods which can be invoked by callbacks or actions are
exported by registering them with Wcl.  This means the widgets
in the interface can invoke the external methods of the application
object.  In the constructor, there was a call to MailCheck_RegisterWithWcl
which is defined like this:

======================================================================
static void MailCheck_RegisterWithWcl()
{
    /* -- Useful shorthand for registering things with the Wcl library */
#define RCP( name, class  ) WcRegisterClassPtr   ( app, name, class );
#define RCO( name, constr ) WcRegisterConstructor( app, name, constr );
#define RAC( name, func   ) WcRegisterAction     ( app, name, func );
#define RCB( name, func   ) WcRegisterCallback   ( app, name, func, NULL );

    /* -- register widget classes and constructors */
    /* -- Register application specific actions */
    /* -- Register application specific callbacks */
    RCB( "MailCheck_Close",     MailCheck_CloseCB       );

    /* -- Register testing specific actions and callbacks */
    RAC( "MailCheck_Test",      MailCheck_TestACT       );
    RCB( "MailCheck_Test",      MailCheck_TestCB        );
}
======================================================================

The close callback looks like this:

======================================================================
/* Close: Make Window Disappear
******************************************************************************
   Invoked due to Close from the mwm frame menu on the upper left of the
   window border, and from the Close button on the user interface.  Make
   the window disappear (not iconified).

   Widget may be protocol widget on the shell (if invoked due to mwm menu)
   or may be the Close button on the user interface.
*/
/*ARGSUSED*/
static void MailCheck_CloseCB( dontKnowWhichWidget, clientData, callData )
    Widget      dontKnowWhichWidget;
    XtPointer   clientData, callData;
{
    /* Update our local state BEFORE unmapping so an action or event
     * heandler watching for mapping events will know we closed the
     * window, it was not iconified.
     */
    check.state = invisible_state;

    /* Remove the interface from the screen
    */
    XtPopdown( check.shell );

    /* Tell the core that the interface is closed.
    */
    basChkClose();
}
======================================================================

Now, in general, you don't have statically allocated objects, but
you may have many basically identical widget trees, each representing
a different object.  Then, you need to go from the widget passed
to the callback, action, or event handler to the object instance.
Here is an example, all the Pak callbacks use the macro WIDGET_TO_PAK:

======================================================================
#define WIDGET_TO_PAK(CALLER,WID,PAK) \
  PAK = Pak_WidgetToPak( WID ); \
  if ( PAK == (Pak)0 ) \
  { \
    char  buf[BUFSIZ]; \
    char* fullName = WcWidgetToFullName( WID ); \
    sprintf( buf, \
             "PAK: %s cannot find Pak from %s!", \
             PAK, fullName); \
    /* dialog box, and to error log file */ \
    uifErrorMsg( (long)0, PB_MAERR1, "Pak", "Configuration", buf, NoErrorCB ); \
    LOGEND(2,(0,"%s\n",buf)); \
    XtFree(fullName); \
    return; \
  }

/*ARGSUSED*/
static void Pak1_ClipboardPasteEnclosureCB( widget, clientData, callData )
    Widget      widget;
    XtPointer   clientData, callData;
{
    Pak pak;

    WIDGET_TO_PAK( widget, pak )

    /* Must check this here, because we want to be able to swap an XmList
     * for the DteList, and have everything work except drag&drop
     */
    if ( XtIsSubclass( pak->pak1.enc->w, xmDteLstWidgetClass ) )
        doClipboardPaste( pak->pak1.enc->w );
}
======================================================================

The actual mapping of the widget id to the instance is done, in this
case, by Pak_WidgetToPak() which uses a Mapping Agent
(an efficient sparse array provided with Wcl, see MapAg.h) to
quickly do the lookup - about 16 instructions on a SPARC.
When an object is associated with a widget, it registers this
association with a class specific mapping agent.

Another approach at finding objects from callbacks is to use the
WcMethod mechanism, which is described in detail in the Wcl man
pages and especially the WcCreate.h include file.  Look at Mri/Test.c
and Mri/WcMethods for examples of that technique.

---------------------------------------------------------------------------
David E. Smyth			David.Smyth@ap.mchp.sni.de
Object/X Researcher		AP 154, Carl-Wery-Str 22, Munich 83 Germany
	Research Grant provided by Esprit Investments Ltd, 
	major sponsor Siemens Nixdorf Informationssysteme AG




