This worked example comes from the X-Designer Replay sources. It is an example of how to register converters for a class of widgets, in this case the Motif XmList widget class. It demonstrates how a click in an XmList widget can be converted to the selection of a particular instance of an element from that list. This is the actual mechanism used for XmList widgets by X-Designer Replay. An example of its use is illustrated in the script fragment below:
in my_shell push my_list_widget(`this line',1)
When the script is replayed, a button click is simulated at the appropriate (x,y) coordinates within the widget.
Once you have read through this example, you will be able to:
The source files for the example, together with a Makefile are provided in the $XDROOT/src/examples/replay/cvtXm directory, where $XDROOT is the location of your XDesigner installation.
The contents of this directory are listed below:
The support files provide the framework which allows your shared object to communicate with the X-Designer Replay engine. You do not need to change any of these files.
For the purposes of this example, we will be examining motif2.c which contains the XmList converters and register.c which includes code to register these converters.
The example illustrates the three stages in extending the X-Designer Replay widget set:
The three associated routines are:
What is important is the structure of the converter code and how the converters are registered and not how we have implemented the XmList converters.
This function is in motif2.c. It converts an (x,y) coordinate to a name/attribute pair, illustrating the xdsXyToNameProc interface definition structure. This function is used when X-Designer Replay is in record mode.
int xdsListXyToName( widget, x, y, namep, attrp) Widget widget; int x, y; char ** namep; char ** attrp; { extern Boolean XmStringCompare(); extern char * xdsCvtXmStringToString(); extern Boolean xdsCvtSetListError(); extern int xdsCvtListFailure(); extern Boolean xdsCvtGetXmListEntries(); extern int XmListYToPos(); static char name[255]; static char count[20]; int pos; int len = 0; int n; int instance = 1; XmString * list = (XmString*)0; XmString item; /* get the element */ if (!xdsGetXmListEntries( widget,&list, &len)) { return xdsListFailure(); } /* use XmListYToPos() to get the list element */ pos = XmListYToPos( widget, (Position)y); if (pos < 0 || pos > len) { xdsCvtSetListError(LIST_OUT_OF_BOUNDS); return xdsCvtListFailure(); } item = list[--pos]; for (n = 0; n < pos; n++) { if (XmStringCompare( item, list[n]) == True) instance++; } /* prepare the description */ (void) sprintf ( count, "%d", instance); (void) strcpy ( name, xdsXmStringToString(item)); *namep = name; *attrp = count; return 1; }
This function is also in motif2.c. It converts a name/attribute pair to an (x,y) coordinate, illustrating the xdsNameToXyProc interface definition structure. It is the complementary function to xdsListXyToName(). This function is used when X-Designer Replay is in replay mode
int xdsListNameToXy( widget, name, attr, xp, yp) Widget widget; char * name; char * attr; int * xp; int * yp; { extern char * xdsCvtXmStringToString(); extern Boolean xdsCvtSetListError(); extern int xdsCvtListFailure(); extern int xdsCvtSetListItem(); extern Boolean xdsCvtGetXmListEntries(); Position x, y; Dimension w, h; int pos; int len = 0; int n; char * s; int instance = 1; XmString * list = (XmString*)0; XmString item; if ((instance = atoi(attr)) == 0) { xdsCvtSetListError(LIST_BAD_INSTANCE); return xdsCvtListFailure(); } instance--; if (!xdsCvtGetXmListEntries( widget, &list, &len)) { xdsCvtSetListError(LIST_EMPTY_LIST); return xdsCvtListFailure(); } for ( n = 0; n < len; n++) { s = xdsCvtXmStringToString(list[n]); if (strcmp( name, s) != 0) continue; if (instance--) continue; break; } if (n == len) { xdsCvtSetListError(LIST_ELEMENT_NOT_FOUND); return xdsCvtListFailure(); } (void) xdsCvtSetListItem( widget, n+1); if (!XmListPosToBounds( widget, n+1, &x, &y, &w, &h)) { xdsCvtSetListError(LIST_OUT_OF_BOUNDS); return xdsCvtListFailure(); } *xp = x + (w/2); *yp = y + (h/2); return 1; }
The function is called in register.c. It registers the two converters in motif2.c and those for the XmScrollBar, XmScale and XmDrawingArea widgets.
void RegisterWidgets() { extern Boolean xdsRegister(); extern int xdsListNameToXy(); extern int xdsListXyToName(); extern int xdsScrollBarNameToXy(); extern int xdsScrollBarXyToName(); extern int xdsScaleNameToXy(); extern int xdsScaleXyToName(); extern int xdsDaNameToXy(); extern int xdsDaXyToName(); (void) xdsRegister( "XmList", xdsListNameToXy, xdsListXyToName); (void) xdsRegister( "XmScrollBar", xdsScrollBarNameToXy, xdsScrollBarXyToName); (void) xdsRegister( "XmScale", xdsScaleNameToXy, xdsScaleXyToName); (void) xdsRegister( "XmDrawingArea", xdsDaNameToXy, xdsDaXyToName); } void RegisterThisListWidget( Widget w; { xdsRegisterContextHandler(w, xdsListNameToXy, xdsListXyToName); }
The function is defined in xdsSetup.h and illustrates the xdsRegisterContextHandler interface definition structure.
Boolean xdsRegister( classname, name2xy, xy2name) char * classname; int_f name2xy; int_f xy2name; { bool_f bf = xdsGetRegisterFunction(); if (!bf) return False; return (*bf)( classname, name2xy, xy2name); }
The supplied Makefile is configured to build a shared object. A number of systems are supported. These can be listed by typing: make.
You only need to change the OBJECT line in the Makefile in order to build the shared object. This should be changed to:
OBJECT = cvt<classname>
where classname is the prefix of the widget class. In this example, the widget class is XmList, so we use the Xm prefix, i.e.
OBJECT=cvtXm
To create the shared object, type: make <system>. For example on a Solaris machine, you would type: make solaris. This would create a shared object called libcvtXm.so.
Once the shared object has been built, copy or link it into the directory $XDROOT/lib/xds. It will then be loaded by X-Designer Replay when required.
The example described above relates to widget classes. For custom widgets (i.e. a specific instance of a widget, such as a Motif XmDrawingArea) a mechanism for registering conversion routines is provided for you in the XDesigner distribution. This allows you to customize the behavior of X-Designer Replay in order to allow it to record and replay user actions within drawing areas and non-Motif widgets within an application.
The converter registration code is listed below:
int_f _xdsRegisterFunction = (int_f)0; Boolean xdsRegisterContextHandler( widget, name2xy, xy2name) Widget widget; int_f name2xy; int_f xy2name; { if (!_xdsRegisterFunction) return False; return (*_xdsRegisterFunction)( widget, name2xy, xy2name, True); }
The routine for registering converters is described below.
xdsRegisterContextHandler - interface definition for procedure used to register a converter
Boolean xdsRegisterContextHandler( Widget widget, xdsNameToXYProc name2xy, xdsXYToNameProc xy2name) Boolean xdsRemoveContextHandler( Widget widget)
This routine is for registering your own interpretations of events in a widget. You can call xdsRegisterContextHandler at any time after you have created the widget in your code, for example:
button1 = XmCreatePushButton ( shell1, "button1", al, ac ); xdsRegisterContextHandler(shell1, func1, func2)
When replaying, X-Designer Replay will call the func1 routine. When recording, it will call the func2 routine.
The mechanism is available to you either as a source file (client.c), or as a precompiled library module (libxdsclient.a). In the former case, it has to be compiled with your application, in the latter case re-linked with it.
It has no impact on the application itself and can be left in it with no adverse effects.
To extend the X-Designer Replay widget set:
For widget classes:
For custom widgets:
Use the contents of the supplied examples directory as a guide to writing, registering and building your converters.
See also: