Adding C++ Components

The procedure for adding new C++ components, such as ViewKit classes or UI class components, is similar to the procedure for adding widgets. The classes are in a shared library, within the same search path. Several control files define Builder Xcessory's use of the classes.

The primary difference between the procedure for adding class components and the procedure for adding widgets is in the interface that Builder Xcessory uses to manipulate the class components. Builder Xcessory can use standard functions to manipulate all widget and gadgets because they are subclasses of the basic Xt widget classes. The mechanisms for manipulating widgets and gadgets are defined by Xt. For example, there are standard Xt functions to create widgets and to change their resource values. For Class Components, no such standards are available.

Because there is no standard way to manipulate ViewKit or UI class components, you must provide standardized interface functions such that Builder Xcessory can operate on the components. Your code then acts as a filter between Builder Xcessory and the component you are operating on.

Create a shared library that contains these functions (and possibly the component as well) and place it in a directory in the library search path used by Builder Xcessory. The names of the functions are specified in the WML specification for each component. Whenever you create an instance of the component in question, Builder Xcessory loads the shared library and uses the functions you specified in the WML specification.1


Note: You cannot add C++ components to Builder Xcessory on the SunOS 4 platform.


The following sections describe five functions you can use to manipulate class components.

Creating a Component (CreationFunction)

Regular instantiable components

An instantiable component is a component that can be created directly with a call to new. This routine can have any name, but must match the Xm-style creation prototype:

void* CreateComponent(Widget parent, char* name,
ArgList args, Cardinal arg_count);

This routine should create an instance of your component and return the instance as a void* cast. Your component must be a subclass of UIComponent (generated by Builder Xcessory) or VkComponent. The following example illustrates a sample create method:

extern "C" void* 
CreateMyComponent(Widget parent, char* name,
ArgList args, Cardinal ac)
{
MyComponent* obj = new MyComponent(name, parent);
XtSetValues(obj->baseWidget(), args, ac);
return (void*)obj;
}

Conditions for calling the routine

This routine is called under the following conditions:

  • When a new instance of the component is required.
  • When the component needs to be recreated, for whatever reason.

Abstract Components

An abstract component is a component that cannot be created directly with a call to new. The user of the class must first make a subclass of this class before instantiation with new can take place. Builder Xcessory cannot create instances of abstract classes so the integration of these classes is somewhat more involved than the regular instantiable component class shown in the previous section.

Integrating an abstract class

The first step in integrating an abstract class is to create a class for which Builder Xcessory can create an instance. This class must implement all abstract methods provided by the abstract base class. We recommend prefixing your class name with "BX" to distinguish this class as a Builder Xcessory integration class. If you are integrating an abstract class called MyAbstract, then your integration class can be called BXMyAbstract (this name is not enforced by Builder Xcessory but it is a convenient naming convention).

The create routine now creates an instance of the new subclass:

extern "C" void*
CreateMyAbstract (Widget parent, char* name,
ArgList args, Cardinal ac)
{
BXMyAbstract* obj = new BXMyAbstract (name, parent);
EditMyAbstract ((void*)obj, True, args, ac);
return (void*)obj;
}

Important: All actions performed on this component are performed on your subclass, although in Builder Xcessory it appears as though they are applied to the abstract class.


Builder Xcessory must also enforce that whenever the user creates an instance of this abstract class, a concrete subclass is first created within Builder Xcessory, which is then instantiated for the user. To inform Builder Xcessory that a class should be automatically subclassed whenever an instance is created, use the WML flag:

AutoSubclass = "MyAbstract";

The quoted string is the name of the class being subclassed. In most cases this string will be the same as the abstract class name.

When creating any abstract class it is common to give protected scope to methods that alter the state of the abstract portion of the component. This ensures that the subclass can manipulate its behavior without having to have those methods publicly available.

When creating a concrete subclass for Builder Xcessory to instantiate, those protected methods are not available to the Edit function described in "Editing a Component (AttributeFunction)" . To facilitate editing these resources (by calling the protected methods), the Builder Xcessory subclass should make all these methods public. By default, they call the abstract classes protected methods.

Managing additional constructor parameters

Because most components are composed of several widgets or components, it is unlikely that your component will have resources applied only to its base widget. As far as the user of your component is concerned, there is a set of resources presented to them in the Builder Xcessory Resource Editor.

Methods For Setting Resources

The component writer can set these resources using one of the following methods:

  • Setting the method on the class
  • Setting the constructor parameter

Setting methods on a class

Refer to "Editing a Component (AttributeFunction)" for more detailed information on resources set using a set method on the class.

Setting the constructor parameter

The only time in the integration process that you actually create an instance of an object is in the creation function. Therefore, you must manage constructor parameters within the creation function.


Note: Do not create an instance of the object in any function but the creation function.


To allow additional constructor parameters, scan the ArgList passed to the Create method for any resources that correspond to constructor parameters. The appropriate constructor can then be called based on which resources are passed.

You can recreate a component more than once with Builder Xcessory. If you have resources that are constructor parameters, any change to these attributes in Builder Xcessory causes the component to be recreated. Builder Xcessory passes all resources that have changed from their default values to the creation function. To mark a resource as a constructor parameter, add the following to the Resource definition in the WML file:

Recreate = True;

It is highly likely that constructor parameters have no corresponding get method associated with them. If this is the case, then the resource should also be specified as:

NeverVerify = True;

Example create method dealing with constructors

The following example illustrates a create method that deals with additional constructor parameters:

extern "C" void*
CreateMyComponent(Widget parent, char *name,
ArgList args, Cardinal ac)
{
// Storage for the constructor parameter value.
char *label = NULL;
// So as not to pass the constructor parameter resource
// to the Edit function, we'll allocate a new resource
// list and copy in only the resources not corresponding
// to constructor parameters.
Cardinal rsc_count = 0;
ArgList resources=(ArgList)XtMalloc(ac*sizeof(Arg));
// Now scan the list for constructor parameters.
for (int i = 0; i < ac; i++)
{
if (!strcmp("label", args[i].name))
{
label = (char *)args[i].value;
}
else
{
XtSetArg(resources[rsc_count],
  args[i].name, args[i].value);
rsc_count++;
}
}
// Create the component with the constructor parameter
// and pass the remaining attributes to the Edit function.
MyComponent *obj = new MyComponent(name, parent, label);
EditMyComponent((void *)obj, True, resources, rsc_count);
// Free the allocated memory.
XtFree((char *)resources);
// Return the new component.
return (void *)obj;
}

In this example, the resource "label"2 was removed from the ArgList before the ArgList was passed on to the Edit function. Removing the resource is a wise move, because this resource might be applied to the base widget of the component, producing unwanted results (as shown in "Editing a Component (AttributeFunction)" ).

1

Builder Xcessory calls all these functions, including widget classes. Advanced Builder Xcessory users might want to monitor what Builder Xcessory is doing with a widget class; they can provide these other functions. Normally, however, the functions are required only when integrating class components.

2

Although a literal string is used here, usually the value is defined in a header file (as with widget resources). It might appear as XzNlabel or MyNlabel.

Documentation: