## Extending the WorkShop Visual Replay Widget Set - An Example

This worked example comes from the WorkShop Visual 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 WorkShop Visual 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:

- integrate a 3rd party widget

- use the conversion mechanisms and understand the widget(name,attribute) notation

- underatand how we have implemented the XmList widget class

The source files for the example, together with a Makefile are provided in the $VISUROOT/src/examples/replay/cvtXm directory, where $VISUROOT is the location of your visu installation.

The contents of this directory are listed below:

- Makefile - allows you to build the shared object on different platforms
- README - shows how to build your shared object
- motif*.c - conversion routines
- register.c - registration routines
- xds* - support files

The support files provide the framework which allows your shared object to communicate with the WorkShop Visual 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 WorkShop Visual Replay widget set:

1. Event (x,y) to Name/Attribute pair - xdsXYToNameProc

2. Name/Attribute pair to Event (x,y) - xdsNameToXYProc

3. Registering Converters - xdsRegisterContextHandler

The three associated routines are:

1. xdsListXyToName() - which takes an (x,y) position and returns a name/attribute pair.

2. xdsListNameToXy() - which takes a name/attribute pair and returns an (x,y) position.

3. xdsRegister() - which registers a widget and its associated converters with WorkShop Visual Replay

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.

### xdsListXyToName()

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 WorkShop Visual 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;
    }

### xdsListNameToXy()

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 WorkShop Visual 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;
    }

### xdsRegister()

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);
    }

### Building the Example

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 $VISUROOT/lib/xds. It will then be loaded by WorkShop Visual Replay when required.

## Adding Converters for Custom Widgets

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 visu distribution. This allows you to customize the behavior of WorkShop Visual 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);
    }

Note - The _xdsRegisterFunction function pointer variable is set to 0. This means that the routine will always return and do nothing in your application when it is run without WorkShop Visual Replay. When you run with WorkShop Visual Replay, the variable is set to point to the Register handler which then gets called.

The routine for registering converters is described below.

### Registering Converters

#### Name

        xdsRegisterContextHandler  -   interface definition for procedure used to register a converter

#### Synopsis

        Boolean xdsRegisterContextHandler(
          Widget widget,
          xdsNameToXYProc name2xy,
          xdsXYToNameProc xy2name)
        Boolean xdsRemoveContextHandler( Widget widget)

#### Inputs

- widget - the widget that will use the routines
- name2xy - pointer to a conversion function for replay
- xy2name - pointer to a conversion function for record

#### Description

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, WorkShop Visual 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.

## Summary

To extend the WorkShop Visual Replay widget set:

- Create an (x,y) to name/attribute converter

- Create a name/attribute to (x,y) converter

For widget classes:

- Register the converters

- Create a shared object and copy or link it to the $VISUROOT/lib/xds directory

For custom widgets:

- Build your application with client.c or link it with the libxdsclient.a library

Use the contents of the supplied examples directory as a guide to writing, registering and building your converters.

### Footnotes

See also:

- xdsGetXmListEntries()
- XmListYToPos()
- xdsXmStringToString()
- XmListPosToBounds()
- Extending the WorkShop Visual Replay Widget Set
- Adding Your Own WorkShop Visual Replay Commands
- Getting the Most from WorkShop Visual Replay