All ViewKit ObjectPak components are derived from the base class VkComponent, which defines a basic structure and protocol for all components. When creating your own components, you should also derive them from VkComponent or one of its subclasses.
The VkComponent class enforces certain characteristics on components and expects certain behaviors of its subclasses. These characteristics and the features provided by VkComponent are discussed in detail in the following sections. The following list summarizes the more important characteristics:
The VkComponent constructor has the following form:
VkComponent( const char *name )
The VkComponent constructor is declared protected and so can be called only from derived classes. Its primary purpose is to initialize component data members, in particular _name and _baseWidget.
Each component should have a unique name, which is used as the name of the component's base widget. The VkComponent constructor accepts a name as an argument, creates a copy of this string, and assigns the address of the copy to the _name data member.
The _baseWidget data member is the base widget of the component's widget subtree. The VkComponent constructor initializes _baseWidget to NULL.
Each derived class's constructor should take at least two arguments:
Each derived class must perform at least the following initialization steps:
For example, consider a user-defined component called StartStopPanel that implements a simple control panel containing Start and Stop buttons. This section illustrates the code for possible constructor for this class:
In this example, the StartStopPanel constructor passes the name argument to the VkComponent constructor to initialize the _name data member. The VkComponent constructor also initializes the _baseWidget data member to NULL. It then creates a RowColumn widget as the base widget to manage the other widgets in the component. The constructor uses the _name data member as the name of the base widget, uses the parent argument as the parent widget, and assigns the RowColumn widget to the _baseWidget data member. Immediately after creating the base widget, the constructor calls installDestroyHandler(). Then, it creates the two buttons as children of the base widget and manages the two child widgets.
A real constructor would then perform all other initialization needed by the class, such as setting up callbacks for the buttons and initializing any other data members that belong to the class. "Using Xt Callbacks with Components" describes how you should set up Xt callbacks when working with ViewKit ObjectPak components.
The virtual VkComponent destructor performs the following functions:
The destructor for a derived class need free only the space that was explicitly allocated by the derived class, but of course it can perform any other cleanup your class requires.
For example, if your class allocates space for a string, you should free that space in your destructor, as shown in the following code example.
Even if you don't need to perform any actions in a class destructor, you should still declare an empty one. If you don't explicitly declare a destructor, the C++ compiler creates an empty inline destructor for the class; however, because the destructor in the base class, VkCallbackObject, declares the destructor as virtual, the C++ compiler generates a warning because a virtual member function can't be inlined. The compiler then "un-inlines" the destructor and, to ensure that it's available wherever needed, puts a copy of it in every file that uses the class. Explicitly creating an empty destructor for your classes avoids this unnecessary overhead.
VkComponent provides access functions for accessing some of the class's data members.
The name() function returns the name of a component as pointed to by the _name data member. This is the same as the name that you provided in the component's constructor. The syntax of the name() function is:
The className() function returns a string identifying the name of the ObjectPak class to which the component belongs. The syntax of className() is:
All component classes should override this virtual function to return a string that identifies the name of the component's class. ObjectPak uses this string for resource handling and other support functions. The class name for the VkComponent class is "VkComponent."
For example, if you create a StartStopPanel class, you should override the StartStopPanel::className() function as follows:
The baseWidget() function returns the base widget of a component as stored in the _baseWidget data member:
Normally, components are as encapsulated as possible, so you should avoid operating directly on a component's base widget outside the class. However, certain operations might require access to a component's base widget. For example, after instantiating a component as a child of an XmForm widget, you might need to set various constraint resources, as shown in the following example code:
As a convenience, VkComponent defines a Widget operator that allows you to pass a VkComponent object directly to functions that expect a widget. By default, the operator converts the component into its base widget. However, the operator is defined as a virtual function so that derived classes can override it to return a different widget. Note that you must use an object, not a pointer to an object, because of the way operators work in C++. For example, the Widget operator makes the following code fragment equivalent to the fragment presented above:
The virtual member function show() manages the base widget of the component, displaying the entire component. The virtual member function hide() performs the inverse operation. You can call show() after calling hide() to redisplay a component. The syntax of these commands are simply:
For example, the following lines display the component panel, an instance of the StartStopPanel:
You could hide this component with the line:
If you're familiar with Xt, you can think of these functions as performing operations analogous to managing and unmanaging the widget tree; however, you shouldn't regard these functions simply as "wrappers" for the XtManageChild() and XtUnmanageChild() functions. First, these member functions show and hide an entire component, which typically consists of more than one widget. Second, other actions might be involved in showing a component. In general, the show() member function does whatever is necessary to make a component visible on the screen. You shouldn't circumvent these member functions and manage and unmanage components' base widgets directly. For example, some components might use XtMap() and XtUnmap() as well. Other components might not even create their widget subtrees until show() is called for the first time.
The VkComponent class also provides the protected virtual function afterRealizeHook(). This function is called after a component's base widget is realized, just before it's mapped for the first time. The default action is empty. You can override this function in a subclass if you want to perform actions after a component's base widget exists.
All ViewKit ObjectPak components provide the virtual member function okToQuit() to support "safe quit" mechanisms:
A component's okToQuit() function returns TRUE if it is "safe" for the application to quit. For example, you might want okToQuit() to return FALSE if a component is in the process of updating a file. By default, okToQuit() always returns TRUE; you must override okToQuit() for all components that you want to perform a check before quitting.
Usually only VkSimpleWindow and its subclasses use okToQuit(). When you call VkApp::quitYourself(), VkApp calls the okToQuit() function for all registered windows before quitting. If the okToQuit() function for any window returns FALSE, the application doesn't exit. "Exiting ViewKit ObjectPak Applications" provides more information on how to quit a ViewKit ObjectPak application, and "Additional Virtual Functions and Data Members" describes how to override VkSimpleWindow::okToQuit() to provide a "safe quit" mechanism for a window.
In some cases you might want to check one or more components contained within a window before quitting. To do so, override the okToQuit() function for that window to call the okToQuit() functions for all the desired components. Override the okToQuit() functions for the other components to perform whatever checks are necessary.
Another utility function provided by VkComponent is the static member function isComponent():
The isComponent() function applies heuristics to determine whether the pointer passed as an argument represents a valid VkComponent object. If component points to a VkComponent that has not been deleted, this function always returns TRUE; otherwise the function returns FALSE. It is possible, though highly unlikely, that this function could mistakenly identify a dangling pointer to a deleted object as a valid object. This could happen if another component were to be allocated at exactly the same address as the deleted object a pointer previously pointed to. The isComponent() function is used primarily for ObjectPak internal checking, often within assert() macros.
Callbacks pose a minor problem for C++ classes. C++ member functions have a hidden argument, which is used to pass the this pointer to the member function. This hidden argument makes ordinary member functions unusable as callbacks for Xt-based widgets. If a member function were to be called from C (as a callback), the this pointer would not be supplied and the order of the remaining arguments might be incorrect.
Fortunately, there is a simple way to handle the problem, although it requires the overhead of one additional function call. The approach is to use a regular member function to perform the desired task, and then use a static member function for the Xt callback. A static member function does not expect a this pointer when it is called. However, it is a member of a class, and as such has the same access privileges as any other member function. It can also be encapsulated so it is not visible outside the class.
The only catch is that the static member function used as a callback needs a way to access the appropriate instance of the class. This can be provided by specifying a pointer to the component as the client data when registering the callback.
Generally, you should follow these guidelines for using Xt callbacks with ViewKit ObjectPak components:
For example, the constructor presented in Example 2. for the simple control panel component described in "Component Constructors" omitted the setup of callback routines to handle the activation of the buttons.
To implement these callbacks you must perform the following tasks:
Suppose that for the control panel, you want to call the member function StartStopPanel::start() when the user clicks on the Start button, and to call StartStopPanel::stop() when the user clicks on the Stop button:
You should then define the StartStopPanel::startCallback() and StartStopPanel::stopCallback() static member functions as follows:
Finally, you need to register the static member functions as callbacks in the constructor. Remember that you must pass the this pointer as client data when registering the callbacks.
This section illustrates the code for the updated StartStopPanel constructor, which installs the Xt callbacks for the buttons.
When widgets are destroyed, dangling references--pointers to memory that once represented widgets, but which are no longer valid--are often left behind. For example, when a widget is destroyed, its children are also destroyed. Keeping track of references to these children is difficult, and you will often write a program that accidentally references the widgets in a class after the widgets have already been destroyed. Sometimes, applications try to delete a widget twice, causing the program to crash. In this situation, calling XtSetValues() or other Xt functions with a widget already deleted also occurs easily.
To protect the encapsulation of ViewKit ObjectPak classes, VkComponent provides a private static member function, widgetDestroyedCallback(), to register as an XmNdestroyCallback for the base widget so that the component can properly handle the deletion of its base widget. This callback cannot be registered automatically within the VkComponent constructor because derived classes have not yet created the base widget when the VkComponent constructor is called.
As a convenience, rather than force every derived class to install the widgetDestroyedCallback() function directly, VkComponent provides a protected installDestroyHandler() function that performs this task:
Immediately after creating a component's base widget in a derived class, call installDestroyHandler(). For example:
When you link your program with the debugging version of the ObjectPak library, a warning is issued for any class that does not install the widgetDestroyedCallback() function.
The widgetDestroyedCallback() function calls the virtual member function widgetDestroyed():
By default, widgetDestroyed() sets the component's _baseWidget data member to NULL. You can override this function in derived classes if you want to perform additional tasks in the event of widget destruction; however, you should always call the base class's widgetDestroyed() function as well.
Occasionally, you might need to remove the destroy callback installed by installDestroyHandler(). For example, the VkComponent class destructor removes the callback before destroying the widget. To do so, you can call the removeDestroyHandler() function: