Component Resource Support

The X resource manager is a very powerful facility for customizing both applications and individual widgets. The resource manager allows the user or programmer to modify both the appearance and behavior of applications and widgets.

Resource management

ViewKit ObjectPak provides a variety of utilities to simplify resource management. Using ViewKit, you can easily perform the following tasks:

  • Set resource values for a single component or an entire class of components
  • Initialize data members using values retrieved from the resource database
  • Programmatically set default resource values for a component
  • Obtain resource values

Guidelines

Use the following guidelines to ensure proper ViewKit resource support:

  • Override each components's virtual className() member functions, returning a string that identifies the name of each component's C++ class. For example, if you create a StartStopPanel component class, override StartStopPanel::className() as follows:
    const char* StartStopPanel::className()
    {
    return "StartStopPanel";
    }

  • Provide a unique component name when instantiating each component. This string must be used as the name of the component's base widget. This ensures a unique path through the application's widget tree for each widget. Widgets within a component can have hardcoded names because they can be qualified by the name of the root of the component subtree.

Setting Resource Values by Class or Individual Component

The structure of ViewKit ObjectPak allows you to specify resource values for either an individual component or for all components of a given class.

To set a resource for an individual instance of a component, refer to the resource using the syntax:

*name*resource

In this case, "name" refers to the ObjectPak component's name that you pass as an argument to the component's constructor, and "resource" is the name of the resource. A specification of this form works for setting both widget resources and "synthetic" resources that you use to initialize data member values. ("Initializing Data Members Based on Resource Values" describes a convenience function for initializing data members from resource values.)

For example, you could set a "verbose" resource to TRUE for the instance named "status" of a hypothetical ProcessMonitor class with a resource entry such as:

*status*verbose: TRUE

To set a resource for an entire component class, refer to the resource using the syntax:

*className*resource

In this case, "className" is the name of the ObjectPak class returned by that class's className() function, and "resource" is the name of the resource. A specification of this form works for setting "synthetic" resources only, not widget resources.1

For example, you can set a "verbose" resource for all instances of the hypothetical ProcessMonitor class to TRUE with a resource entry such as:

*ProcessMonitor*verbose: TRUE

Initializing Data Members Based on Resource Values

If you want to initialize data members in a class using values in the resource database, you can call the VkComponent member function getResources():

void getResources ( const XtResourceList resources,
const int numResources )

The resources argument is a standard resource specification in the form of an XtResource list, and the numResources argument is the number of resources. You should define the XtResource list as a static data member of the class to encapsulate the resource specification with the class. You should call getResources() in the component constructor after creating your component's base widget.

getResources

getResources() retrieves the specified resources relative to the root of the component's widget subtree. For example, to set the value of a resource for a particular instance of a component, you would need to set the resource with an entry in the resource database of the form:

*name.resource: value

where name is the component's name, resource is the name of the resource, and value is the resource value.

Setting resource values for component classes

To set the value of a resource for an entire component class, you would need to set the resource with an entry in the resource database of the form:

*className.resource: value

where className is the component class name, resource is the name of the resource, and value is the resource value.

Example 5. Initializing a Data Member from the Resource Database

The following code section demonstrates the initialization of a data member, _verbose, from the resource database. A default value is specified in the XtResource structure, but the ultimate value is determined by the value of the resource named "verbose" in the resource database.

Code

// Header file: ProcessMonitor.h
#include <Vk/VkComponent.h>
#include <Xm/Frame.h>
class ProcessMonitor : public VkComponent
{
private:
static XtResource _resources[];
protected:
Boolean _verbose;
public:
ProcessMonitor(const char *, Widget);
~ProcessMonitor();
virtual const char *className();
};
// Source file: ProcessMonitor.C
#include "ProcessMonitor.h"
XtResource ProcessMonitor::_resources [] = {
{
"verbose",
"Verbose",
XmRBoolean,
sizeof ( Boolean ),
XtOffset ( ProcessMonitor *, _verbose ),
XmRString,
(XtPointer) "FALSE",
},
};
ProcessMonitor::ProcessMonitor(Widget parent, const char *name) :
VkComponent (name)
{
_baseWidget = XtVaCreateWidget ( _name, xmFrameWidgetClass,
parent, NULL ) ;
installDestroyHandler();
// Initialize members from resource database
getResources ( _resources, XtNumber(_resources) );
// ...
}

To initialize the _verbose data member to TRUE in all instances of the ProcessMonitor class, you need only to set the following resource in the resource database:

*ProcessMonitor.verbose: TRUE

To initialize _verbose to TRUE for an instance of ProcessMonitor named conversionMonitor, you could set the following resource in the resource database:

*conversionMonitor.verbose: TRUE

Setting Default Resource Values for a Component

Often, you might want to specify default resource values for a component. A common way to accomplish this is to put the resource values in an application resource file. However, this makes the component dependent on that resource file; to use that component in another application, you must remember to copy those resources into the new application's resource file. This is especially inconvenient for classes that you reuse in multiple applications.

A better method of encapsulating default resources into a component is to use a ViewKit ObjectPak facility that allows you to specify them programmatically and then merge them into the resource database during execution. Although the resources are specified programmatically, they can be overridden by applications that use the class, or by end users in resource files. However, the default values are specified by the component class and cannot be separated from the class accidentally. If you later want to change the implementation of a component class, you can also change the resource defaults when necessary, knowing that applications that use the class will receive both changes simultaneously.

The VkComponent class provides the setDefaultResources() function for storing a collection of default resources in the application's resource database. The resources are loaded with the lowest precedence, so that these resources are true defaults. They can be overridden easily in any resource file. You should call this function in the component constructor before creating the base widget in case any resources apply to the component's base widget.

The setDefaultResources() function has the following syntax:

void setDefaultResources ( const Widget w,
const String *resourceSpec )

Aruguments

The first argument is a widget. Always use the parent widget passed in the component's constructor.

The second argument is a NULL-terminated array of strings, written in the style of an X resource database specification. Specify all resources in the list relative to the root of the component's base widget, but do not include the name of the base widget. If you want to apply a resource to the base widget, simply use the name of the resource preceded by a "*" character. When resources are loaded, the value of _name is prefixed to all entries, unless that entry begins with the "-" character. As long as you use unique names for each component that you create of a given class, this results in resource specifications unique to each component. If you precede a resource value in this list with a "-" character, setDefaultResources() does not qualify the resource with the value of _name. This is useful in rare situations where you want to add global resources to the database.

Encapsulating resource sets

Declare the resource list as a static data member of the class to encapsulate the set of resources with the class.


Note: Generally, setting resources using setDefaultResources() is most appropriate for components that you plan to reuse in multiple applications. In particular, it is a good method for setting resources for widget labels and other strings that your component displays. You should not use setDefaultResources() to set widget resources, such as orientation, that you would normally set programmatically. Typically you don't need to change these resources when you use the component in different applications, and so you save memory and execution time by not using setDefaultResources() to set these resources.


Example 6. Setting a Component's Default Resource Values

The following code section builds on the StartStopPanel constructor from Example 4. to specify the default label strings "Start" and "Stop" for the button widgets.

Code

// StartStopPanel.h
class StartStopPanel: public VkComponent {
public:
StartStopPanel (const char *, Widget);
~StartStopPanel();
// ...
private:
static String _defaultResources[];
// ...
}
// StatStopPanel.C
String StartStopPanel::_defaultResources[] = {
"*start.labelString: Start",
"*stop.labelString: Stop",
NULL
};
StartStopPanel::StartStopPanel(const char *name, Widget parent) : VkComponent(name)
{
// Load class-default resources for this object before creating base widget
setDefaultResources(parent, _defaultResources );
_baseWidget = XmCreateRowColumn ( parent, _name, NULL, 0 );
installDestroyHandler();
XtVaSetValues(_baseWidget, XmNorientation, XmHORIZONTAL, NULL);
_startButton = XmCreatePushButton ( _baseWidget, "start", NULL, 0);
_stopButton = XtCreatePushButton ( _baseWidget, "stop", NULL, 0);
// ...
}

Convenience Function for Retrieving Resource Values

ViewKit ObjectPak also provides VkGetResource(), a convenience function for retrieving resource values from the resource database. VkGetResource() is not a member function of any class. You must include the header file <Vk/VkResource.h> to use VkGetResource().

VkGetResource() has two forms. The first is:

char * VkGetResource( const char * name,
const char * className )

This form returns a character string containing the value of the application resource you specify by name and class name. This function is similar to XGetDefault(3) except that this form of VkGetResource() allows you to retrieve the resource by class name whereas XGetDefault() does not.


Note: Do not attempt to change or delete the value returned by VkGetResource().


The second form of VkGetResource() is:

XtPointer VkGetResource( Widget w,
const char *name,
const char *className,
const char *desiredType,
const char *defaultValue)

This second form is similar to XtGetSubresource(3) in that it allows you to retrieve a resource relative to a specific widget. You can specify the resource as a dot-separated list of names and classes, allowing you to retrieve "virtual" sub-resources. You can also specify a target type. VkGetResource() will convert the retrieved value, or the default value if no value is retrieved, to the specified type.


Note: Do not attempt to change or delete the value returned by VkGetResource().


For example, suppose that you want to design an application for drawing an image and you want to allow the user to select various aspects of the style in which the image is drawn, such as color and fill pattern (a pixmap). You could specify each aspect of each style as a resource and retrieve the values as follows:

Widget canvas = XmCreateDrawingArea(parent, "canvas", NULL, 0);
Pixel fgOne = (Pixel) VkGetResource(canvas,
"styleOne.foreground", "Style.Foreground",
XmRString, "Black");
Pixel fgTwo = (Pixel) VkGetResource(canvas,
"styleTwo.foreground", "Style.Foreground",
XmRString, "Black");
Pixel bgOne = (Pixel) VkGetResource(canvas,
"styleOne.background", "Style.Background",
XmRString, "White");
Pixel bgTwo = (Pixel) VkGetResource(canvas,
"styleTwo.background", "Style.Background",
XmRString, "White");
Pixmap pixOne = (Pixmap) VkGetResource(canvas,
"styleOne.pixmap", "Style.Pixmap",
XmRString, "background");
Pixmap pixTwo = (Pixmap) VkGetResource(canvas,
"styleTwo.pixmap", "Style.Pixmap",
XmRString, "background");

Another common technique used in ObjectPak programming is to use a string to search for resource value and, if no resource exists, use the string as the value. You can do this easily if you pass the string to VkGetResource() as the default value. For example, consider the following code:

char *timeMsg = "Time";
// ...
char *timeTitle = (char *) VkGetResource(_baseWidget, timeMsg, "Time",
XmRString, timeMsg);

In this case, VkGetResource() searches for a resource (relative to the _baseWidget widget) whose name is specified by the character string timeMsg. If no such resource exists, VkGetResource() returns the value of timeMsg as the default value.

If you use this technique, you should not pass a string that contains a embedded spaces or newlines.