ViewKit ObjectPak Callback Support

All ViewKit ObjectPak components support ObjectPak member function callbacks (also referred to simply as ObjectPak callbacks). ObjectPak callbacks are analogous to Xt-style callbacks supported by widget sets, but ObjectPak callbacks are in no way related to Xt.

The ObjectPak callback mechanism allows a component to define conditions or events, the names of which are exported as public static string constants encapsulated by that component. Any other component can register any of its member functions to be called when the condition or event associated with that callback occurs.

Unlike the case when registering ObjectPak functions for Xt-style callbacks, the functions you register for ObjectPak callbacks must be regular member functions, not static member functions.

ObjectPak callbacks are implemented by the VkCallbackObject class. VkComponent is derived from VkCallbackObject, so all ViewKit ObjectPak components can use ObjectPak callbacks. If you create a class for use with a ViewKit ObjectPak application, that class must be derived from VkCallbackObject or one of its subclasses (such as VkComponent) for you to be able to use ObjectPak callbacks with that class.

Registering ObjectPak Callbacks

The addCallback() function defined in VkCallbackObject registers a member function to be called when the condition or event associated with a callback occurs.


Caution: When registering an ObjectPak callback, remember to call the addCallback() member function of the object that triggers the callback, not the object that is registering the callback.


The format of addCallback() for registering a member function is:

void addCallback(const char *name,
VkCallbackObject *component,
VkCallbackMethod callbackFunction,
void *clientData = NULL)

where the arguments for this function are as follows:

name The name of the ObjectPak callback. You should always use the name of the public static string constant for the appropriate callback, not a literal string constant. (For example, use VkComponent::deleteCallback, not "deleteCallback".) This allows the compiler to catch any misspellings of callback names.

component A pointer to the object registering the callback function.

callbackFunction The member function to invoke when the condition or event associated with that callback occurs.

clientData A pointer to data to pass to the callback function when it is invoked.

For example, consider a member of a hypothetical Display class that instantiates another hypothetical component class, Control. The code fragment below registers a function to be invoked when the value set by the Control object changes and the Control object triggers its valueChanged callback:

Display::createControl()
{
_control = new Control(_baseWidget, "control");
_control->addCallback(Control::valueChanged, this,
(VkCallbackMethod) &Display::newValue);
}

In this example, the Display object requests that when the Control object triggers its valueChanged callback, it should call the Display::newValue() function of the Display object that created the Control object. The "(VkCallbackMethod)" cast for the callback function is required.

All ObjectPak callback functions must have the form:

void memberFunctionCallback(VkCallbackObject *obj,
void *clientData,
void *callData)

The obj argument is the component that triggered the callback, which you must cast to the correct type to allow access to members provided by that class. The clientData argument is the optional client data specified when you registered the callback, and the callData argument is optional data supplied by the component that triggered the callback.

For example, you would define the Display::newValue() callback method used above as follows:

class Display : VkComponent {
private:
void newValue(VkCallbackObject *, void *, void *);
// ...
};
void Display::newValue(VkCallbackObject* obj,
void *clientData,
void *callData);
{
Control *controlObj = (Control *) obj;
// Perform whatever operation that is needed to update
// the Display object. You can also access member
// functions from the Control object (controlObj).
// The clientData argument contains any information
// you provided as clientData when you registered
// this callback; cast it to the proper type to use it.
// If the Control object passed the new value as the
// callData argument, you can cast that to the proper
// type and use it.
}

There is also a version of addCallback() for registering non-member functions. It's syntax is:

void addCallback(const char *name,
VkCallbackFunction callbackFunction,
void *clientData = NULL)

where the arguments for this version are as follows:

name The name of the ObjectPak callback. You should always use the name of the public static string constant for the appropriate callback, not a literal string constant.

callbackFunction The non-member function to invoke when the condition or event associated with that callback occurs.

clientData A pointer to data to pass to the callback function when it is invoked.

The form of your non-member ObjectPak callback functions must be:

void functionCallback(VkCallbackObject *obj,
void *clientData,
void *callData)
For example, if you have a non-member function errorCondition():
void errorCondition(VkCallbackObject *obj,
void *clientData,
void *callData)
{
// Handle error condition
}

You could register it for an ObjectPak callback with the line such as

sample->addCallback(SampleComponent::errorCallback,
(VkCallbackFunction) &errorCondition);

The "(VkCallbackFunction)" cast for the callback function is required.

Removing ObjectPak Callbacks

The removeCallback() function provided by the VkCallbackObject class removes previously registered callbacks. The following version of removeCallback() removes a member function registered as a callback:

void removeCallback(char *name,
VkCallbackObject *otherObject,
VkCallbackMethod memberFunction,
void *clientData = NULL)

The following version of removeCallback() removes a non-member function registered as a callback:

void removeCallback(const char *name,
VkCallbackFunction callbackFunction,
void *clientData = NULL)

To remove a callback, you must provide the same arguments specified when you registered the callback. For example, the following line removes the Control callback registered in the previous section:

_control->removeCallback(Control::valueChanged, this,
(VkCallbackMethod) &Display::newValue);

The removeAllCallbacks() function removes multiple ObjectPak callbacks:

void removeAllCallbacks()
void removeAllCallbacks(VkCallbackObject *obj)

Without an argument, this function removes all callbacks from an object, regardless of which components registered the callbacks. If you provide a pointer to a component, this function removes from an object all ObjectPak callbacks set by the specified component. For example, removes all callbacks that Display object set from the Control object _control :

_control->removeAllCallbacks(this);

Defining and Triggering ObjectPak Callbacks

To create an ObjectPak callback for a component class, define a public static string constant as the name of the callback. For clarity, you should use the string's name as its value. For example, the following defines a callback, StartStopPanel::actionCallback, for the hypothetical StartStopPanel class discussed earlier in this chapter:

class StartStopPanel : public VkComponent {
public:
static const char *const actionCallback;
// ...
}
const char *const StartStopPanel::actionCallback = "actionCallback";

The callCallbacks() member function triggers a specified callback, invoking all member functions registered for that callback:

callCallbacks(const char *callback, void *callData)

Arguments

The first argument specifies the name of the callback. Use the name of the public static string constant for the appropriate callback, not a literal string constant. (For example, StartStopPanel::startCallback, not "startCallback".) This allows the compiler to catch misspelled callback names.

The second argument supplies additional data that might be required.

For example, define the StartStopPanel::start() and StartStopPanel::stop() functions to trigger the actionCallback and pass an enumerated value as call data to indicate which button the user clicked on:

enum PanelAction { START, STOP };
class StartStopPanel : public VkComponent {
public:
static const char *const actionCallback;
// ...
}
const char *const StartStopPanel::actionCallback = "actionCallback";
void StartStopPanel::start(Widget w, XtPointer callData)
{
callCallbacks(actionCallback, (void *) START);
}
void StartStopPanel::stop(Widget w, XtPointer callData)
{
callCallbacks(actionCallback, (void *) STOP);
}

Predefined ObjectPak Callbacks

The VkComponent class (and all derived classes) includes the ObjectPak callback deleteCallback, invoked when the component's destructor is called. You can use this callback to prevent dangling pointers when maintaining pointers to other components.

Example 7. Using the Predefined deleteCallback Callback

The following code illustrates how to use deleteCallback to prevent dangling pointers:

Code

class MainComponent : VkComponent {
// ...
AuxComponent *_aux;
void createAux();
void auxDeleted(VkCallbackObject *, void *, void *);
// ...
};
// ...
void MainComponent::createAux()
{
_aux = new AuxComponent(_baseWidget, "auxilliary");
_aux->addCallback(VkComponent::deleteCallback, this,
(VkCallbackMethod) &MainComponent::auxDeleted);
}
void MainComponent::auxDeleted(VkCallbackObject*,
void *, void *)
{
_aux = NULL;
}

In function MainComponent::createAux(), the MainComponent class creates an instance of the AuxComponent, and immediately registers MainComponent::auxDeleted() as a callback to invoke when the AuxComponent object is deleted.

The auxDeleted() callback definition assigns NULL to the AuxComponent object pointer. All other MainComponent functions test the value of _aux to ensure that it is not NULL before attempting to use the AuxComponent object. This eliminates the possibility that the MainComponent class will try to access the AuxComponent object after deletion, or attempting to delete it a second time.

Registering deleteCallback callbacks is necessary only to create multiple pointers to a single object. In general, you should avoid multiple pointers to the same object. However, you can use VkComponent::deleteCallback to control situations in which you must violate this guideline.