The ViewKit ObjectPak Programmer's ReferenceTM is copyrighted by Integrated Computer Solutions, Inc., with all rights reserved. No part of this book may be reproduced, transcribed, stored in a retrieval system, or transmitted in any form or by any means electronic, mechanical, photocopying, recording, or otherwise, without the prior written consent of Integrated Computer Solutions, Inc.
Integrated Computer Solutions, Inc.
201 Broadway, Cambridge, MA 02139
E-mail: info@ics.com
WWW: http://www.ics.com
ViewKit ObjectPak, VKit, Builder Xcessory, Builder Xcessory Professional, BX Pro, BX, BX/Ada, GraphPak, EnhancementPak, EPak, The ICS EnhancementPak, The Motif EnhancementPak, Widget Databook, Database Xcessory, DX, DatabasePak, DBPak, ICS Motif, Ada/Motif, and Xhibition are trademarks of Integrated Computer Solutions, Inc.
IRIS ViewKit is a trademark of Silicon Graphics, Inc.
Java and Devguide are trademarks of Sun Microsystems, Inc.
Flexible License Manager is a trademark of GLOBEtrotter Software, Inc.
OPEN LOOK is a trademark of UNIX System Laboratories
OSF/Motif, UNIX, and X Window System are trademarks of the Open Group
Purify is a trademark of Pure Atria Software
X, X11, and X Window System are trademarks of X Consortium, Inc.
XRunner is a trademark of Mercury Interactive Corporation.
All other products or company names are used for identification purposes only, and may be trademarks of their respective owners.
This chapter includes the following sections:
Figure 6. shows the inheritance graph for VkApp and an auxiliary class, VkCursorList.
Figure 6. Inheritance Graph for VkApp
VkApp provides the following data access functions for retrieving data useful for your application:
The VkCheckBox class, derived from VkComponent, provides a simple method for creating check boxes. Instantiating the component creates an empty, labeled component to which you can add individual toggle buttons. VkCheckBox provides a variety of methods for determining when the user changes the state of a toggle; you can use the method most convenient for your applications. You can also programmatically change the values of the toggles.
The VkCheckBox constructor accepts the standard ObjectPak component name and parent widget arguments:
The constructor creates an empty, labeled component.
You add toggles to the check box using the VkCheckBox::addItem() function:
name is the name of the toggle item. You can specify its initial state by providing a state argument; TRUE sets the toggle and FALSE clears it.
You can also provide an Xt-style callback function, proc, that VkCheckBox activates whenever the user changes the value of the toggle; and clientData, which VkCheckBox passes as client data to the callback function. Following ObjectPak conventions as described in "Using Xt Callbacks with Components" , if you provide a callback function, you should pass the this pointer as client data so that the callback functions can retrieve the pointer, cast it to the expected component type, and call a corresponding member function. "Recognizing Changes in Check Box Toggle Values" further discusses how to use the callback function.
The VkCheckBox component creates a LabelGadget named "label" to display a label. Each toggle in the check box is implemented as a ToggleButtonGadget. The name of the gadget is the name string that you provide to addItem() when you add the toggle.
Set the XmNlabelString resource of the check box label and its toggles to set their labels:
For example, Figure 52. illustrates a simple window that contains only a check box with four toggles:
Figure 52. Example Check Box
The following example shows the code used to create this check box.
After creation, you can programmatically set the state of any toggle with the VkCheckBox::setValue() function:
index is the position of the toggle in the check box; the first toggle in the check box has an index of 0. newValue is the new state for the toggle; TRUE sets the toggle and FALSE clears it.
Note: Setting a toggle using setValue() activates the toggle's valueChanged callback. This in turn activates all of the VkCheckBox object's methods for detecting changes in toggle values as described in "Recognizing Changes in Check Box Toggle Values" on page 293.
You can set the values of multiple toggles using the VkCheckBox::setValues() function:
The Boolean array values specifies the new values for a group of toggles in the check box beginning with the first toggle. numValues specifies the number of values the values array contains.
Note: Setting toggles using setValues() activates each toggle's valueChanged callback, which activates all of the VkCheckBox object's methods for detecting changes in toggle values. Refer to "Recognizing Changes in Check Box Toggle Values" for more information on each toggle.
You can retrieve the value of a specific toggle with the VkCheckBox::getValue() function:
index is the position of the toggle in the check box; the first toggle in the check box has an index of 0. The function returns TRUE if the toggle is set and FALSE if the toggle is not set.
VkCheckBox provides different methods that you can use to determine when the user changes the value of a toggle: Xt-style callbacks, ObjectPak callbacks, and subclassing. Use whichever method is most convenient.
The first method of determining when the user changes a toggle value is to register an Xt-style callback for each toggle button. When you create a toggle with the addItem() function, you can optionally specify a callback function and client data. When the value of the toggle changes, the callback function is called with the client data you provided, and a pointer to a XmToggleButtonCallbackStruct structure as call data.
For example, the following adds a toggle named "lineNumbers" to the parametersBox check box and registers a callback function:
MyComponent::toggleLineNumbersCallback(), which must be declared as a static member function of the class MyComponent, is registered as a callback function for this toggle, and the this pointer is used as the client data. The definition of toggleLineNumbersCallback() could look like this:
The second method of determining when the user changes a toggle value is to use a ObjectPak callback. The VkCheckBox component provides the VkCheckBox::itemChanged callback. Any ViewKit ObjectPak component can register a member function to be called when the user changes a check box toggle. The VkCheckBox object provides the integer index of the toggle as client data to the callback functions.
Note: The itemChanged callback is activated when the user changes any toggle. You cannot register an ObjectPak callback for an individual toggle.
For example, the following line registers the member function MyComponent::parameterChanged() as an ObjectPak callback function to call when the user changes a toggle in the parametersBox check box:
Note this example lacks client data.
The definition of parameterChanged() could look like this:
The third method of determining when the user changes a toggle value is to create a subclass of VkCheckBox. Whenever the user changes a toggle, VkCheckBox calls the virtual function VkCheckBox::valueChanged():
index is the index of the item that changed and newValue is the current (new) value of that item. By default, valueChanged() is empty. You can override its definition in a subclass and perform processing as needed.
Derived classes have access to the following protected data members of the VkCheckBox class:
This section describes the VkAction class, which supports ViewKit ObjectPak command classes. Command classes allow you to implement actions as objects.
Nearly every user action in an interactive application can be thought of as a "command." Programmers typically implement commands as functions (callback functions, for example) that are invoked as a result of some user action. This section explores an approach in which each command in a system is modeled as an object.
Representing commands as objects has many advantages. Many commands have some state or data associated with the command, while others may involve a set of related functions. In both cases, a class allows the data and functions associated with a single logical operation to be encapsulated in one place. Because command objects are complete and self-contained, you can queue them for later execution, store them in "history" lists, re-exec might need to save some state data before executing the command. When you model commands as objects, you can store this information in data members.
The VkMenuAction class (described in "Menu Actions" ) implements the command class model to a certain extent in that it allows you to specify callback functions both for performing an action and undoing that action. But the VkMenuAction class does not provide a true command class in that it does not allow you to encapsulate any data or support functions the action might need within a discrete object. Furthermore, you must use the VkMenuAction class within a menu; it does not allow you to implement command classes activated by pushbuttons, text fields, or other input mechanisms.
ObjectPak provides two abstract classes to implement command classes in an application: VkAction and VkMenuActionObject. VkAction supports commands that do not appear in menus and VkMenuActionObject supports commands that appear in menus. VkAction does not inherit from any other classes, whereas VkMenuActionObject is a subclass of VkMenuAction, which allows you to add instances of it to a menu and manipulate them as you would any other menu item.
You can encapsulate with a subclass of VkAction or VkMenuActionObject any data or support functions required to perform an action. Additionally, commands implemented as subclasses of VkAction and VkMenuActionObject automatically register themselves with the ViewKit ObjectPak undo manager whenever you execute them.
To use command classes in ViewKit ObjectPak, you must create a separate subclass for each command in your application.
The syntax of the VkAction constructor is:
VkAction(const char *name)
Each class derived from VkAction should provide a constructor that takes at least one argument: the object's name. All derived class constructors should pass the name to the VkAction constructor to initialize the basic class data members, and then initialize any subclass-specific data members.
The syntax of the VkMenuActionObject constructor is as follows:
VkMenuActionObject(const char *name, XtPointer clientData = NULL)
Each class derived from VkMenuActionObject should provide a constructor that takes two arguments: the object's name and optional client data. All derived class constructors should pass the name and the client data to the VkMenuActionObject constructor to initialize the basic class data members, and then initialize any subclass-specific data members.
The VkMenuActionObject constructor stores the client data in the protected data member _clientData:
void *_clientData
VkMenuActionObject objects do not use the _clientData data member for callback functions. Instead it is simply an untyped pointer that you can use to pass any information your command object might need. For example, you could pass a pointer to another object, a value, a string, or any other value. You can access and manipulate _clientData from member functions of your command subclass.
Both VkAction and VkMenuActionObject have two protected pure virtual functions that you must override: doit() and undoit():
doit() performs the command class's action; undoit() undoes the action.
You can use command classes derived only from VkMenuActionObject in a ViewKit ObjectPak menu. Because VkAction is not derived from VkMenuItem, it does not provide the services required of a menu item.
You cannot specify VkMenuActionObject objects in a static menu description; you must add them dynamically using VkMenu::add(), which is described in "Creating a Menubar Using a Static Description" .
When a user selects a VkMenuActionObject command object from a menu, ObjectPak executes the command by calling the object's doit() function. ObjectPak also automatically registers the command with the undo manager.
To activate a command object that is a subclass of VkAction, call that action's execute() member function:
execute() calls the object's doit() function and registers the command with the undo manager.
Note: Do not call a command object's doit() function directly. Otherwise ObjectPak cannot register the command with the undo manager.
Set the label of a VkMenuActionObject command object as you would any other VkMenuItem item (by setting the object's XmNlabelString resource or by calling the object's setLabel() function). "Common Features of Menu Items" describes how to set the label for a menu item.
Because VkAction objects are command classes and not interface classes, they technically do not have labels; however, the undo manager requires a label that it can display after you have executed a VkAction command. Therefore, ObjectPak allows you to set the value of a "labelString" resource for VkAction objects, qualified by the object's name. For example, if you have an instance of a VkAction named "formatPara", you can set the label for this object by providing a value for the "formatPara.labelString" resource:
*formatPara: Format Paragraph
If you do not provide a value for a VkAction object's "labelString" resource, the undo manager uses the object's name as the label.
Note: The VkAction "labelString" resource is a "synthetic" resource, not a widget resource. The only way that you can set the value of this resource is through a resource file. You can't use XtSetValues() because the object contains no widgets, and you can't use setDefaultResources() because VkAction is not a subclass of VkComponent.
This section describes how to compile ViewKit ObjectPak programs.
To compile and link with the ViewKit ObjectPak libraries, you must have a C++ compiler, X11R5 and OSF/Motif 1.2 libraries.
Depending on whether or not you made links, all ViewKit ObjectPak header files appear in the following directories:
In most cases, the header file for a given class is the class name followed by ".h". For example, the header file for the VkWindow class is <Vk/VkWindow.h>. Some minor classes are grouped together into single header files. For example, the header file for the VkMenu class automatically includes the header information for every type of menu supported by ViewKit ObjectPak. These cases are noted in the text where appropriate.
You need to include OSF/Motif header files for only those OSF/Motif widgets that you explicitly use in a ViewKit ObjectPak program. ViewKit ObjectPak automatically includes any X or OSF/Motif header files required by ViewKit ObjectPak components that you use in your program.
You must link all ViewKit ObjectPak programs with the ViewKit ObjectPak library, libvk, and the OSF/Motif and X libraries. If you use an external help system with your application, you should link with an appropriate help library; otherwise, you should link with the ViewKit ObjectPak help library, libvkhelp. (Refer to Appendix C-- Using a Help System for information on using a help system with ViewKit ObjectPak applications.)
For example, to compile a file hello.C to produce the executable hello, enter:
If you are debugging a program, you might find it useful to compile your program with the debug libraries, which contain additional symbol table information.
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.
ViewKit ObjectPak provides a variety of utilities to simplify resource management. Using ViewKit, you can easily perform the following tasks:
Use the following guidelines to ensure proper ViewKit resource support:
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
If you want to initialize data members in a class using values in the resource database, you can call the VkComponent member function getResources():
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() 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.
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.
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.
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
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:
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.
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.
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.
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:
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:
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:
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:
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.
This appendix includes the following sections:
Note: This appendix helps you plan how you can expand ViewKit ObjectPak by describing some ObjectPak classes that users have contributed. These classes are not supported by ICS and their interfaces might change in future ViewKit ObjectPak releases.
This section describes the conventions used for presenting information in this book.
These type conventions and symbols are used in this guide:
C++ class names, C++ member functions, C++ data members, function names, literal command-line arguments (options and flags)
Filenames; onscreen button names; system commands; executable files; manual and book titles; glossary entries; new terms; variable command-line arguments; program variables; and variables to be supplied by the user in examples, code, and syntax statements
Onscreen text, prompts, error messages, examples, and code listings
User input, including keyboard keys (printing and nonprinting); literals supplied by the user in examples, code listings, and syntax statements
(Double quotation marks) Onscreen menu items and references in text to document section titles
(Parentheses) Follow function names; also used to surround reference page (man page) section in which a command, function, or class is described
(Angle brackets) Surround nonprinting keyboard keys, for example, <Esc>, <Ctrl-D>
Shell prompt for the superuser (root)
Shell prompt for users other than superuser
Reference pages (also known as man pages) are referred to by name and section number, in this format: name(section), where "name" is the name of a command, system call, library routine, or class; and "section" is the section number where the entry resides. For example, XtSetValues(3) refers to the XtSetValues() reference page in section 3.
The first page of each chapter provides a map of the sections included in the chapter under the heading Overview.
Most chapters also include a graph that depict the inheritance hierarchy of the classes described in that chapter. The graph appears in the section labeled Inheritance graph. For example, Figure 1. displays an example of a class inheritance graph that might appear at the beginning of a chapter.
Figure 1. Example of a Class Inheritance Graph
In these inheritance graphs, classes are presented with the base classes to the left and the derived classes to the right. Abstract classes have dashed borders and non-abstract classes have solid borders. Classes described within the chapter appear in white boxes, whereas classes described elsewhere appear in shaded boxes.
In the inheritance graph shown in Figure 1., VkComponent is an abstract base class. As indicated by its shaded box, it is not described within the chapter. The chapter describes three subclasses of VkComponent: VkDoubleBuffer, an abstract class; and VkTickMarks and VkResizer, non-abstract classes. The chapter also discusses the non-abstract class VkAlignmentGroup, which is derived from the non-abstract base class VkWidgetList.
This chapter includes the following sections:
Figure 16. shows the inheritance graph for the classes required to create and manipulate menus.
Figure 16. Inheritance Graph for ViewKit ObjectPak Menu Classes
This section describes the following three methods that you can use to create the contents of a window:
The following sections describe each method, and the advantages and disadvantages of each approach.
The preferred method of defining the contents of a window is to create the interface in the constructor of a VkSimpleWindow or VkWindow subclass. In this case, you simply create the widgets and components that you want to appear in your window in your subclass constructor. Remember that each window can have only one direct child widget as a view, so in most cases you must create a container widget and then create all other widgets and components as descendents of this direct child. Manage all widgets except the container widget, which you should leave unmanaged.
The parent widget of your view's top-level widget or component must be the window's XmMainWindow widget. You can retrieve this widget by calling the mainWindowWidget() function inherited from VkSimpleWindow. "Window Data Access Functions" discusses the mainWindowWidget() function.
Note: The _baseWidget data member for VkSimpleWindow and derived classes is the window's popup shell widget. Do not assign any other widget to this data member in a derived class.
After creating your interface, call addView():
addView() accepts as an argument either a widget or a pointer to a component, which addView() installs as the view for the window.
Note: Some OSF/Motif functions such as XmCreateScrolledText(3) create a ScrolledWindow widget and a child widget, and then return the ID of the child widget. As a convenience for using these functions, addView() can automatically determine the correct parent widget if you provide the child widget ID instead of the ScrolledWindow ID.
Example 17. shows a simple example that defines ScaleWindow, which creates a window with a RowColumn widget containing three Scale widgets. Because ScaleWindow is derived from VkSimpleWindow, it does not support a menu bar. If you required a menu bar, you would instead derive this class from VkWindow.
Note: ScaleWindow includes default resources for the Scale widget labels. This encapsulation technique is a good object-oriented practice to follow when creating reusable components in ViewKit ObjectPak. For example, if you were to extend this class by adding callback functions to the Scale widgets, you should make the callback functions members of the ScaleWindow class.
Running the scaleApp program, as illustrated in this example, displays a ScaleWindow, shown in Figure 12.
Figure 12. A Simple Example of a VkSimpleWindow Subclass
You can also create components and add them just as you would widgets. The constructor shown in Example 18. creates a VkRadioBox(3) component and installs several items.
Running the radioApp program, as illustrated in this example, displays a RadioWindow shown in Figure 13.
Figure 13. Using a Component as a Window's View
When you create your window interface in your window constructor using addView(), all setup overhead occurs when the window is instantiated. Additionally, your program allocates memory for all of the widgets created. Occasionally, you might need to instantiate a window so that your application can access some of its public functions, but not display it. If the window interface is large or complex, the time and memory consumed to create the interface is unnecessary if the user might not display it.
The ViewKit ObjectPak window classes provide a mechanism for delaying the creation of a window's interface until the window needs to be displayed. Rather than including the interface code in the window constructor, you can include the code in the definition of the protected virtual member function setUpInterface().
When you call show() to display a window, show() checks to see whether you have already added a view to the window (for example, in the window's constructor). If not, show() calls setUpInterface() to create the window's interface.
Using this approach, you do not allocate memory for the window interface until your application actually displays the window for the first time--and you never allocate the memory if your application never displays the window. Additionally, this approach reduces your application's startup time. The trade-off is that the first time you display this window, the response time might be slow because your application must create the interface before displaying the window.
The syntax of setUpInterface() is:
virtual Widget setUpInterface(Widget parent)
show() passes the main window widget to setUpInterface() for you to use as the parent of the window's widget hierarchy. You must return a widget to be added as a view. Do not call addView() from within setUpInterface().
Note: Some OSF/Motif functions such as XmCreateScrolledText(3) create a ScrolledWindow widget and a child widget, and then return the ID of the child widget. As a convenience for using these functions, setUpInterface() can automatically determine the correct parent widget if you provide the child widget ID instead of the ScrolledWindow ID.
Example 19. shows the RadioWindow example from Example 18. rewritten to use setUpInterface() instead of addView() in the constructor.
Note: This example uses the Widget operator defined by VkComponent to return the VkRadioBox's base widget in setUpInterface(). (See "VkComponent Access Functions" on page 18 for information on the Widget operator.)
If you prefer, you can explicitly call baseWidget():
return( rb->baseWidget() );
There are exceptional cases for which you may choose to directly instantiate a VkSimpleWindow or VkWindow object and use addView() to associate a view with the window. For example, if you have a complex, self-contained component and need a window simply to display the component, you might find this method acceptable. Example 20. shows a simple example of adding a component to a direct instantation of the VkSimpleWindow class.
In most cases, you should not use this technique because most windows require data and support functions that should be encapsulated by the window class to follow proper object-oriented programming style.
Occasionally, you might want to replace the view of an existing window. To do so, you must first remove the current view using the removeView() function:
void removeView()
You should not call this function unless you have previously added a view to this window. removeView() does not destroy the view; if you no longer need the view, you should destroy it.
After removing a view, you can add another view using addView().
This chapter includes the following sections:
Figure 51. shows the inheritance graph for the ViewKit ObjectPak classes used for data input.
Figure 51. Inheritance Graph for Miscellaneous ViewKit Input Classes
Widget sets such as OSF/Motif provide simple, low-level building blocks, like buttons, scrollbars, and text fields. However, to create interesting and useful applications, you must build collections of widgets that work together to perform given tasks. For example, many applications support a system of menus, which are constructed from several individual widgets. Just as the user thinks of the menu bar as a single logical component of the user interface, ViewKit ObjectPak builds abstractions that let applications deal with a "menu" rather than the individual pieces of the menu.
C++ allows you to do exactly this: to encapsulate collections of widgets and other objects as logical entities. By creating C++ classes and providing simple, convenient manipulation functions, you can avoid the complexity of creating widgets, specifying widget locations, setting resources, assigning callbacks, and other common tasks. Furthermore, for commonly used objects like menus, you can design general-purpose classes that you can easily use in many different applications.
In ViewKit ObjectPak, the general user interface classes are referred to as components. A component not only encapsulates a collection of widgets, but also defines the behavior of the overall component. ObjectPak components are designed to implement as many commonly-used features as possible. Typically, all you need to do to use a ViewKit ObjectPak component is create a subclass of the appropriate ObjectPak class and define any application-specific behavior. Furthermore, using the ObjectPak classes as a base, you can create your own library of reusable components.
This section describes VkApp protected functions and data members that you can use in a VkApp subclass, and presents an example of subclassing VkApp to parse command-line options.
You can use VkApp::parseCommandLine() to parse command line options:
You should call parseCommandLine() from within the constructor of your VkApp subclass. Provide an XrmOptionDescRec(3) table as the options argument and specify the number of entries in the table with the numOptions argument. parseCommandLine() passes these arguments to XtOpenDisplay(3), which parses the command line and loads recognized options into the application's resource database. parseCommandLine() modifies argv to remove all recognized options and returns an updated value of argc which you must use to update the value of argc. Example 16. shows an example of using parseCommandLine().
You can override VkApp::afterRealizeHook() to perform certain actions after all application windows have been realized:
virtual void afterRealizeHook()
For example, you could override afterRealizeHook() to install a colormap or set properties on the application's windows. By default, function is empty.
When subclassing VkApp, you also have access to the protected data member VkApp::_winList:
VkComponentList _winList
This data member maintains the list of the application's top-level windows. Consult the VkComponentList(3) reference page for more information on the VkComponentList class.
The most common reason for creating a subclass of VkApp is to parse the command line and set global resources based on command line options. Also, rather than use global variables, you can store data that is needed throughout your application in data members of your VkApp subclass.
The program in Example 16. creates MyApp, a VkApp subclass that recognizes a -verbose command line argument and initializes a protected data member depending on whether or not the flag is present.
Note: Example 16. uses the protected VkApp function parseCommandLine() to extract a flag if it exists. This function returns an updated value of argc that must be used to update the value of argc passed by the calling application.
This section demonstrates how to use the VkComponent class to create new components. It includes guidelines to follow when creating new components, an example of creating a new component, and an example of subclassing that component to create yet another component class.
The following is a summary of guidelines for writing components based on the VkComponent class:
To illustrate many of the features of the VkComponent base class, this chapter has shown how to build a simple class called StartStopPanel, which implements a control panel containing two buttons. Figure 4. shows the default appearance of a StartStopPanel object.
Figure 4. Default Appearance of a StartStopPanel Component
The following code section lists the full implementation of this class.
Example 8. slightly changes the StartStopPanel class from previous examples by declaring the member function StartStopPanel::start() and StartStopPanel::stop() as virtual functions. This allows you to use the StartStopPanel in two different ways: using the component directly and subclassing the component.
The simplest way to use the StartStopPanel class is to register callbacks with StartStopPanel::actionCallback. To do so, instantiate a StartStopPanel object in your application and register as a callback a member function that tests the value of the call data and performs some operation based on the value. This option avoids the additional work required to create a subclass of StartStopPanel. This technique of using a component class is most appropriate if the class already has all the functionality you require.
Example 9. shows a simple example of using the StartStopPanel directly. The PanelWindow class is a simple subclass of the VkSimpleWindow class, which is discussed in Chapter 4--ViewKit Windows. It performs the following activities in its constructor:
The following simple program displays the resulting PanelWindow object (Refer to Chapter 3--Application Class for more information about the VkApp:
Figure 5. shows the resulting PanelWindow window displayed by this program.
Figure 5. Resulting PanelWindow Window
Another way to use the StartStopPanel class is to derive a subclass and override the StartStopPanel::start() and StartStopPanel::stop() functions. This technique of using a component class is most appropriate if you need to expand or modify a component's action in some way.
Example 10. creates ControlPanel, a subclass of StartStopPanel that incorporates the features implemented in the PanelWindow class shown in Example 9.
The ControlPanel constructor uses the StartStopPanel constructor to initialize the component, creating the widgets and initializing the component's data members. Then, the ControlPanel constructor sets the orientation resource of the RowColumn widget, which is the component's base widget, to VERTICAL.
The ControlPanel class also overrides the virtual functions start() and stop() to perform the actions handled previously by the PanelWindow class. After performing these actions, the ControlPanel::start() and ControlPanel::stop() functions call StartStopPanel::start() and StartStopPanel::stop() respectively. While this may seem unnecessary for an example this simple, it helps preserve the encapsulation of the classes. You could now change the implementation of the StartStopPanel class, perhaps adding a status indicator to the component that the StartStopPanel::start() and StartStopPanel::stop() functions would update, and you would not have to change the start() and stop() function definitions in derived classes such as ControlPanel.
The following simple example creates a VkSimpleWindow object, adds a ControlPanel as the window's view, and then displays the window:
This section summarizes how to create subclasses from the ViewKit ObjectPak window classes. It describes additional virtual functions and data members not covered in previous sections, provides a window creation checklist, and shows an example of deriving a window subclass.
In addition to the functions described in previous sections, the ObjectPak window classes provide a number of virtual functions and data members that you can access from window subclasses. These functions and data allow you to perform the following tasks:
The VkComponent class provides the virtual function okToQuit() to support "safe quit" mechanisms:
virtual Boolean okToQuit()
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 does not exit. ("Exiting ViewKit ObjectPak Applications" describes VkApp::quitYourself().)
Also, the window's handleWmDeleteMessage() function calls okToQuit() when the window receives a WM_DELETE_WINDOW message from the window manager. This determines whether it is safe to delete the window. ("Window Properties and Shell Resources" describes handleWmDeleteMessage().)
To perform a test to see whether it is safe to delete a window, override the window's okToQuit() function. If you want to check one or more components contained within a window, you can override the window's okToQuit() function so that it calls the okToQuit() functions for all the desired components. You can then override the okToQuit() functions for the other components so you can perform whatever checks or shutdown actions are necessary. For example, you could post a blocking dialog asking whether the user wants to save data before quitting. ( Chapter 7--Using Dialogs describes how to use ViewKit ObjectPak dialogs.)
The ObjectPak window classes provide the following protected data members for determining the current states of a window:
IconState _iconState Contains an enumerated constant of type IconState that describes the current iconification state of the window. This variable contains OPEN if the window is not iconified, CLOSED if it is iconified, and ICON_UNKNOWN if it is in an unknown state. (Typically, the unknown state is used only internally to the VkSimpleWindow class.)
VisibleState _visibleState Contains an enumerated constant of type VisibleState that describes the current visibility state of the window. This variable contains VISIBLE if the window is visible, HIDDEN if it is not visible, and VISIBLE_UNKNOWN if it is in an unknown state. (Typically, the unknown state occurs only before you add a view to your window.)
StackingState _stackingState Contains an enumerated constant of type StackingState that describes the current stacking state of the window relative to the application. This variable contains RAISED if the window is at the top of the application's window stack, LOWERED if it is at the bottom of the window stack, and STACKING_UNKNOWN if it is in an unknown state (the state before you make any calls to raise() or lower() on this window).
If you need to perform any operations when your window changes its iconification state, you can override stateChanged():
virtual void stateChanged(IconState newState)
stateChanged() is called whenever the window's iconification state changes, whether programmatically (by calls to iconify() and open()) or through window manager interaction. Because this function is responsible for maintaining the window's state information, if you override this function in a subclass you should call the base class's stateChanged() function before performing any additional operations.
To perform certain actions only after a window exists, you can override the afterRealizeHook() function inherited from VkComponent:
virtual void afterRealizeHook()
Note: Use setUpWindowProperties() to set window properties instead of afterRealizeHook(). The difference between afterRealizeHook() and setUpWindowProperties() is that setUpWindowProperties() is guaranteed to be called before the window manager is notified of the window's existence. Because of race conditions, this might not be true of afterRealizeHook(), which is appropriate for performing actions that do not affect the window's interaction with the window manager.
Handle events not normally handled by the Xt dispatch mechanism by overriding the window's handleRawEvent() function:
virtual void handleRawEvent(XEvent *event)
As described in "ViewKit ObjectPak Event Handling" , VkApp::run() supports events not normally handled by the Xt dispatch mechanism. For example, VkApp::run() can handle client messages and events registered for non-widgets (such as a PropertyNotify event on the root window).
When run() receives an event not handled by the Xt dispatch mechanism, it calls the virtual function VkApp::handleRawEvent(), which passes the event to the handleRawEvent() function of each instance of VkSimpleWindow (or subclass) in the application. By default, these member functions are empty.
If you want a window to handle events through this mechanism, call XSelectInput(3) to select the events that you want to receive, and override handleRawEvent() in the VkSimpleWindow subclass to implement your event processing.
The ObjectPak window classes also provide the protected data member _mainWindowWidget:
Widget _mainWindowWidget
_mainWindowWidget contains the XmMainWindow widget created by the window constructor. In a subclass, you can use this data member instead of calling mainWindowWidget(), although this is not recommended.
The following lists a summary of guidelines for creating subclasses of the ObjectPak window classes:
The program in Example 22. creates ColorWindow, a VkSimpleWindow subclass that implements a simple utility for determining the results of mixing primary ink colors when printing. The user can use toggles to select any of the three primary colors--cyan, magenta, and yellow--and the window reports the resulting color.
Figure 14. shows the widget hierarchy of the ColorWindow subclass. The VkSimpleWindow constructor creates the window's popup shell and XmMainWindow widget. The ColorWindow constructor creates a Form widget to serve as the window's view. The constructor adds a VkCheckBox component as a child of the Form to provide the toggle buttons.
The constructor then adds a Frame widget as a child of the Form widget, and creates two Label gadgets as children of the Frame:
The constructor manages all of these widgets except for the top-level Form widget. (The constructor manages the VkCheckBox component by calling its show() member function.)
Figure 14. Widget Hierarchy of ColorWindow Subclass
This example illustrates a number of object-oriented techniques that you should follow when programming in ViewKit ObjectPak.
Note: All data and utility functions used by the window are declared as members of the ColorWindow class. ColorWindow uses resources to set all the text that it displays, including a set of default values. You can override these values in a resource file (for example, to provide German-language equivalents for all the strings).
The colors program displays the ColorWindow shown in Figure 15.
Figure 15. Example of the ColorWindow Window Subclass
This section summarizes how to create subclasses from the ViewKit ObjectPak window classes. It describes additional virtual functions and data members not covered in previous sections, provides a window creation checklist, and shows an example of deriving a window subclass.
In addition to the functions described in previous sections, the ObjectPak window classes provide a number of virtual functions and data members that you can access from window subclasses. These functions and data allow you to perform the following tasks:
The VkComponent class provides the virtual function okToQuit() to support "safe quit" mechanisms:
virtual Boolean okToQuit()
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 does not exit. ("Exiting ViewKit ObjectPak Applications" describes VkApp::quitYourself().)
Also, the window's handleWmDeleteMessage() function calls okToQuit() when the window receives a WM_DELETE_WINDOW message from the window manager. This determines whether it is safe to delete the window. ("Window Properties and Shell Resources" describes handleWmDeleteMessage().)
To perform a test to see whether it is safe to delete a window, override the window's okToQuit() function. If you want to check one or more components contained within a window, you can override the window's okToQuit() function so that it calls the okToQuit() functions for all the desired components. You can then override the okToQuit() functions for the other components so you can perform whatever checks or shutdown actions are necessary. For example, you could post a blocking dialog asking whether the user wants to save data before quitting. ( Chapter 7--Using Dialogs describes how to use ViewKit ObjectPak dialogs.)
The ObjectPak window classes provide the following protected data members for determining the current states of a window:
IconState _iconState Contains an enumerated constant of type IconState that describes the current iconification state of the window. This variable contains OPEN if the window is not iconified, CLOSED if it is iconified, and ICON_UNKNOWN if it is in an unknown state. (Typically, the unknown state is used only internally to the VkSimpleWindow class.)
VisibleState _visibleState Contains an enumerated constant of type VisibleState that describes the current visibility state of the window. This variable contains VISIBLE if the window is visible, HIDDEN if it is not visible, and VISIBLE_UNKNOWN if it is in an unknown state. (Typically, the unknown state occurs only before you add a view to your window.)
StackingState _stackingState Contains an enumerated constant of type StackingState that describes the current stacking state of the window relative to the application. This variable contains RAISED if the window is at the top of the application's window stack, LOWERED if it is at the bottom of the window stack, and STACKING_UNKNOWN if it is in an unknown state (the state before you make any calls to raise() or lower() on this window).
If you need to perform any operations when your window changes its iconification state, you can override stateChanged():
virtual void stateChanged(IconState newState)
stateChanged() is called whenever the window's iconification state changes, whether programmatically (by calls to iconify() and open()) or through window manager interaction. Because this function is responsible for maintaining the window's state information, if you override this function in a subclass you should call the base class's stateChanged() function before performing any additional operations.
To perform certain actions only after a window exists, you can override the afterRealizeHook() function inherited from VkComponent:
virtual void afterRealizeHook()
Note: Use setUpWindowProperties() to set window properties instead of afterRealizeHook(). The difference between afterRealizeHook() and setUpWindowProperties() is that setUpWindowProperties() is guaranteed to be called before the window manager is notified of the window's existence. Because of race conditions, this might not be true of afterRealizeHook(), which is appropriate for performing actions that do not affect the window's interaction with the window manager.
Handle events not normally handled by the Xt dispatch mechanism by overriding the window's handleRawEvent() function:
virtual void handleRawEvent(XEvent *event)
As described in "ViewKit ObjectPak Event Handling" , VkApp::run() supports events not normally handled by the Xt dispatch mechanism. For example, VkApp::run() can handle client messages and events registered for non-widgets (such as a PropertyNotify event on the root window).
When run() receives an event not handled by the Xt dispatch mechanism, it calls the virtual function VkApp::handleRawEvent(), which passes the event to the handleRawEvent() function of each instance of VkSimpleWindow (or subclass) in the application. By default, these member functions are empty.
If you want a window to handle events through this mechanism, call XSelectInput(3) to select the events that you want to receive, and override handleRawEvent() in the VkSimpleWindow subclass to implement your event processing.
The ObjectPak window classes also provide the protected data member _mainWindowWidget:
Widget _mainWindowWidget
_mainWindowWidget contains the XmMainWindow widget created by the window constructor. In a subclass, you can use this data member instead of calling mainWindowWidget(), although this is not recommended.
The following lists a summary of guidelines for creating subclasses of the ObjectPak window classes:
The program in Example 22. creates ColorWindow, a VkSimpleWindow subclass that implements a simple utility for determining the results of mixing primary ink colors when printing. The user can use toggles to select any of the three primary colors--cyan, magenta, and yellow--and the window reports the resulting color.
Figure 14. shows the widget hierarchy of the ColorWindow subclass. The VkSimpleWindow constructor creates the window's popup shell and XmMainWindow widget. The ColorWindow constructor creates a Form widget to serve as the window's view. The constructor adds a VkCheckBox component as a child of the Form to provide the toggle buttons.
The constructor then adds a Frame widget as a child of the Form widget, and creates two Label gadgets as children of the Frame:
The constructor manages all of these widgets except for the top-level Form widget. (The constructor manages the VkCheckBox component by calling its show() member function.)
Figure 14. Widget Hierarchy of ColorWindow Subclass
This example illustrates a number of object-oriented techniques that you should follow when programming in ViewKit ObjectPak.
Note: All data and utility functions used by the window are declared as members of the ColorWindow class. ColorWindow uses resources to set all the text that it displays, including a set of default values. You can override these values in a resource file (for example, to provide German-language equivalents for all the strings).
The colors program displays the ColorWindow shown in Figure 15.
Figure 15. Example of the ColorWindow Window Subclass
Creating an instance of the VkMsgApp class opens a ToolTalk connection and sets up all resources needed to send and receive ToolTalk messages. Remember to use the VkMsgApp class in your application instead of a VkApp object if you want ToolTalk support for your application. The syntax of the VkMsgApp constructor is:
The first five arguments are the same as those that you can provide to the VkApp constructor. The ptid argument specifies a process type. It defaults to NULL which indicates no process type. You need to provide a ptid argument only if this application is autostarted (see "Registering Services for Autostart" ). sessid specifies a session to join. If you don't provide a value, the process joins the default session.
The noProtocol argument determines whether your application automatically provides support for handling "Lower," "Raise," and "Quit" messages. If this value is FALSE (the default), your application calls VkApp::iconify() upon receiving a "Lower" message, VkApp::open() and VkApp::raise() upon receiving a "Raise" message, and VkApp::quitYourself() upon receiving a "Quit" message.
You can also specify the session using command line arguments when you invoke your applications:
-project sessid Join the session specified by sessid
-projectWindow windowid Join the same session as the window specified by windowid
-projectWindow Allow the user to click on a window and join the same session as the window specified
The VkMsgApp class also creates an instance of VkMsgClient that you can use to manage messages in your application. You can retrieve a pointer to this object with the VkMsgApp::messageClient() function:
VkMsgClient* messageClient()
If you want to exit a ViewKit ObjectPak application, but also want to allow other parts of the application the option to abort the exiting if necessary, call VkApp::quitYourself():
virtual void quitYourself()
VkApp::quitYourself() calls the okToQuit() function for each top-level VkSimpleWindow (or subclass). All windows that return TRUE are deleted; however, if the okToQuit() function of any window returns FALSE, the shutdown is terminated and the windows returning FALSE are not deleted. quitYourself() queries the windows in the reverse order in which they were created, except that it checks the window designated as the main window last. (See "Managing Top-Level Windows" for information on designating the main window.)
The default, as provided by VkComponent, is for the okToQuit() function to return TRUE in all cases. You must override okToQuit() for all components that you want to perform a check before quitting. For example, you could override the okToQuit() function for a window to post a dialog asking the user whether he or she really wants to exit the application and then abort the shutdown if the user says to do so. Another possibility would be to return FALSE if a component is in the process of updating a file.
Usually, only VkSimpleWindow and its subclasses use okToQuit(). 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.
A ViewKit ObjectPak application automatically exits when all of its windows are deleted, under any of the following circumstances:
Once all windows are deleted, the application exits by calling VkApp::terminate():
virtual void terminate(int status = 0)
terminate() is a virtual function that calls exit(2). terminate() is also called from within ObjectPak when any fatal error is detected.
You can call terminate() explicitly to exit a ViewKit ObjectPak application immediately. Usually you would use this if you encounter a fatal error. If you provide a status argument, your application uses it as the exit value that the application returns.
You can override terminate() in a VkApp subclass to perform any cleanup operations that your application requires before aborting (for example, closing a database). If you override terminate() in a derived class, call the base class's terminate() function after performing your cleanup operations.
Note: Although you can override quitYourself() in a VkApp subclass, in most cases you should override terminate() instead. This ensures that any cleanup operations you add are performed no matter how the application exits (for example, by error condition or by user interaction with the window manager). If you decide to override quitYourself(), you must perform your cleanup operations before calling the base class's quitYourself(): if quitYourself() succeeds in deleting all windows, your application calls terminate() and exits before ever returning from quitYourself().
This section gives you information on example programs that you might find helpful when getting started with ViewKit ObjectPak programming. It first describes the simplest ViewKit ObjectPak program, which displays a window containing a single label, and discusses the structure of the program. Then, it discusses the demonstration programs provided with ViewKit ObjectPak.
Applications based on ViewKit ObjectPak must obey certain conventions. To see how this organization works, consider the following simple example of a ViewKit ObjectPak application that displays the label "hello" in a window:
To build this example, simply compile the file hello.C and link with the ViewKit ObjectPak library, the help library, and the OSF/Motif and X libraries:
Running the hello program displays a window that says "hello," as shown in Figure 2.:
Figure 2. Result of Running hello
This example uses two classes: the VkApp class and an application-defined class, HelloWindow. The HelloWindow class is derived from the ViewKit ObjectPak VkSimpleWindow class, which is discussed in a moment.
First look at main(). All ViewKit ObjectPak applications start by creating an instance of VkApp. The arguments to this constructor specify the Xt-style class of the application, a pointer to argc, and the argv array. Instantiating a VkApp object opens a connection to the X server and initializes many other services needed by typical applications. VkApp is described in detail in Chapter 3--Application Class. Next, the hello.C program instantiates a HelloWindow object that serves as the application's top-level window. The constructor for this class requires only a name for the window. Finally, the application concludes by calling the HelloWindow object's show() function and the VkApp object's run() function. The run() method never returns. The body of most ViewKit ObjectPak programs is very similar to this short example.
Now examine the HelloWindow class. ViewKit ObjectPak encourages you to create classes to represent all major elements of the user interface. In this example, the only major user interface component is a top-level window containing a label widget. ViewKit provides a class, VkSimpleWindow, that supports many features common to all top-level windows and works closely with the VkApp class to implement various ViewKit ObjectPak features. To use the VkSimpleWindow class, you derive a new subclass and create a single-rooted widget tree that the window displays as its view. ViewKit applications do not have to create shell widgets directly.
The hello.C example is so simple that the HelloWindow class creates only a single XmLabel widget. The XmLabel widget is created in the constructor and then designated as the window's view. More complex classes might create a manager widget and create other widgets as children, or might instantiate other objects, as well. Chapter 4--ViewKit Windows describes how to create windows using ViewKit ObjectPak.
By convention, all ViewKit ObjectPak classes support the className() member function. This function is used by several ViewKit facilities, and is discussed in "VkComponent Access Functions" .
Demonstration programs illustrate different features of ViewKit. For the purposes of documentation, ${ObjectPak} refers to the directory where ViewKit was installed. Demonstration programs include the following:
This chapter contains the following sections:
Figure 41. shows the inheritance graph for the high-level component VkGraph (for displaying and manipulating complex arc-and-node graphs) and an auxiliary class, VkNode.
Figure 41. Inheritance Graph for the ViewKit ObjectPak Graph Classes
Note: To develop applications that use ToolTalk, you must have the ToolTalk development software from your vendor.
This appendix includes the following sections:
The ViewKit interprocess message facility consists of a set of classes that support the ToolTalkTM message service for interprocess communication. Figure 60. shows the inheritance graph for the classes supporting the ViewKit message facility. The ViewKit message facility also provides several utility functions that are not class member functions.
Figure 60. Inheritance Graph for ViewKit Message Facility Classes
The VkApp class provides several access functions and constant data members that you can use to identify your application and the current ViewKit ObjectPak release.
VkApp::ViewKitMajorRelease is a static integer constant that identifies the major release of ViewKit ObjectPak; VkApp::ViewKitMinorRelease is a static integer constant that identifies the minor release of ViewKit ObjectPak, and
VkApp::ViewKitReleaseString is a static character array constant that contains the complete major and minor release information. For example, in a 1.2 release, the value of VkApp::ViewKitMajorRelease would be 1, the value of VkApp::ViewKitMinorRelease would be 2, and the value of VkApp::ViewKitReleaseString would be "ViewKit Release: 1.2". These values can be useful if you need to provide conditional statements in your code to handle different versions of the ViewKit ObjectPak library.
You can use VkApp::setVersionString() to set version information for an application based on ViewKit ObjectPak:
void setVersionString(const char *versionInfo)
You can retrieve the version string using VkApp::versionString():
const char *versionString()
ViewKit ObjectPak displays this version string in the Product Information dialog that is posted when a user selects "Product Info" from the default Help menu. (See "ViewKit Help Menu" for more information on the default Help menu.) For example, consider an application that you invoke with the command MapMaker that includes the following line of code:
theApplication->setVersionString("MapMaker 2.1");
If you select "Product Info" from the default Help menu, your application posts the dialog shown in Figure 9.
Figure 9. Example of the Product Information Dialog
You can use VkApp::setAboutDialog() to replace the standard Product Information dialog with your own custom dialog:
void setAboutDialog(VkDialogManager *dialog)
You must provide setAboutDialog() with a pointer to an object that is a subclass of VkDialogManager. Most frequently, you will actually create a subclass of VkGenericDialog, an abstract subclass of VkDialogManager that simplifies the process of creating custom dialogs. Refer to "Deriving New Dialog Classes Using the Generic Dialog" .
The VkApp::aboutDialog() function returns a pointer to the custom Product Information dialog you have installed:
VkDialogManager* aboutDialog()
ViewKit ObjectPak provides some management classes that control the display of components and widgets. These classes function as attachments: you attach them to one or more existing widgets or components. Then you can use the management class to control some aspect of displaying the widgets and components to which the class is attached.
The VkAlignmentGroup class provides support for aligning collections of widgets with each other in various ways. VkAlignmentGroup is derived from the convenience class VkWidgetList. Consult the VkWidgetList(3) reference page for more information on that class.
To use the VkAlignmentGroup class, you create a VkAlignmentGroup object, add widgets or components to the group, and then call one of the alignment functions provided by VkAlignmentGroup.
The VkAlignmentGroup constructor does not take any arguments:
VkAlignmentGroup objects are not components and do not require names. ObjectPak uses names to uniquely identify widget trees of components, and VkAlignmentGroup class does not create any widgets.
The VkAlignmentGroup destructor destroys only the VkAlignmentGroup object, widgets managed by the object remain unaffected by the VkAlignmentGroup destructor.
Use the add() function to add widgets or components to a VkAlignmentGroup object:
Providing a widget causes add() to add the widget to the alignment group. Providing a pointer to a component adds the base widget of the component to the alignment group. Providing a pointer to a VkOptionMenu object adds each individual menu item to the VkAlignmentGroup object rather than adding the VkOptionMenu object as an entity.
Remove widgets or components from a VkAlignmentGroup object with the remove() function inherited from VkWidgetList:
Provide the widget ID or component pointer that you used to add the widget or component to the alignment group.
To align or distribute elements in a VkAlignmentGroup object, call one of the following functions (all take no arguments and have a void return type):
VkAlignmentGroup provides the following access functions:
Dimension width()
Dimension height()
Position x()
Position y()
VkAlignmentGroup also inherits all access and utility functions provided by VkWidgetList. Refer to the VkWidgetList(3) reference page for more information on that class.
VkResizer class provides controls for moving and resizing existing widgets. Figure 48. shows a simple example of a push button with a VkResizer attachment.
Figure 48. A Widget With a VkResizer Attachment
Use the left mouse button to click on either of the square handles provided by the VkResizer object to drag the handle to a new location. When you release the handle, the VkResizer object resizes the widget so that the widget matches the new size of the VkResizer object. Figure 49. shows an example of resizing the push button shown in Figure 48.
Figure 49. Effect of Resizing a Widget With a VkResizer Attachment
Use the middle mouse button to click on either of the square handles provided by the VkResizer object to drag the entire widget to a new location. When you release the handle, the VkResizer object moves the widget to the new location of the VkResizer object. Figure 50. shows an example of moving the push button shown in Figure 49.
Figure 50. Effect of Moving a Widget With a VkResizer Attachment
To use the VkResizer class, create a VkResizer object, associate an existing widget with the object, and then display the resizer's geometry controls.
The VkResizer constructor accepts two Boolean arguments:
autoAdjust controls whether the VkResizer object automatically tracks outside geometry changes of its attached widget. If you set this value to TRUE, the VkResizer object automatically adjusts its geometry controls whenever its attached widget changes geometry. If you set this value to FALSE, you must call the VkResizer::adjustGeometry() function whenever you want the VkResizer object to adjust its geometry controls to the geometry of its attached widget. The default value of this argument is FALSE.
liveResize controls whether the widget itself or a rectangle representing the widget area is displayed during geometry changes. Setting the second parameter to TRUE causes intermediate geometry changes in the attached widget, which may affect performance. The default value is FALSE.
VkResizer objects do not require names because they are not components; ObjectPak uses names to uniquely identify the widget trees of components, and the VkResizer class does not create any widgets.
The VkResizer destructor destroys only the VkResizer object. If you have a widget attached to the object, it is unaffected by the VkResizer destructor.
Once you have created a VkResizer object, use the VkResizer::attach() function to attach it to an existing widget:
You can also attach a VkResizer object to a component by attaching it to the component's base widget. For example, if resizer is a VkResizer object and obj is a component, you can attach the resizer to the component as follows:
If the VkResizer object is already attached to a widget, it detaches from the old widget before attaching to the new one. You can use the VkResizer::detach() function to detach a VkResizer object from a widget without immediately attaching it to another:
After attaching a VkResizer object to a widget, you must call the VkResizer object's VkResizer::show() function to display its geometry controls:
You can hide the geometry controls by calling the VkResizer object's VkResizer::hide() function:
The VkResizer::shown() function returns a Boolean value indicating whether the VkResizer object is visible and displaying its geometry controls:
You can configure the VkResizer object's geometry manipulations with the VkResizer::setIncrements() function:
setIncrements() accepts four integer arguments. The first two arguments specify the resize increments in the horizontal and vertical dimension, respectively. The last two arguments specify the move increments in the horizontal and vertical dimension, respectively. Setting an increment to zero prohibits resizing or moving in that dimension.
The VkResizer class also provides a ObjectPak member function callback named VkResizer::stateChangedCallback:
This callback informs the application when VkResizer has modified the geometry of its attached widget. The callback supplies as call data a value of the enumerated type VkResizerReason (defined in <Vk/VkResizer.h>). The value can be any of VR_resizing, VR_moving, VR_resized, or VR_moved. VR_resizing and VR_moving indicate that resizing or moving are in progress, and are sent repeatedly as the user adjusts the geometry. VR_resized and VR_moved indicate that the resizing or moving is complete, and are sent when the user releases the VkResizer geometry controls.
ViewKit ObjectPak provides management classes that control the operation of components and widgets. These classes function as attachments, that is, you attach them to one or more existing widgets or components. You can then use the management class to control an aspect of operation of the widgets and components to which the class is attached.
The VkGangedGroup class provides support for "ganging" together OSF/Motif ScrollBar or Scale widgets so that all of them move together; when the value of one of the ScrollBar or Scale widgets changes. All other widgets in the group are updated with that value. VkGangedGroup is derived from the convenience class VkWidgetList. Consult the VkWidgetList(3) reference page for more information on that class.
To use the VkGangedGroup class, you create a VkGangedGroup object and add widgets or components to the group. Thereafter, the VkGangedGroup object automatically updates all of the scales and scrollbars in the group whenever the value of one of them changes.
The VkGangedGroup constructor does not take any arguments:
VkGangedGroup objects do not require names because they are not components; ObjectPak uses names to uniquely identify the widget trees of components, and the VkGangedGroup class does not create any widgets.
The VkGangedGroup destructor destroys only the VkGangedGroup object. If you have widgets or components managed by the object, they are unaffected by the VkGangedGroup destructor.
Use the VkGangedGroup::add() function to add widgets or components to a VkGangedGroup object:
If you provide a widget, add() adds that widget to the alignment group. If you provide a pointer to a component, add() adds the component's base widget to the alignment group.
Note: If you add a component to a VkGangedGroup object, the base widget of that component must be an OSF/Motif ScrollBar or Scale widget.
You can remove widgets or components from a VkGangedGroup object with the remove() function inherited from VkWidgetList:
Provide the widget ID or component pointer that you used to add the widget or component to the ganged group.
You can also use the removeFirst() and removeLast() functions inherited from VkWidgetList to remove the first or last item respectively in the ganged group:
OSF/Motif supports collections of toggle buttons that exhibit one-of-many or "radio-style" behavior by placing all related buttons in a RadioBox widget. This is adequate in many cases, but in some cases it is useful to enforce radio-style behavior on a collection of buttons dispersed throughout an application.
The VkRadioGroup class provides support for enforcing radio-style behavior on an arbitrary group of toggle buttons, no matter where they appear in your application's widget hierarchy. The VkRadioGroup class supports both OSF/Motif ToggleButton and ToggleButtonGadget widgets. Furthermore, you can add OSF/Motif PushButton and PushButtonGadget widgets to a VkRadioGroup object; the VkRadioGroup object simulates radio-style behavior on these buttons by displaying them as armed when the user selects them (using the XmNarmColor color resource as the button's background color and displaying the XmNarmPixmap if the button contains a pixmap).
VkRadioGroup is derived from the convenience class VkWidgetList. Refer to the VkWidgetList(3) reference page for more information.
To use the VkRadioGroup class, create a VkRadioGroup object and add widgets or components to the group. Thereafter, the VkRadioGroup object automatically updates all buttons contained in the group whenever the user selects one of the buttons.
Note: Membership in a VkRadioGroup object is not exclusive; a widget can potentially belong to multiple groups at once.
The VkRadioGroup constructor does not take any arguments:
VkRadioGroup objects do not require names because they are not components; ObjectPak uses names to uniquely identify the widget trees of components, and the VkRadioGroup class does not create any widgets.
The VkRadioGroup destructor destroys only the VkRadioGroup object. If you have widgets or components managed by the object, they are unaffected by the VkRadioGroup destructor.
Use the VkRadioGroup::add() function to add widgets or components to a VkRadioGroup object:
If you provide a widget, add() adds that widget to the radio group. If you provide a pointer to a component, add() adds the component's base widget to the alignment group.
Note: If you add a component to a VkRadioGroup object, the base widget of that component must be an OSF/Motif ToggleButton, ToggleButtonGadget, PushButton, or PushButtonGadget widget.
You can remove widgets or components from a VkRadioGroup object with the remove() function inherited from VkWidgetList:
Provide the widget ID or component pointer that you used to add the widget or component to the radio group.
You can also use the removeFirst() and removeLast() functions inherited from VkWidgetList to remove the first or last item, respectively, in the radio group:
If you use a direct instantiation of VkRadioGroup, you must rely on Xt callback functions registered directly with the toggle buttons to detect and handle state changes in the group. Another approach is to derive a subclass of VkRadioGroup and override the protected VkRadioGroup::valueChanged() function:
valueChanged() is called whenever any member of the radio group changes state. The first argument is the selected widget. The second argument is the call data from the XmNvalueChangedCallback (in the case of a ToggleButton or ToggleButtonGadget widget) or the XmNactivateCallback (in the case of a PushButton or PushButtonGadget widget).
You can override valueChanged() to receive notification of state changes and perform any actions you want. If you override valueChanged(), you should call VkRadioGroup::valueChanged() to update the states of all members of the radio group before performing any other actions.
The VkModifiedAttachment class provides support for tracking the previous and current values in an OSF/Motif Text or TextField widget. The VkModifiedAttachment class automatically displays a dogear (a "folded corner") in the upper-right corner of the text widget when the user changes the text value. Figure 58. shows an example of a text widget with a VkModifiedAttachment dogear.
Figure 58. VkModifiedAttachment Dogear
The user can "flip" between the previous and current text values by clicking on the dogear. Figure 59. demonstrates the results of flipping to a previous text value by clicking on the dogear.
Figure 59. Flipping to a Previous Text Widget Value using a
VkModifiedAttachment Dogear
When the user presses the <Return> key in the text field, the text displayed becomes the current value of the text field and the previously-displayed text becomes the previous value. If the current and previous values are the same, the VkModifiedAttachment object does not display the dogear; the VkModifiedAttachment object redisplays the dogear when the current and previous values are different.
Note: If the user clicks on the dogear before pressing the <Return> key, any changes the user made are discarded.
To use the VkModifiedAttachment class, you must: 1) create an OSF/Motif Text or TextField widget; 2) create a VkModifiedAttachment object; 3) attach the VkModifiedAttachment object to the widget; and 4) display the VkModifiedAttachment object (to display its dogear).
The VkModifiedAttachment class also provides several functions for retrieving the previous and current values of the text field, setting the value of the text field, and managing the display of the object.
Note: Because the VkModifiedAttachment class adds callback functions to handle the changes in value of the text widget, you should not register your own XmNactivateCallback or XmNvalueChangedCallback functions with the text widget. Instead, use the VkModifiedAttachment::modifiedCallback ObjectPak callback to determine when the text widget changes its value, and use the VkModifiedAttachment access functions to obtain the current or previous value of the text widget.
VkModifiedAttachment is derived from the VkModified base class, which tracks previous and current text values not necessarily associated with a text widget. In most cases, you will use the VkModifiedAttachment class; therefore, this section describes the functions inherited from VkModified along with the functions implemented by VkModifiedAttachment. For more information on the VkModified class, consult the VkModified(3) reference page.
Note: The VkModified and VkModifiedAttachment classes are both declared in the <Vk/VkModified.h> header file.
The VkModifiedAttachment constructor accepts three Boolean values:
blankIsValue determines whether the VkModifiedAttachment object accepts a null string (a blank) as a valid previous value when displaying the dogear. If blankIsValue is FALSE, the VkModifiedAttachment object does not display the dogear if the previous value is blank.
autoAdjust determines whether the VkModifiedAttachment object automatically watches its attached text widget for geometry changes and adjusts its own area accordingly. If you set this value to FALSE, you must explicitly call VkModifiedAttachment::adjustGeometry() after changing the geometry of the text widget.
If incrementalChange is TRUE, each incremental change to the text value updates the current and previous values. In this mode, activation of the text widget's XmNvalueChangedCallback callback is considered an incremental change. Examples of incremental changes are: each character added or deleted, each deletion of selected characters, and each text insertion by pasting selected text. If incrementalChange is FALSE, the VkModifiedAttachment object updates the current and previous values only when the user presses the <Return> key in the text field.
The VkModifiedAttachment destructor destroys only the VkModifiedAttachment object. If you have a widget attached to the object, it is unaffected by the VkModifiedAttachment destructor.
Once you have created a VkModifiedAttachment object, use the VkModifiedAttachment::attach() function to attach it to an existing widget:
If the VkModifiedAttachment object is already attached to a widget, it detaches from the old widget before attaching to the new widget. You can use the VkModifiedAttachment::detach() function to detach a VkModifiedAttachment object from a widget without immediately attaching it to another widget:
Once you have attached a VkModifiedAttachment object to a text widget, you must call VkModifiedAttachment::show() to display the attachment:
You can hide a VkModifiedAttachment object by calling VkModifiedAttachment::hide():
When a VkModifiedAttachment object is hidden, it still tracks the current and previous values of the text widget to which it is attached; the user simply cannot toggle between the values. You can still use the VkModifiedAttachment class's access functions to retrieve the previous and current values of the text field.
VkModifiedAttachment::expose() forces a redraw of the attachment's dogear:
expose() is called whenever the dogear widget receives an Expose event. Normally, you should not need to call this function.
You can retrieve the current and previous values of the text widget with value() and previousValue() respectively:
The VkModifiedAttachment class provides an ObjectPak member function callback named VkModifiedAttachment::modifiedCallback:
The VkModifiedAttachment object activates this callback whenever the text widget triggers its XmNactivateCallback or XmNvalueChangedCallback callback. The modifiedCallback provides a pointer to a VkModifiedCallback structure as call data. VkModifiedCallback has the following structure:
The VkModifiedCallback fields are:
reason Reason for callback. Can take one of two values: VM_activate, if text widget triggered its XmNactivateCallback callback, or VM_valueChanged if text widget triggered its XmNvalueChangedCallback callback.
obj Pointer to the VkModifiedAttachment object
event Pointer to the event that triggered the callback
Typically, your callback function should test the reason for the callback and perform an action if appropriate. For example, you can use one of the access functions to obtain the current or previous value of the text widget.
Note: Because the VkModifiedAttachment class adds callback functions to handle the changes in value of the text widget, you should not register your own XmNactivateCallback or XmNvalueChangedCallback callback functions with the text widget. Instead, always use the modifiedCallback ObjectPak callback to determine when the text widget changes its value.
You can programmatically set the new current value of a VkModifiedAttachment object with VkModifiedAttachment::setValue():
setValue() sets the object's new current value; the old current value becomes the previous value. VkModifiedAttachment forces the text widget to display the new current value.
VkModifiedAttachment::toggleDisplay() programmatically toggles the text widget display between the current value and the previous value:
To determine which value the text widget is displaying, call VkModifiedAttachment::latestDisplay():
latestDisplay() returns TRUE if the text widget is displaying the current value or FALSE if the text widget is displaying the previous value.
Finally, you can reset the contents of the text widget with VkModifiedAttachment::displayValue()
displayValue() discards any changes the user may have made and updates the text widget with the current value (if the user has the current view selected) or the previous value (if the user has the previous view selected).
By default, the VkModifiedAttachment object automatically watches its attached text widget for geometry changes and adjusts its own area accordingly. If you set the autoAdjust argument in the VkModifiedAttachment constructor to FALSE, you must explicitly call VkModifiedAttachment::adjustGeometry() after changing the geometry of the text widget to adjust the attachment's geometry:
You can also control the size of the VkModifiedAttachment dogear. By default, the dogear is 10 pixels wide by 10 pixels tall. You can set the width and height to different values with the VkModifiedAttachment::setParameters() function:
To retrieve the current width and height of the dogear, call VkModifiedAttachment::getParameters():
The VkModifiedAttachment class provides several additional utility and access functions:
If setValueAlso is TRUE, fixPreviousValue() also updates the attachment's current value to fixedValue; however, this does not permanently fix the current value.
virtual void fixPreviousValue(char *fixedValue,
Boolean setValueAlso = TRUE)
Widget widget()
Boolean modified()
virtual void setModified(Boolean value)
You can set the value of an XmNdisplayModified resource for a text widget to determine whether or not the attached VkModifiedAttachment object should display its dogear. If you set the text widget's XmNdisplayModified resource to TRUE or if you do not provide a value for the text widget's XmNdisplayModified resource, the attached VkModifiedAttachment object displays its dogear. This is the default behavior.
If you set the text widget's XmNdisplayModified resource to FALSE, the attached VkModifiedAttachment object does not display its dogear, but it does continue to track the text widget's current and previous values. You can still use the functions and callbacks provided by VkModifiedAttachment to manipulate the values and manage the text widget.
The VkApp object maintains a list of all windows created in an application. The VkApp object uses this list to manage the application's top-level windows. So that VkApp can properly manage windows, you should always use the VkSimpleWindow and VkWindow classes to create top-level windows in your application. These classes are described in Chapter 4--ViewKit Windows.
Every application has a main window, and by default, the first window you create is considered the main window. Use the VkApp::setMainWindow() function to specify a different window to treat as the main window:
void setMainWindow(VkSimpleWindow *window)
The access function VkApp::mainWindow() returns a pointer to the VkSimpleWindow (or subclass) object installed as the application's main window:
VkSimpleWindow *mainWindow() const
Additionally, the VkApp class supports several operations that can be performed on all top-level windows in a multi-window application. The following functions take no arguments, have a void return value, and are declared virtual:
show() Displays all of the application's hidden, non-iconified windows.
hide() Removes all of the application's windows from the screen.
iconify() Iconifies all visible windows in the application.
open() Opens all iconified windows in the application.
raise() Raises all visible windows in the application to the top of the window manager's window stack.
lower() Lowers all visible windows in the application to the bottom of the window manager's window stack.
You can also specify whether or not your application's windows start in an iconified state using VkApp::startupIconified():
void startupIconified(const Boolean flag)
If flag is TRUE, then the application starts all windows in the iconified state.
Note: You must call startupIconified() before calling run(), otherwise it will not have any effect.
VkSimpleWindow and VkWindow classes provide simple functions to show, hide, raise, lower, iconify, and open windows. The following functions do not take arguments and return a void value:
show() Displays window and has no effect if window is currently iconified.
hide() Removes window from the screen.
iconify() Iconifies window.
open() Opens window if iconified.
raise() Raises window to top of application's window stack.
lower() Lowers window to bottom of application's window stack.
These functions are declared virtual. If overridden in a subclass, call the corresponding base class function after performing operations required by your subclass.
The VkSimpleWindow class is useful for windows that require only a work area. However, windows frequently require menus. By providing support for a menu bar along the top of the window, the VkWindow class extends the VkSimpleWindow class.
In ViewKit ObjectPak, the VkMenuBar(3) class provides support for menu bars. See Chapter 5--Creating Menus for more details about creating and manipulating menus. "Menu Bar" describes additional functions specific to the VkMenuBar class and provides an example of constructing a menu bar for an application. This section only describes functions provided by VkWindow for installing and manipulating a menu bar.
To install a menu bar, use setMenuBar():
If you provide a pointer to an existing VkMenuBar object, setMenuBar() installs that menu bar. If you provide a VkMenuDesc static menu description, setMenuBar() creates a menu bar from that description and then installs the menu bar.
After installing a menu bar, menu() returns a pointer to the menu bar object:
virtual VkMenuBar *menu() const
To add a menu pane to the menu bar, use addMenuPane():
addMenuPane() creates a VkSubMenu(3) object and adds it to the window's menu bar. If you provide a VkMenuDesc static menu description, addMenuPane() uses it to create the menu pane. Additionally, addMenuPane() automatically creates and installs a menu bar if the window does not currently have one.
You can add a menu pane that enforces radio behavior on the toggle items it contains using addRadioMenuPane():
addRadioMenuPane() creates a VkRadioSubMenu(3) object and adds it to the window's menu bar. If you provide a VkMenuDesc static menu description, addRadioMenuPane() uses it to create the menu pane. Additionally, addRadioMenuPane() automatically creates and installs a menu bar if the window does not currently have one.
This chapter includes the following sections:
Figure 45. shows the inheritance graph for the miscellaneous ViewKit ObjectPak classes that you use primarily to display information or to manage display items.
Figure 45. Inheritance Graph for Miscellaneous ViewKit Display Classes
The ObjectPak help library also provides support for determining the token strings passed to the help system. You must link your application with the libvkhelp library to use this feature, but after you determine all of the token strings you needs, you can link with another help library to provide the final help system for your application.
To determine the token strings, set the "*helpAuthorMode" resource for your application to TRUE. Then, when you run your application, whenever the help system would normally display a help message, it instead displays the token string passed to the help system.
Preference dialogs allow users to customize the behavior of an application, but often involve large numbers of text input fields, labels, toggle buttons, and other controls. Without high-level support, these dialogs can take considerable time and effort to write. Also, a user expects preference dialogs to work in a specific way. Typically, a user sets a number of preferences and clicks on an Apply button or an OK button to apply all changes at once. A user also expects to be able to click on Cancel and return all preferences to their previous state, regardless of the number of changes made.
ViewKit ObjectPak supports an easy-to-use collection of classes for building preference dialogs. Rather than dealing directly with widgets and their placement, callbacks, and so forth, you can simply create groups of preference items. These items maintain their own states, allowing an application to query each item for changes. ObjectPak handles layout automatically and provides the ability to apply or revert all preferences to their previous state.
In ViewKit ObjectPak, preference dialogs are implemented as a specialized class of dialog. Specifically, the base preference dialog class, VkPrefDialog, is a subclass of VkGenericDialog, which is in turn a subclass of VkDialogManager. Thus, the VkPrefDialog class inherits all of the functions and data members provided by these base classes.
However, the way you use preference dialogs in your programs differs significantly from to way you use other dialog classes. A single, reusable instance of each type of dialog is sufficient for the other dialog classes. Details such as the message, button labels, or dialog title change from posting to posting, but general dialog behavior remains the same.
On the other hand, individual postings of preference dialogs often vary significantly. Usually, they have different preference items and data structures associated with each preference item. Unlike the other dialog classes, VkPrefDialog does not create a global instance of a preference dialog. Instead, you must create a separate instance of VkPrefDialog for each preference dialog you want to display in your program. For very simple preference dialogs (such as a few toggle buttons), you might be able to directly instantiate a VkPrefDialog object. However, in most cases you should create a separate subclass of VkPrefDialog for each preference dialog in your application.
For each preference dialog, you create a collection of preference items and associate them with the dialog. Each preference item maintains its own state or value, and your program can query the value of preference items as needed. Users can change the values associated with any number of preference items, then click on the Apply button to apply all changes and keep the dialog up, or the OK button to apply all changes and dismiss the dialog. Users can also click on the Cancel button to return all preferences to their last applied values and dismiss the dialog.
The VkPrefDialog class also supplies an ObjectPak callback named prefCallback. The preference dialog activates this callback whenever the user clicks on the dialog's Apply, OK, or Cancel button.
The basis for all ViewKit ObjectPak preference item classes is the abstract class VkPrefItem, which is derived from VkComponent. All preference items are derived from the base class VkPrefItem, which provides a common set of manipulation functions.
Preference items can be divided into three groups: those that implement various controls such as text fields, toggles, and option menus; those that are "ornamental"; and those that arrange other preference items and manage them as a group.
The following table describes the preference items that implement controls:
The following table describes preference items that are ornamental:
Preference Item |
Description |
---|---|
VkPrefLabel |
A text label. |
VkPrefSeparator |
A separator. |
VkPrefEmpty |
A "null" item that you can use to add extra space between other items. |
The following table describes preference items that create groups of items:
Each preference item maintains its own state or value, and your program can query the value of preference items as needed. Preference items automatically update their stored values when the user clicks on the preference dialog's Apply or OK button, and revert to their previous values when the user clicks on the dialog's Cancel button.
This example demonstrates how to create the preference dialog shown in Figure 30. using the ObjectPak classes.
Figure 30. Example of an ViewKit ObjectPak Preference Dialog
To post this dialog, create an instance of the DocPrefDialog class and use one of the post() functions described in "Posting Dialogs" . For example:
Retrieve the value of a preference item with the getValue() function, as described in "Obtaining and Setting Preference Item Values" . For example:
This section provides an overview of ObjectPak's ToolTalk support. It discusses the classes you use to interface with ToolTalk and the policies enforced by those classes.
The basis for interacting with ToolTalk in your application. VkMsgClient allows you to register and manage message patterns; declare actions for your application to perform when it receives messages; compose messages; and send notices and requests.
A subclass of VkApp, opens a ToolTalk connection and sets up all resources required to send and receive ToolTalk messages.1 You must instantiate a VkMsgApp object in your application (rather than a VkApp object) if you want ToolTalk support for your application. The VkMsgApp constructor creates an instance of VkMsgClient that you can use to manage messages in your application.
A subclass of VkWindow that works with VkMsgApp to support the ObjectPak interprocess message facility. To provide ToolTalk support for your application, you must use VkMsgWindow for your application's windows instead of VkSimpleWindow or VkWindow. The VkMsgWindow constructor creates an instance of VkMsgClient that you can use to manage messages in that window.
A subclass of VkComponent that works with VkMsgApp to support the ObjectPak interprocess message facility. You should use VkMsgComponent to derive new components if those components must send or receive ToolTalk messages. You do not need to derive components from VkMsgComponent if those components do not interact with ToolTalk. The VkMsgComponent constructor creates an instance of VkMsgClient that you can use to manage messages in that component.
The ViewKit ObjectPak message facility also provides several utility functions for manipulating messages. These functions are implemented as normal functions rather than class member functions.
The ObjectPak message facility provides mostly a mechanism for exchanging ToolTalk messages between applications, but it does impose some policies:
These are the policies currently implemented, but they are subject to change in future releases of ViewKit ObjectPak; however, applications that adhere to these policies should not be affected by future changes.
The VkApp class, derived from the VkComponent class, provides facilities required by all ViewKit ObjectPak applications. In all of your ViewKit ObjectPak applications you must create a single instance of VkApp or a class derived from VkApp.
The primary responsibility of VkApp is to handle the initialization and event-handling operations common to all Xt-based applications. When you write a ViewKit ObjectPak application, instead of calling Xt functions such as XtAppInitialize(3) and XtAppMainLoop(3), you simply instantiate and use a VkApp object.
The VkApp class also provides support for other application-level tasks. For example, VkApp provides functions for quitting your application; showing, hiding, iconifying, and opening all of the application's windows; handling application busy states; maintaining product version information; and setting the application's cursor shape.
The VkApp class also stores some essential information that can be accessed throughout an application. This information includes:
This information is maintained in the private portion of the class and is available through public access functions.
This chapter includes the following sections:
Figure 29. shows the inheritance graph for the ViewKit ObjectPak classes necessary to create and manipulate preference dialogs.
Figure 29. Inheritance Graph for the ViewKit Preference Dialog Classes
This guide assumes that you are already an experienced C++ programmer. It also assumes that you are generally familiar with OSF/Motif.
For a thorough discussion of the concepts on which the ObjectPak toolkit is based, see:
For information on OSF/Motif, see:
For comprehensive information on the X Window SystemTM, Xlib, and Xt, see:
The VkRadioBox class provides a simple method for creating radio check boxes (that is, check boxes in which only one toggle at a time can be selected). VkRadioBox is a subclass of VkCheckBox. The only difference between the two classes is that VkRadioBox enforces radio behavior on the toggles it contains.
VkRadioBox provides all of the same functions and data members as VkCheckBox does. You use the VkRadioBox class in the same way that you do the VkCheckBox class.
For example, consider a simple window that contains only a check box with four toggles as shown in Figure 53.
Figure 53. Example Radio Box
Example 42. contains the code used to create this check box.
For some messages, you might want a service process to get autostarted as necessary. When the message is sent and the process is not available, the ToolTalk service starts the process and queues the message.
To set this up, you need to register your application class with what ToolTalk calls a "ptype," a process type (refer to the ToolTalk Programmer's Guide). For example, you might have a file called myapp.ptype that contains:
This indicates that the message "load_file" is observed by a process typed "USR_MY_APP" and that when a "load_file" message is sent, the ToolTalk service should execute "/usr/bin/X11/myApp."
Then, instantiate the VkMsgApp object in the myApp application as follows:
Finally, when you install your application, register this information in the static ToolTalk config file by executing:
This adds the contents of myapp.ptype to the system config file, and tells all existing ttsession processes to update their configurations.
When you express message patterns in a ptype, do not register the pattern in your application. Otherwise, your message handler will be called twice. To register an action, but not register the corresponding pattern, use createAction() instead of addAction().
The VkRepeatButton class, derived from VkComponent, provides an auto-repeating pushbutton. A regular pushbutton activates only once when the user clicks on and releases it. A VkRepeatButton (similar to a scrollbar button) activates when the user clicks on it, and after a given delay, begins repeating at a given interval. It stops activating when the user releases it.
The VkRepeatButton constructor takes three arguments:
name is a character string specifying the component name. parent is the parent widget of the component. type is a VkRepeatButtonType enumerated value specifying the type of button to create. This value can be any of RB_pushButton, RB_pushButtonGadget, RB_arrowButton, or RB_arrowButtonGadget. These create PushButton, PushButtonGadget, ArrowButton, and ArrowButtonGadget widgets, respectively.
A VkRepeatButton object triggers a VkRepeatButton::buttonCallback ObjectPak callback when the button activates. Any ObjectPak object can register a member function with the callback to be invoked when the button activates.
The callback provides an XmAnyCallbackStruct pointer as call data; the XmAnyCallbackStruct.reason contains the reason for the callback, and the XmAnyCallbackStruct.event field contains the event that triggered the callback.
The VkRepeatButton::setParameters() function changes the delay parameters for the button:
initial controls how long, in milliseconds, the user has to hold the button down before it begins to repeat. repeat controls the interval between auto-repeat activations, in milliseconds.
If you need to determine the type of a VkRepeatButton after creation, you can call the VkRepeatButton::type() function:
The return value is a VkRepeatButtonType enumerated value specifying the type of button. This value can be any of RB_pushButton, RB_pushButtonGadget, RB_arrowButton, or RB_arrowButtonGadget, which indicates PushButton, PushButtonGadget, ArrowButton, and ArrowButtonGadget widgets, respectively.
The VkRepeatButton class provides the following X resources that determine operating characteristics of the component:
initialDelay Initial delay in milliseconds before auto-repeat begins (default value 1000)
repeatDelay Auto-repeat interval in milliseconds (default value 200)
Once you have instantiated a VkApp object and set up your program's interface, call VkApp::run():
virtual void run()
The run() function enters a custom main loop that supports dispatching raw events in addition to the normal Xt event handling. See "ViewKit ObjectPak Event Handling" for more information on event handling.
Note: Do not call XtMainLoop(3) or XtAppMainLoop(3) in a ViewKit ObjectPak application.
Example 11. illustrates the typical use of the VkApp class in the main body of a ViewKit ObjectPak program.
This section describes how to register and manage message patterns, declare actions for your application to perform when it receives messages, composes messages, and sends notices and requests. You accomplish all of these tasks using the VkMsgClient class.
You do not explicitly instantiate VkMsgClient objects. Instead, the VkMsgApp, VkMsgWindow, and VkMsgComponent classes all automatically instantiate their own VkMsgClient objects and provide access functions to those objects.
This section describes how to send notices and requests using VkMsgClient. Both notices and requests are types of messages. A sending application sends a notice message to provide information to other applications; the sending application does not expect a reply to a notice. All of the parameters in a notice have an "in" mode, which indicates that receiving applications should read only those parameters.
A sending application sends a request message to ask another application to perform an action and expects a single reply to a request. In a request, some message parameters have an "out" mode, which indicates that the receiving application should fill in these parameters for the reply message. Or "in/out", which indicates that the receiving application should read those parameters and then fill them in with new values for the reply message.
If you're sending a notice consisting of only string arguments or only integer arguments, you can use the VkMsgClient::sendStringNotice() or VkMsgClient::sendIntNotice() function, respectively:
For both functions, op is the message operator. sendStringNotice() expects a NULL-terminated list of pointers to character strings; sendIntNotice() expects a NULL-terminated list of integers. These functions create a message with the arguments you provide, send the message, and then automatically delete the message (that is, delete the storage space allocated by your application when the functions create the message).
Caution: Because sendIntNotice() expects a NULL-terminated list of arguments, be sure that you don't provide a zero-valued argument or else you'll prematurely terminate your argument list.
To send requests or to send notices that contain a mix of argument types, you must compose the message before sending it. To begin composing a message, call VkMsgClient::composeBegin():
void composeBegin()
Note: You can compose only one message at a time among all VkMsgClient objects.
You can then add arguments to your message one at a time, using VkMsgClient::composeAdd():
You can add as an argument: 1) a NULL-terminated character string; 2) an integer value; or 3) a binary string. If you provide a binary string, you must also specify the length of the string as the len argument.
The mode argument is an enumerated value of type VkMsgMode. VK_MSG_IN indicates that the argument is written by the sending application and can be read by the handling application and any observing applications. VK_MSG_OUT indicates that the argument is written by the handling applications and is read by the sending application. VK_MSG_INOUT indicates that the argument can be written by both the sending and handling applications, and can be read by the sending, handling, or any observing applications.
Once you finish composing the message, you send the message. If the message is a notice, use VkMsgClient::sendNotice():
void sendNotice(char *op)
op is the message operator. sendNotice() sends the message you composed, and then automatically deletes the message (that is, deletes the storage space allocated by your application when you compose the message).
To send a request, use VkMsgClient::sendRequest():
VkMsgMessage sendRequest(char *op)
sendRequest() sends the message you composed. op is the message operator. sendRequest() returns an opaque message handle. You can use this handle when calling the various utility functions provided by the ViewKit ObjectPak message facility as described in "Useful functions when handling messages" .
When you send a request using sendRequest(), the function returns immediately. To obtain and handle the reply message to your request, your application must register a message action as described in "Receiving Notices and Handling Requests" .
Note: The ViewKit ObjectPak message facility automatically deletes the request message when your application receives the corresponding reply or failure message.
In some cases, your application might require a reply to a request before performing any other processing. In these cases, you can use VkMsgClient::sendSyncRequest() to send a synchronous request:
VkMsgMessage sendSyncRequest(char *op)
sendSyncRequest() uses a secondary X event loop to simulate a synchronous reply to a request. sendSyncRequest() blocks until it receives the reply message, which it passes as its return value. If the request fails, sendSyncRequest() returns NULL. Note that because sendSyncRequest() uses a secondary X event loop, you should beware of any problems with re-entrant code in any callbacks that could be invoked.
Once you receive the reply message, you can use the various utility functions provided by the ObjectPak message facility, as described in "Useful functions when handling messages" , to parse the reply. You must also use VkMsgDestroy() to destroy the message when you no longer need the reply:
VkMsgStatus VkMsgDestroy(VkMsgMessage msg)
VkMsgDestroy() is implemented as a normal function rather than a class member function. It is declared in the header file <Vk/VkMsg.h>. The VkMsgStatus return value is the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.
The convention for passing a filename argument in a message is to specify the filename as a message attribute rather than a message argument. The VkMsgClient class provides the following functions for sending a message with a filename attribute:
In these functions, file is the filename.
This section describes how to receive notices and handle requests using the ObjectPak message facility, as well as message dispatch, writing message action callbacks, creating message patterns, associating message actions with message patterns, and registering and unregistering message patterns.
For each type of message an application wants to receive, it must register a message pattern with the ToolTalk service. The message pattern describes the operator, arguments, and attributes that a message must have to be delivered to the application. Your application must also register message actions, callback functions that are called when your application receives messages with a particular message pattern.
When the ToolTalk service receives a message, it matches the message against all registered patterns. If the message is a notice, the ToolTalk service delivers a separate message to each application with a matching pattern; if the message is a request, the ToolTalk service selects the "best" pattern match and delivers a single message to the application with the matching pattern.
The ObjectPak message facility then dispatches the message received by the application to each action registered for the matching operator, regardless of any other pattern information you provided when registering the action. Your action is responsible for testing the message arguments and determining whether to process the message or not.
As an example, consider an application that registers an action for a message pattern consisting of a "show" operator and an integer argument. If another process then sends a "show" message with an integer argument, the ToolTalk service sends the message to your application, and your application dispatches the message to the action. On the other hand, if another process sends a "show" message with a character string argument, the ToolTalk service does not send the message to your application because it does not match the registered pattern.
A complication arises if you have multiple actions registered for a particular message operator; for example, if in addition to the action described above, your application registered an action for a message pattern consisting of a "show" operator and a character string argument. In this case, the ToolTalk service sends to your application any "show" message with either an integer or a character string argument. The ObjectPak message facility then dispatches those messages to each action in your application registered for the "show" operator. Each action is then responsible for testing the message arguments and determining whether to process the message or not.
You implement message actions as callback functions that your application invokes when it receives a message matching a given message pattern. This section describes how to write the message action callback functions. "Creating and registering simple message patterns" describes how to register these callback functions with the ObjectPak message facility.
All message action callback functions must be of type VkMsgClientAction:
The callback function arguments are as follows:
clientData The arbitrary client data you provided as the clientData argument when you registered this callback function using the addAction() function, as described in "Creating and registering simple message patterns" .
reason The reason for calling the callback function, expressed as an enumerated value of type VkMsgFacilityReason. Possible values are:
VK_MSG_FACILITY_NOTICE--Notice
VK_MSG_FACILITY_REQUEST--Request
VK_MSG_FACILITY_REPLY--Reply to a previous request
VK_MSG_FACILITY_FAILED--Request failed (not handled)
VK_MSG_FACILITY_STARTED--Request caused autostart of the handler and was queued
msg_in The incoming message in the form of an opaque message handle. You can use this handle when calling the various utility functions provided by the ViewKit ObjectPak message facility as described in "Useful functions when handling messages" . For example, you might want to compare returned values against those you sent in your request.
op The message operator
argc The number of message arguments
argv The message arguments, passed as an array of VkMsgArg structures.
The format of the VkMsgArg structure, for passing message arguments, is:
The elements of the structure are as follows:
type The argument type. This can take any of three pre-defined constant values:
VK_MSG_INT_MSG_ARG_TYPE--integer value
VK_MSG_STRING_MSG_ARG_TYPE--character string value
VK_MSG_BSTRING_MSG_ARG_TYPE--binary string value
type The argument value, expressed as a VkMsgValue union. The definition of the VkMsgValue union is:
typedef union {
int ival;
char *val;
VkMsgBValue bval;
} VkMsgValue
typedef struct {
unsigned char *val;
int len;
} VkMsgBValue
type The argument mode, expressed as an enumerated value of type VkMsgMode. Possible values are:
VK_MSG_IN--the argument is written by the sending application and can be read by the handling application and any observing applications.
VK_MSG_OUT--the argument is written by the handling applications and is read by the sending application.
VK_MSG_INOUT--the argument can be written by both the sending and handling applications, and can be read by the sending, handling, or any observing applications.
Note: Remember that the ViewKit ObjectPak message facility dispatches the messages received by the application to each action registered for the matching operator, regardless of any other pattern information you provided when registering the actions. Your actions are responsible for testing the message arguments and determining whether or not to process the messages they receive.
Message action callbacks that process notices are relatively straightforward to write; the callback simply examines the message data and performs any actions required by the application. You can use the various utility functions provided by the ObjectPak message facility, as described in "Useful functions when handling messages" , to parse the message. Notice callbacks are not expected to send reply messages.
Message action callbacks that process requests must first decide whether or not to handle the request. If so, the callback should perform the following steps:
VkMsgStatus VkMsgReply(VkMsgMessage msg)
VkMsgReply() is implemented as a normal function rather than a class member function. It is declared in the header file <Vk/VkMsg.h>.
After performing any appropriate actions, your message action callback should return a Boolean value to indicate whether or not the ViewKit ObjectPak message facility should propagate the message to other callbacks registered for that action. A Boolean value of FALSE propagates the message; a value of TRUE does not propagate the message.
If a message action callback that processes a request decides to handle a request, it should return TRUE to prevent other message action callbacks from attempting to handle the request as well. If the callback decides not to handle a request, it should return FALSE to allow other callbacks to attempt to handle the message. If all of an application's message actions reject a message, the ToolTalk service tries to dispatch the request to another application.
You should always return FALSE in message action callbacks that process notice messages.
The ViewKit ObjectPak message facility automatically destroys request messages when your application receives the corresponding reply or failure message. If for some reason you need to explicitly destroy a request message, call the VkMsgDestroyRequest() function:
VkMsgStatus VkMsgDestroyRequest(VkMsgMessage msg)
VkMsgDestroyRequest() is implemented as a normal function rather than a class member function. It is declared in the header file <Vk/VkMsg.h>. The VkMsgStatus return value is the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.
The ObjectPak message facility also provides several utility functions for manipulating messages. These functions are implemented as normal functions rather than class member functions. Most of these utility function are actually redefined ToolTalk functions. The ObjectPak message facility provides this level of indirection to allow messaging services other than ToolTalk to be used. You should never directly call any of the ToolTalk routines in your application. Similarly, all of the ToolTalk constants (TT_*) have been replaced by ObjectPak message facility equivalents (VK_MSG_*).
VkMsgTypeIsInt(), VkMsgTypeIsString(), and VkMsgTypeIsBString() check to see whether a given argument is an integer, a character string, or a binary string respectively:
The header file <Vk/VkMsgUtils.h> contains these declarations.
VkMsgSetIVal(), VkMsgSetVal(), and VkMsgSetBVal() change the integer, character string, or binary string value, respectively, of a given message argument:
The header file <Vk/VkMsg.h> contains these declarations.
To parse a message's arguments into a VkMsgArg structure (as described in "Writing message action callbacks" ), use the VkMsgParseArguments() function:
This function is declared in the header file <Vk/VkMsgUtils.h>. You must free the argv result when done.
To retrieve the file attribute associated with a message, use function VkMsgFile():
char *VkMsgFile(VkMsgMessage msg)
This function is declared in the header file <Vk/VkMsg.h>. You are responsible for freeing the returned value when you are finished using it. If there is no file attribute, VkMsgFile() returns NULL.
VkMsgIsErr() checks to see whether a VkMsgStatus value is an error status:
int VkMsgIsErr(VkMsgStatus status)
This function is declared in the header file <Vk/VkMsg.h>. If the VkMsgStatus value is a warning, VkMsgIsErr() returns 0; if it is an error VkMsgIsErr() returns 1.
VkMsgPtrError() determines whether a given opaque handle returned by an ObjectPak message facility function is valid:
VkMsgStatus VkMsgPtrError(void *pointer)
This function is declared in the header file <Vk/VkMsg.h>. You can use this function to test for valid message handles or pattern handles. If the handle is valid, VkMsgPtrError() returns the constant VK_MSG_OK.
VkMsgFail() informs the ToolTalk service that your process cannot handle this request and that the message should not be offered to other processes of the same ptype as yours:
VkMsgStatus VkMsgFail(VkMsgMessage msg)
This function is declared in the header file <Vk/VkMsg.h>. The ToolTalk service sends the message back to the sender with state VK_MSG_FAILED.
VkMsgReject() informs the ToolTalk service that your process cannot handle this message:
VkMsgStatus VkMsgReject(VkMsgMesage msg)
This function is declared in the header file <Vk/VkMsg.h>. The ToolTalk service will try other handlers.
To use your message actions, you must associate them with message patterns and then register those patterns with the ObjectPak message facility.
Use the VkMsgClient::addAction() function to create a message pattern and associate a message action with that pattern:
Note: Register your application's message actions before executing VkApp::run() or posting any ObjectPak dialog. You must register your message actions before entering any Xt event loop; otherwise your application might receive messages before registering message actions, and your application will not process the message as expected.
addAction() creates and registers a simple message pattern consisting of only a message operator, op. You can create more detailed message patterns using the functions described in "Creating more detailed message patterns" . After your application registers this pattern, it receives all messages sent that contain this operator. addAction() returns an opaque message pattern handle. You use this handle to remove this action with the removeAction() function described later in this section.
The proc argument is the callback function invoked when your application receives a message matching the message pattern. The function must be of type VkMsgClientAction. The ObjectPak message facility passes the clientData argument to the function as client data. See "Writing message action callbacks" for information on writing message action callbacks.
The type argument specifies the type of message processing the message action implements. type is an enumerated value of type VkMsgActionType, which can take any of the following values:
VK_MSG_ACTION_OBSERVE Observe messages; use to process notices
VK_MSG_ACTION_HANDLE Handle messages; use to process requests
VK_MSG_ACTION_REPLY Process replies to a request
VK_MSG_ACTION_FAIL Process request failures
VK_MSG_ACTION_START Process notices of message handler starting
The deleteMessage argument determines whether or not your application automatically deletes the message after all actions process it. By default, the ObjectPak message facility automatically destroys the message and your message actions don't need to worry about it. However, if you want to save a copy of the message to deal with it later (for example, to send a reply), you must set deleteMessage to False.
After you've created your message patterns, you must call VkMsgClient::updatePatterns() to register those patterns:
void updatePatterns()
The VkMsgClient::removeAction() function removes an action:
void removeAction(VkMsgPattern pat)
pat is the message pattern returned by addAction(). removeAction() automatically unregisters the associated message pattern from the ObjectPak message facility.
The message pattern created by addAction() consists of only the message operator string. Any message received with that operator matches the particular message pattern and is dispatched to your action.
You can create more detailed message patterns, adding arguments and attributes, to restrict the messages that the ToolTalk service dispatches to your application. To create a more detailed pattern, you first create a basic message pattern using the VkMsgClient::createAction() function:
createAction() accepts the same arguments as addAction(). Like addAction(), it creates a simple message pattern consisting of only a message operator, op, and associates the message action, proc, with that pattern. Unlike addAction(), createAction() does not automatically register the pattern with the ObjectPak message facility. Instead, you can specify additional arguments or attributes to the message pattern using various pattern modifier functions.
All of the pattern modifier functions are implemented as normal functions rather than class member functions. They are actually redefined ToolTalk functions. The header file <Vk/VkMsg.h> contains these declarations. The ObjectPak message facility provides this level of indirection to allow messaging services other than ToolTalk to be used. You should never directly call any of the ToolTalk routines in your application.
Consult the <Vk/VkMsg.h> header file for a list of pattern modifier functions you might find useful. Refer to the ToolTalk Programmer's Guide for information on these functions.
After modifying the message pattern, call VkMsgClient::registerPattern() to register the pattern with the ObjectPak message facility:
VkMsgStatus registerPattern(VkMsgPattern pat)
pat is the opaque message pattern handle returned by createAction(). registerPattern() returns a VkMsgStatus value indicating its status. The VkMsgStatus values are the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.
After you've created your message patterns, you must call VkMsgClient::updatePatterns() to register those patterns:
void updatePatterns()
You can unregister a message pattern with the VkMsgClient::unregisterPattern() function:
VkMsgStatus unregisterPattern(VkMsgPattern pat)
pat is the opaque message pattern handle returned by createAction(). unregisterPattern() returns a VkMsgStatus value indicating its status. The VkMsgStatus values are the same as the tt_status values used by ToolTalk; consult the ToolTalk Programmer's Guide for information on these values.
As an example of creating a detailed message pattern, consider the following code sample:
This example creates a message pattern that matches messages with the operator "message_op," and has three arguments. The first is an "in" integer argument. The second is an "out" argument of any type. The last is an "in" integer argument with value 5.
The two kinds of errors that can occur when a request is made and a reply is expected are as follows:
For instance, a request to "Raise the Mail Tool" would fail if there is no mail tool, since no reply could be received. In this case, if you had registered a VK_MSG_ACTION_FAIL type message action for the "Raise the Mail Tool" operator, your application would call that message action. If you were using VkMsgClient::sendSyncRequest(), it would return NULL.
If your request is successfully sent to a handler, but an error occurs while processing the request, most handlers send a reply and indicate that an error condition occurred. Many handlers return a status code indicating the return status. To obtain a reply status code, call function VkMsgStatusCode():
int VkMsgStatusCode(VkMsgMessage msg)
Furthermore, some handlers provide useful error strings to display in the case of errors. To obtain a reply error string, call function VkMsgStatusString():
char *VkMsgStatusString(VkMsgMessage msg)
The meaning of status codes and the validity of status strings depend on the service handling the request. Both functions are implemented as normal function rather than class member functions. They are actually redefined ToolTalk functions. Header file <Vk/VkMsg.h> contains these declarations.
By default, VkApp installs two cursors for ObjectPak applications: an arrow for normal use, and a watch for display during busy states. (See "Supporting Busy States" for information on busy states in ObjectPak applications.) The VkApp class also provides several functions for installing your own cursors and retrieving the currently installed cursors.
VkApp::setNormalCursor() sets the normal cursor for use in all of your application's windows while the application is not busy:
void setNormalCursor(Cursor c)
You must provide setNormalCursor() with a Cursor argument. See the XCreateFontCursor(3) man page for more information.
You can retrieve the current normal cursor with VkApp::normalCursor():
virtual Cursor normalCursor()
The VkApp class supports both fixed and animated busy cursors. A fixed busy cursor retains the same appearance throughout a busy state. An animated busy cursor is actually a sequence of Pixmaps that you can cycle through while in a busy state, giving the appearance of animation. "Animating the Busy Cursor" describes the procedure to follow to cycle through an animated busy cursor's Pixmaps. If you install an animated busy cursor but do not cycle it, VkApp simply uses the animated cursor's current Pixmap as a fixed busy cursor.
The default busy cursor that VkApp installs, a watch, is actually an animated cursor.
VkApp::setBusyCursor() sets a fixed busy cursor for use in all of your application's windows while the application is busy:
void setBusyCursor(Cursor c)
You must provide setBusyCursor() with a Cursor argument.
You can retrieve the current busy cursor with VkApp::busyCursor():
virtual Cursor busyCursor()
To create an animated busy cursor, you must create a subclass of the abstract base class VkCursorList. The syntax of the VkCursorList constructor is:
VkCursorList (int numCursors)
numCursors is the number of cursor Pixmaps in your animated cursor.The VkCursorList constructor uses this value to allocate space for an array of Cursor pointers. In your subclass constructor, you should perform any other initialization required by your cursor.
In your subclass, you must also override the pure virtual function VkCursorList::createCursor():
virtual void createCursor(int index)
createCursor() creates the cursor for the given index in the animated cursor array. Cursors are numbered sequentially beginning with zero. When your application animates the cursor, it step through the cursor array sequentially. createCursor() must assign the cursor it creates to the index entry in the protected _cursorList array:
Pixmap *_cursorList
Example 12. shows the code required to create an animated hourglass busy cursor.
Once created, to install the animated busy cursor as your application's busy cursor, use an overloaded version of the VkApp::setBusyCursor() function:
void setBusyCursor(VkCursorList *animatedCursor)
Provide a pointer to your animated busy cursor object as the argument to setBusyCursor().
When you use an animated busy cursor, the busyCursor() function returns the currently displayed Pixmap of your busy cursor.
To set a temporary cursor for use in all of your application's windows, use VkApp::showCursor():
void showCursor(Cursor c)
Calling showCursor() immediately displays the temporary cursor. The cursor stays in effect until the application enters or exits a busy state, or you reset the cursor to the normal cursor by calling showCursor() with a NULL cursor argument. Use this function to display a cursor only briefly. To change the cursor for an extended period, use setNormalCursor() or setBusyCursor().
This section describes ObjectPak's support for busy states. A busy state is when you lock out user input during an operation.
Whenever you expect a procedure to take considerable time to complete, you can call the VkApp::busy() function before entering the relevant region of code to lock out user input in all application windows:
If you call busy() with no arguments, the application simply displays a busy cursor. If you provide a string as the first argument, the application posts a dialog to display the string. The string is treated first as a resource name that busy() looks up relative to the dialog widget. If the resource exists, its value is used as the message. If the resource does not exist, or if the string contains spaces or newline characters, busy() uses the string itself as the message.
If you provide a VkSimpleWindow (or subclass) as the second argument, the application posts the dialog over this specified window. If you do not specify a window, the application posts the dialog over the main window. (See "Managing Top-Level Windows" for instructions on setting the main window. See Chapter 7--Using Dialogs for more details on dialog behavior.)
The VkApp::notBusy() function undoes the previous call to busy():
virtual void notBusy()
You can nest calls to busy(), but you must always have matching busy()/notBusy() pairs. An application exits the busy state only when the number of notBusy() calls matches the number of busy() calls.
Note: ViewKit ObjectPak does not "stack" nested busy dialogs, it simply displays the most recently posted busy dialog. Once you post a busy dialog, it remains displayed until the busy state is over or you replace it with another busy dialog.
Example 13. shows an example of setting busy dialog messages using resource values and using nested busy()/notBusy() calls. Note that this is not a complete example: it lists only the code relating to the busy states.
The ReportWindow class defines the busy dialog messages as resource values and loads these values using setDefaultResources() in the ReportWindow constructor.1 The calls to busy() pass these resource names instead of passing the actual dialog text. This allows you to override these resource values in an app-defaults file should you need to.
When the application calls ReportWindow::report(), it posts the busy dialog shown in Figure 7.
Figure 7. Example of Busy Dialog
When the application calls ReportWindow::sort(), it posts the busy dialog shown in Figure 8.
Figure 8. Example of Nested Busy Dialog
Note: The application continues to display the second busy dialog until reaching the theApplication->notBusy() statement in ReportWindow::report().
To animate the busy cursor during a busy state, periodically call VkApp::progressing():
virtual void progressing(const char *msg = NULL)
If you have an animated busy cursor installed, progressing() cycles to the next Pixmap in the cursor list. If you have a fixed cursor installed, progressing() has no effect on the busy cursor.
If you provide a character string argument, your application posts a dialog to display the message. The string is treated first as a resource name that progressing() looks up relative to the dialog widget. If the resource exists, its value is used as the message. If the resource does not exist, or if the string contains spaces or newline characters, progressing() uses the string itself as the message.
The code fragment in Example 14. performs a simulated lengthy task and periodically cycles the busy cursor.
By default, busy() displays the dialog using theBusyDialog, a global pointer to an instantiation of the VkBusyDialog class2 (described in "Busy Dialog" ). If you prefer to use a different dialog object, you can pass a pointer to the object to the setBusyDialog() function:
void setBusyDialog(VkBusyDialog *dialog)
This alternate busy dialog must be implemented as a subclass of VkBusyDialog. Calling setBusyDialog() with a NULL argument restores the default VkBusyDialog object.
Most frequently, you will use setBusyDialog() to install theInterruptDialog, a global pointer to an instantiation of the VkInterruptDialog class, which implements an interruptible busy dialog3. ("Interruptible Busy Dialog" describes the VkInterruptDialog class.) Example 15. shows how to temporarily install an interruptible busy dialog for a task.
The VkMsgWindow class is a subclass of VkWindow that works with VkMsgApp to support the ObjectPak interprocess message facility. Use VkMsgWindow for your application's windows instead of VkWindow or VkSimpleWindow if you want ToolTalk support for your application.
The VkMsgWindow constructor creates an instance of VkMsgClient that you can use to manage messages in that window. Access the window's VkMsgClient object with the VkMsgWindow::messageClient() function:
VkMsgClient *messageClient()
VkMsgWindow also provides a variety of convenience functions for directly manipulating a window's VkMsgClient object. Consult the VkMsgWindow(3) reference page for more information on these functions.
The VkMsgComponent class is a subclass of VkComponent that works with VkMsgApp to support the ObjectPak interprocess message facility. You should use VkMsgComponent to derive new components if those components must send or receive ToolTalk messages. You do not need to derive components from VkMsgComponent if those components do not interact with ToolTalk. Furthermore, you usually should handle the ToolTalk interaction in an application through the application's VkMsgApp object or one of its VkMsgWindow objects.
The VkMsgComponent constructor creates an instance of VkMsgClient that you can use to manage messages in that component. Access the window's VkMsgClient object with function VkMsgWindow::messageClient():
VkMsgClient *messageClient()
VkMsgComponent also provides a variety of convenience functions for directly manipulating a component's VkMsgClient object. Consult the VkMsgComponent(3) reference page for more information.
The VkTabPanel class, derived from VkComponent, displays a row or column of overlaid tabs. A tab can contain text, a pixmap, or both. The user can click on a tab with the left mouse button to select it. One tab is always selected, and appears on top of all the others. When the user selects a tab, VkTabPanel activates an ObjectPak member function callback indicating which tab the user selected. You can register callback functions to perform actions based on the tabs selected.
Figure 54. shows an example of a horizontal VkTabPanel component.
Figure 54. Example of a Horizontal VkTabPanel Component
Figure 55. shows an example of a vertical VkTabPanel component.
Figure 55. Example of a Vertical VkTabPanel Component
When the tabs do not fit within the provided space, the VkTabPanel object "collapses" tabs on the left and right ends of the component (or top and bottom if the VkTabPanel object is vertical). The example in Figure 56. shows these collapsed tabs.
Figure 56. Collapsed Tabs in a VkTabPanel Component
The user can click on the collapsed tabs with either the left or right mouse button to display a popup menu listing all the tabs, as shown in Figure 57.. The user can then select a tab by choosing the corresponding menu item.
Figure 57. Selecting a Collapsed Tab in a VkTabPanel Component with the Popup Menu
The VkTabPanel class also provides work areas implemented as OSF/Motif Form widgets to the left and right of the tab display (or top and bottom if the VkTabPanel object is vertical). By default, these work areas are empty. You can access these work area widgets and implement additional displays or controls if you desire. "Tab Panel Access Functions" describes the work area access functions.
The VkTabPanel constructor initializes the tab panel and allocates all resources required by the component:
name and parent are the standard component name and parent widget arguments.
The optional horizOrientation argument determines the orientation of the tab panel. If horizOrientation is TRUE, the tab panel is horizontal; if it is FALSE, the tab panel is vertical.
The optional tabHeight argument determines the height of the tab display area. The default value, 0, indicates that tab height is determined by the default label height. If you plan to include pixmaps in your tabs, you should specify a height sufficient to contain your largest pixmap. You can also set the tab height by setting the value of the VkTabPanel object's "tabHeight" resource. For example, to set the tab height of the VkTabPanel object tabs to 30, you could include the following line in an app-default file:
Note: In most cases when you display a vertical tab panel, you must explicitly set the height of the tab display area. As described above, the default tab display area height is determined by the tab label's font height rather than the width of the label. As a result, the tabs might not be large enough to display all of the label text.
Once you have created a tab panel, you can add a tab to it using VkTabPanel::addTab():
label specifies the label displayed by the tab. You should use a distinct label for each tab. addTab() first treats this argument as a resource name which is looked up relative to the tab panel's name. If the resource exists, its value is used as the tab label. If no resource is found, or if the string contains spaces or newline characters, the string itself is used as the tab label.
When the user selects this tab, the VkTabPanel object activates either VkTabPanel::tabSelectCallback or VkTabPanel::tabPopupCallback (depending on how the user selected the tab). If you provide a pointer to some data as the clientData argument to addTab(), the tab panel includes that data as part of the VkTabCallbackStruct returned as call data by the callbacks. "Responding to Tab Selection" describes in depth these callbacks and how to use them.
The sorted flag determines where the new tab is added in relation to existing tabs. If sorted is FALSE, addTab() adds the tab after all existing tabs; if sorted is TRUE, addTab() inserts the tab before the first tab whose label alphabetically succeeds the new tab's label.
Note: addTab() compares the labels actually displayed in the tabs, so if you use resources to specify tab labels, addTab() correctly uses the labels specified by the resource values.
The return value of addTab() is the position of the newly-added tab in the tab panel. Tabs are numbered sequentially, with 0 representing the left-most tab in a horizontal tab panel or the top-most tab in a vertical tab panel.
New tabs initially have a NULL pixmap. (To add a pixmap to a label, refer to "Adding a Pixmap to a Tab" .) If the new tab is the first tab in the group, addTab() automatically selects the tab by calling VkTabPanel::selectTab(). selectTab() activates VkTabPanel::tabSelectCallback, so if you register a callback function before adding a tab, you activate that callback function when you add your first tab. Refer to "Responding to Tab Selection" for more information.
You can add more than one tab at a time using VkTabPanel::addTabs():
labels is an array of tab label strings. As with addTab(), these label strings are first treated as resource names which are looked up relative to the tab panel's name. If the resources exist, their values are used as the tab labels. If a particular resource name is not found, or if the string contains spaces or newline characters, the label string itself is used as the tab label. clientDatas is an array of client data; the data for a particular tab is included as part of the VkTabCallbackStruct returned as call data by the selection callbacks. numLabels specifies the number of tabs to be added by addTabs(). sorted determines whether or not the tabs are sorted as addTabs() adds them.
To remove a tab from a tab panel, use VkTabPanel::removeTab():
Specify the tab to remove using either its position index or its label. If removeTab() removes the tab, it returns TRUE. Otherwise, if the position index is out of range or cannot find a tab with the specified label string, it returns FALSE.
Note: If you use the same label for two or more tabs and provide a label string to removeTab(), it removes the first tab (the tab with the lowest index) that matches the label string. In general, avoid using duplicate label strings.
To set or change a pixmap associated with a tab, use VkTabPanel::setTabPixmap():
You can specify the tab using either its position index or its label. If setTabPixmap() successfully sets the tab, it redraws the tabs and returns TRUE. Otherwise, if the position index was out of range or it couldn't find a tab with the label string you specified, it returns FALSE. Pixmap can be either a bitmap (Pixmap of depth 1) or a full color Pixmap.
Note: If you use the same label for two or more tabs and provide a label string to setTabPixmap(), it sets the pixmap for the first tab (the tab with the lowest index) that matches the label string. In general, avoid using duplicate label strings.
To remove an existing pixmap from a tab, call setTabPixmap() with a NULL pixmap.
You can retrieve the pixmap currently installed in a tab using VkTabPanel::tabPixmap():
You can specify the tab using either its position index or its label. If tabPixmap() is successful, the function returns TRUE and the value of the pixmap_return argument is set to point to the tab's pixmap; otherwise, if the position index was out of range or the function couldn't find a tab with the label string you specified, tabPixmap() returns FALSE.
The user can select a tab using either of the following methods:
When the user selects a tab, the VkTabPanel object activates its VkTabPanel::tabSelectCallback. You can register callback functions to perform actions based on the tabs selected. When activated, tabSelectCallback provides a pointer to a VkTabCallbackStruct as call data. The format of VkTabCallbackStruct is as follows:
label is the label displayed by the tab. Note that if you set the label by specifying a resource name when you added this tab, the value of label is the value of the resource you specified.
clientData is the client data you provided when you added this tab to the tab panel.
tabIndex is the position index of the tab. Tabs are numbered sequentially, with 0 representing the left-most tab in a horizontal tab panel or the top-most tab in a vertical tab panel.
If the user selected the tab directly (that is, not through the popup menu), event is the ButtonPress event that triggered the selection. Otherwise, event is NULL.
In your callback function, cast the call data to (VkTabCallbackStruct *), determine which tab the user selected, and perform the appropriate action.
The VkTabPanel object also detects when the user clicks the right mouse button on one of the tabs. Doing so does not select the tab, but it does cause VkTabPanel to activate its VkTabPanel::tabPopupCallback. When activated, tabPopupCallback provides a pointer to a VkTabCallbackStruct as call data. You can register callback functions to handle this event and perform any actions that you want.
To programmatically select a tab, use VkTabPanel::selectTab():
To specify the tab to select using either its position index or its label. If selectTab() successfully selects the tab, it returns TRUE. Otherwise, if the position index is out of range or it can't find a tab with the label string you specified, it returns FALSE.
Note: If you use the same label for two or more tabs and provide a label string to selectTab(), it selects the first tab (the tab with the lowest index) that matches the label string. In general, avoid using duplicate label strings.
You can optionally provide an event argument that selectTab() places in a VkTabCallbackStruct structure, which is then passed as call data to tabSelectCallback.
You can also determine the currently selected tab with VkTabPanel::selectedTab():
selectedTab() returns the index of the currently selected tab. Tabs are numbered sequentially, with 0 representing the left-most tab in a horizontal tab panel or the top-most tab in a vertical tab panel.
VkTabPanel provides several functions for accessing information about a tab panel and its tabs:
getTab() returns TRUE if it is successful, and FALSE if the position index was out of range.
Boolean getTab(int index, char **label_return,
void **clientData_return)
Boolean horiz()
int size()
If you attempt to set the tab height through multiple methods, method 1 has the highest precedence and method 4 has the lowest.
Note: In most cases when you display a vertical tab panel, you must explicitly set the height of the tab display area. As described above, the default tab display area height is determined by the tab label's font height rather than the width of the label. As a result, the tabs might not be large enough to display all of the label text.
The total height of a tab, including decoration, is the sum of the following conditions:
The total height of the VkTabPanel component (or width, if the tab panel is horizontal) is the total height of the tab as described in the preceding conditions, plus the value of the VkTabPanel component's "margin" resource.
int tabHeight()
The total width of a tab, including decoration, is: 1) the width of the tab label; plus 2) if the tab has a pixmap installed, the width of the pixmap plus the pixmap spacing, determined by the value of the VkTabPanel component's "pixmapSpacing" resource; plus 3) the tab's left and right margin, determined by the value of the "marginWidth" resource of the "tabLabel" widget created by VkTabPanel plus the value of the VkTabPanel component's "additionalMarginWidth" resource.
Boolean uniformTabs()
int lineThickness()
Pixel tabBg()
Pixel labelFg()
Pixel labelBg()
GC gc()
Widget area1()
Widget area2()
The VkTabPanel class provides X resources that determine the display characteristics of the component The following table lists and describes the available resources:
The VkTabPanel class creates a widget called "tabLabel" to manage the tabs in a tab panel. VkTabPanel provides X resources that determine display characteristics of the "tabLabel" widget. The following table lists and describes these resources:
The VkCompletionField class, derived from VkComponent, provides a text input field component that supports name expansion. While typing in the field, if the user types a space, then the component attempts to complete the current contents of the field based on a list of possible expansions provided by the application. For example, in a field where the user is expected to enter a file name, the application could provide a list of all files in the current working directory.
The VkCompletionField constructor accepts the standard ObjectPak component name and parent widget arguments:
The constructor creates an OSF/Motif TextField widget as the component's base widget. You can access this widget using the baseWidget() function provided by VkComponent.
The VkCompletionField destructor destroys the component's widget and associated data, including the VkNameList object that stores the list of possible expansions. You should be aware of this in case you provide an existing VkNameList object as an argument to the VkCompletionField::clear() function, described in "Setting/Clearing the Text Completion Field Expansion List." Consult the VkNameList(3) reference page for more information on that class.
You can add individual strings to the completion list by passing them as arguments to the VkCompletionField::add() function:
You can clear the completion list by calling the VkCompletionField::clear() function:
If you provide a VkNameList object, clear() deletes the current completion list and uses the VkNameList object that you provide as the new completion list for the completion field. Consult the VkNameList(3) reference page for more information on that class.
The VkCompletionField::getText() function duplicates the contents of the text field and then returns a pointer to the duplicate string:
Note: Because getText() creates a copy of the text field's contents, you can safely change or delete the returned string.
For example, the following line retrieves the contents of a VkCompletionField object called fileName and assigns the string to the variable openFile:
The VkCompletionField class supplies an ObjectPak member function callback named VkCompletionField::enterCallback. This callback is activated whenever the user presses the <Enter> key while typing in the text field. The callback does not pass any call data. If you want to notify an ObjectPak component whenever the user presses the <Enter> key while typing in a VkCompletionField object, register a member function of that component as an enterCallback function.
The VkCompletionField class should be sufficient for most applications; however, if you want to have more control over the expansion process you can create a subclass of VkCompletionField.
The protected member function VkCompletionField::expand() is called whenever the user types in the text field:
By default, expand() checks whether the user has typed a space, and if so, tries to expand the current contents of the text field; if the user types any other character, expand() simply adds that character to the text field. At any point after an expansion, the VkNameList object pointed to by the protected data member _currentMatchList contains a list of all possible expansions:
You can override the expand() function to install your own expansion algorithm.You have access to the VkNameList object pointed to by the protected data member _nameList, which contains all possible expansions registered with the component:
You can override the protected member function VkCompletionField::activate(), called whenever the user presses the <Enter> key while typing in the text field:
activate() is called after expanding the current contents of the text field and after invoking all member functions registered with the enterCallback callback. By default, this function is empty.
The VkTickMarks class, derived from VkComponent, displays a vertical set of tick marks. Most frequently, you would use this component next to a vertical OSF/Motif XmScale(3) widget. By default, a VkTickMarks component right-justifies its tick marks and displays its labels to the left, which is appropriate if you display the component to the left of a scale. You can also configure a VkTickMarks component to left-justify its tick marks and display its labels to the right, which is appropriate if you display the component to the right of a scale. Figure 46. shows an example of each version of the tick marks.
Figure 46. VkTickMarks Component Examples
The VkTickMarks constructor accepts five arguments:
The first two arguments are the standard ObjectPak component constructor arguments, a component name and a parent widget. If labelsToLeft is TRUE, the tick marks are right-justified and the labels appear to the left; if labelsToLeft is FALSE, the tick marks are left-justified and the labels appear to the right. If you set noLabels to TRUE, the VkTickMarks component does not display any labels. If you set centerLabels to TRUE, the VkTickMarks component centers the labels. This is useful if you want to center a VkTickMarks object between two XmScale widgets.
Set the scale of the tick marks with the VkTickMarks::setScale() function:
min and max specify the minimum and maximum values for the tick mark component. If you set the VkTickMarks component to display labels, it displays the minimum and maximum values next to the bottom and top tick marks, respectively.
majorInterval and minorInterval specify the tick mark spacing. You can specify the number of units between each major and minor tick mark.
For example, the following sets the minimum value of the ticks VkTickMarks object to 0, the maximum to 1000, the major interval to 100, and the minor interval to 50, as shown in Figure 47.:
Figure 47. Example of Setting Tick Mark Scale and Spacing
If you do not use setScale() to set the scale of the tick marks, VkTickMarks uses the values of the resources "minimum," "maximum," "majorInterval," and "minorInterval" to set the respective scale values.
You can add additional labels to the scale with VkTickMarks::addLabel():
The VkTickMarks object displays a label at the value you indicate. You can call addLabel() multiple times to add multiple labels.
The VkTickMarks::setMargin() function controls the VkTickMarks margins:
setMargin() allows you to specify the spacing between the top of the VkTickMarks component and the first tick mark, and the bottom of the component and the last tick mark. The default settings are designed for use next to an XmScale widget: the first and last tick marks align horizontally with the mark in the middle of the scale's slider.
The VkTickMarks class provides several X resources that determine display characteristics of the component:
Note: ViewKit currently supports ToolTalk development only on AIX 4.1+, DEC UNIX 4.0+, HP-UX 10.10+, Solaris 2.4+ and SunOS 4.1+. The ToolTalk classes are not provided in the ViewKit library for other platforms. The ToolTalk development library provides low-level functions for setting up connections to the ToolTalk server and sending and receiving messages. The ViewKit message facility is built on the ToolTalk development library to provide a higher-level interface to create and handle interprocess messages.
This section provides a brief review of some ToolTalk concepts and terms. For a complete description of the ToolTalk message service and directions for creating applications that interface with the ToolTalk service, consult the ToolTalk Programmer's Guide.
The ToolTalk message service allows independent applications to communicate with each other without having direct knowledge of each other. Applications exchange ToolTalk messages to communicate with other applications. Sending applications create, fill in, and send a message; the ToolTalk service determines the recipients and delivers the message to the receiving applications. Receiving applications retrieve messages, examine the information in the message, and then either discard the message or perform an operation and reply with the results.
Messages consist of a character string operator, followed by any number of arguments. The arguments can be integers, character strings, or binary strings. Also, a message can have attributes such as a filename. The receiver of a message is told the number and type of arguments, and is also given access to any attribute values.
For each type of message an application wants to receive, it must register a message pattern with the ToolTalk service. The message pattern describes the operator, arguments, and attributes that a message must have to be delivered to the application. When the application registers a pattern, it must indicate whether it wants to observe or handle messages of that type. Any number of applications can observe a message. The ToolTalk service forwards a copy of the message to each application with a matching message pattern. On the other hand, to ensure that a requested operation is performed only once, only one application can handle a message. If the ToolTalk service cannot find a handler for a message, it returns the message to the sending application indicating that delivery failed.
There are two types of messages:
A sending application sends a notice to provide information to other applications; the sending application does not expect a reply to a notice. A sending application sends a request to ask another application to perform an action. The sending application expects a single reply to a request.
Receiving applications receive notices and perform the actions required to process the notice; the receiving applications do not send reply messages in response to notices. A receiving application receives the request, performs the actions required to process the request, and sends a reply message.
The ToolTalk service handles the details of how requests are handled, and ensures that only one receiver gets to reply. When the ToolTalk service determines that a message needs to be delivered to a specific process, but the process is not currently running, it looks for instructions (provided by the application at installation time) on how to start the application.
This section lists some common mistakes to watch for:
Also, it is useful to turn on ttsession (the ToolTalk server process) debugging output. You can toggle ttsession on/off with:
This section describes the ViewKit ObjectPak undo manager, which supports reversing, or "undoing," actions.
The VkMenuUndoManager class is the basis of ObjectPak's undo manager. The ViewKit ObjectPak undo manager provides an easy-to-use method for users to undo commands that they issue to your application.
The user interface to the ObjectPak undo manager is a single menu item that you add to one of your application's menus. By default, the label of that menu item is "Undo: last_command", where last_command is the name of the last command the user issued. Whenever the user issues a command, the undo manager automatically updates the menu item to reflect the latest command. To undo the command, the user simply selects the undo manager's menu item.
By default, ObjectPak's undo manager provides multi-level undo support. The undo manager keeps commands on a stack. When the user undoes a command, the undo manager pops it from the stack, revealing the previously executed command. Once a user has undone at least one command, executing any new command clears the undo stack. Also, executing any non-undoable command clears the undo stack. If you choose, you can also force the undo manager to provide only single-level undo support, where it remembers only the last command the user issued.
You can use the undo manager to support undoing any command, regardless of whether the user issues the command through a menu or through other interface methods (for example, pushbuttons). The undo manager also supports undoing command classes as implemented by the VkAction(3) and VkMenuActionObject(3) classes described in "Command Classes" . In most cases, all you need to provide for each command is a callback function that reverses the effects of that command.
The programmatic interface to the undo manager is simple to use. Because the VkMenuUndoManager class is a subclass of VkMenuItem, you can add it to a menu and manipulate it as you would any other menu item.
To add undo support for an undoable menu item (VkMenuAction(3) and VkMenuToggle(3) items), simply provide an undo callback function (a function that reverses the effects of the item's action) when you either statically or dynamically define the menu item. Similarly, to add undo support for a command class (VkAction and VkMenuActionObject objects), you provide a member function to undo the effects of the command. For those action that are not implemented in your application as menu items or action classes, you can add undo callbacks directly to the undo stack.
Do not directly instantiate a VkMenuUndoManager object in your program. If you provide an undo callback to any menu item or if you use a subclass of VkAction or VkMenuActionObject in your program, ObjectPak automatically creates an instance of VkMenuUndoManager named "Undo". ("Command Classes" describes the VkAction and VkMenuActionObject classes.) The <Vk/VkMenuItem.h> header file provides theUndoManager, a global pointer to this instance. To access the ObjectPak undo manager, simply use this global pointer.1
You add the undo manager to a menu just as you would any other menu item: using the VkMenu::add() function of the menu object to which you want to add the undo manager. For example, the following line adds the undo manager to a menu pane specified by the variable edit:
edit->add(theUndoManager);
You cannot include the undo manager in a static menu description; however, you can add the undo manager to a statically-defined menu after creating the menu. To specify the position of the undo manager within the menu, include a position parameter when you add the undo manager. For example, the following line adds the undo manager to the top of a menu pane specified by the variable edit:
edit->add(theUndoManager, 0);
To add undo support for an undoable menu item (VkMenuAction and VkMenuToggle items), simply provide an undo callback function when you define the menu item. The undo callback function should reverse the effects of the item's action.
For example, the following static description describes a "Cut" menu item that executes the callback function cutCallback() when the user selects the item and undoCutCallback() when the user undoes the command:
Sometimes, you might want to provide undo support for an action not implemented as a menu item (for example, an action invoked by a pushbutton). ViewKit ObjectPak allows you to do this by adding the action directly to the undo stack using VkMenuUndoManager::add():
The name argument provides a name for the action to appear in the undo manager's menu item. The undoCallback argument must be an Xt-style callback function that the undo manager can call to undo the action. The undo manager passes the clientData argument to the undo callback function as client data when it invokes the callback. Following ObjectPak conventions as described in "Using Xt Callbacks with Components" , you should pass the this pointer as client data so that the callback function can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.
Note: add() simply adds an action to the undo stack; it does not "register" a permanent undo callback for an action. Once the undo stack is cleared, the undo information for that action is deleted. If you later perform the action again and you want to provide undo support for that action, you must use add() again to add the action to the undo stack.
Example 30. shows a simple example of adding an action to the undo stack. The MyComponent constructor creates a pushbutton as part of its widget hierarchy and registers actionCallback() as the button's activation callback function. actionCallback(), in addition to performing an action, adds undoActionCallback() to the undo stack.
The ViewKit ObjectPak classes that support command classes, VkAction and VkMenuActionObject, both require you to override the pure virtual function undoit(), which the undo manager calls to undo an action implemented as a command class. "Command Classes" describes how to use VkAction and VkMenuActionObject to implement command classes.
By default, VkMenuUndoManager provides multi-level undo support. The undo manager keeps commands on a stack. When the user undoes a command, the undo manager pops it from the stack, revealing the previously executed command. Once a user has undone at least one command, executing any new command clears the undo stack. Also, executing any undoable command clears the undo stack.
Supporting multi-level undo in your application can be difficult. If you prefer to support undoing only the last command executed, you can change the behavior of the undo manager with the VkMenuUndoManager::multiLevel() function:
void multiLevel(Boolean flag)
If flag is FALSE, the undo manager remembers only the last command executed.
You can force the undo manager to clear its command stack with the VkMenuUndoManager::reset() function:
void reset()
You can examine the contents of the undo manager's command stack using VkMenuUndoManager::historyList():
VkComponentList *historyList()
historyList() returns a list of objects representing commands that have been executed and are available to be undone. Commands are listed in order of execution; the last command executed is the last item in the list. All of the objects in the list are subclasses of VkMenuItem. Commands added directly to the undo stack (as described in "Using the Undo Manager" ) or commands implemented as VkAction objects (as described in "Command Classes" ) appear as VkMenuActionStub objects. VkMenuActionStub is an empty subclass of VkMenuAction.
The label that the undo manager menu item displays is of the form Undo_label:Command_label. Undo_label is the value of the labelXmNlabelString resource of the undo manager. By default, this value is "Undo". You can change this string (for example, for a German-language app-defaults file) by providing a different value for the XmNlabelString resource. For example, you could set the resource as follows:
*Undo.labelString: Annul
Command_label is the label for the last executed command registered with the undo manager, determined as follows:
Example 31. shows an example of using the undo manager.
This chapter includes the following sections:
Figure 21. shows the inheritance graph for ViewKit ObjectPak classes that support undo management and command classes.
Figure 21. Inheritance Graph for ViewKit ObjectPak Classes Supporting Undo Management and Command Classes
In an ObjectPak application, the user can invoke the help system three different ways: 1) by pressing the <F1> key while the mouse pointer is over a widget; 2) by clicking on the Help button in a dialog; or 3) by selecting an item from the Help menu.
The <F1> key is a standard OSF/Motif method of invoking help. ObjectPak applications provide an additional layer of interpretation to allow you to integrate this method of invoking help with the other methods provided by ObjectPak. Rather than installing XmNhelpCallback functions on widgets, you should use ObjectPak's programmatic interface to the help libraries as described in "ViewKit's Programmatic Interface to a Help Library" .
ViewKit ObjectPak dialogs also allow you to include a Help button as described in "Posting Dialogs" . The Help menu, implemented by the VkHelpPane class, also allows the user to invoke the help system. "ViewKit's Programmatic Interface to a Help Library" describes how to link these interfaces to a help system.
This chapter includes the following sections:
Figure 22. shows the inheritance graph for the basic ViewKit ObjectPak classes required to create and manipulate dialogs.
Figure 22. Inheritance Graph for the ViewKit ObjectPak Dialog Classes
This section describes the features of each ViewKit ObjectPak menu subclass. In addition to specific member functions listed, each class also supports all functions provided by the VkMenu class.
The VkMenuBar class provides a menu bar designed to work with the VkWindow class. In addition to the functions described in this section, the VkWindow class provides some member functions for installing a VkMenuBar object as a menu bar. "Menu Bar Support" describes the functions provided by VkWindow.
Examples of menu bar construction were given in "Creating a Menubar Using a Static Description" (Example 24.) and "Creating a Menubar Using a Static Description" (Example 25.).
There are four different versions of the VkMenuBar constructor:
The forms of the constructor that do not take a name argument automatically use the name "menuBar".
If you use a form of the VkMenuBar constructor that accepts a menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide.
Some forms of the constructor also accept an optional defaultClientData argument. If provided, any menu item that does not provide a client data argument will use this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks, as described in "Constructing Menus" .
The last argument to each version of the constructor is a Boolean value that specifies whether the constructor should create a help pane that interfaces to the help system. The default is to automatically provide the help pane. The help pane is implemented by the VkHelpPane class, described in "ViewKit Help Menu" .
The VkMenuBar class also provides the helpPane() member function:
VkHelpPane *helpPane() const
helpPane() returns a pointer to the menu bar's help pane. If the menu bar does not have a help pane, helpPane() returns NULL.
The VkSubMenu class supports pull-down menu panes. You can use these menu panes within a menu bar (a VkMenuBar object), or as a cascading, pull-right menu in a popup or other pull-down menu.
You seldom need to instantiate a VkSubMenu object directly. You can add a submenu to any type of menu by calling that menu's addSubmenu() member function. You can also add menu panes to the menu bar of a VkWindow object by calling VkWindow::addMenuPane().
If you do need to instantiate a VkSubMenu object directly, use the following constructor form:
where name specifies the name of the submenu. If you supply the optional menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide. If you supply the optional defaultClientData argument, any menu item that does not provide a client data argument will use this argument instead. This allows you to specify menus statically, and allows you to use an instance pointer with callbacks (see "Considerations for Xt Callback client data" for more detailed information.)
The VkSubMenu class provides additional public member functions.
OSF/Motif supports tear-off menus that enable the user to retain a menu pane on the screen. If tear-off behavior is enabled for a menu pane, a tear-off button (appears as a dashed line) appears at the top of the menu pane. The user can tear off the pane by selecting the tear-off button.
By default, tear-off behavior is disabled for all menu panes. You can change the tear-off behavior of a submenu using VkSubMenu::showTearOff():
void showTearOff(Boolean showIt)
If you pass the Boolean value TRUE to showTearOff(), the submenu displays the tear-off button. If you pass the value FALSE, the submenu hides the tear-off button.
You can also enable tear-off behavior for a menu by setting the its XmNtearOffModel resource to XmTEAR_OFF_ENABLED (for example, in a resource file).
You can access the RowColumn widget used to implement the submenu's pulldown pane, by calling VkSubMenu::pulldown():
Widget pulldown()
Note: The baseWidget() function of a VkSubMenu object returns the CascadeButton widget required by OSF/Motif pulldown menus.
The VkRadioSubMenu class, derived from VkSubMenu, supports pull-down menu panes. Its function is similar to that of VkSubMenu, but the RowColumn widget used as a menu pane is set to exhibit radio behavior. This class is intended to support one-of-many collections of VkToggleItem objects. You can use VkRadioSubMenu objects as menu panes within a menu bar (a VkMenuBar object), or as a cascading, pull-right menu in a popup or other pull-down menu.
It is seldom necessary to directly create a VkRadioSubMenu object. You can add radio submenus to any VkMenuBar, VkPopupMenu, or VkSubMenu by calling those classes's addRadioSubmenu() member function. You can also add menu panes to a VkWindow by calling VkWindow::addRadioMenuPane().
You should seldom need to instantiate a VkRadioSubMenu object directly. You can add a radio submenu to any type of menu by calling that menu's addRadioSubmenu() member function. You can also add radio menu panes to the menu bar of a VkWindow object by calling VkWindow::addRadioMenuPane().
For those cases where you do need to instantiate a VkRadioSubMenu object directly, the form of the constructor you should use is:
name specifies the name of the radio submenu. If you provide the optional menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide. If you provide the optional defaultClientData argument, any menu item that does not provide a client data argument will use this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks, as described in "Constructing Menus" .
The VkRadioSubMenu class does not provide any public member functions in addition to those provided by the VkSubMenu class. For information on the utility and access functions provided by VkSubMenu, see "Submenus" .
Example 27. illustrates an example of using a VkRadioSubMenu class.
The VkOptionMenu class supports option menus. You can use this component anywhere in your interface.
Note: Unlike many other ObjectPak components, VkOptionMenu objects are automatically visible when you create them; you do not need to call show() initially to display a VkOptionMenu object.
There are two different versions of the VkOptionMenu constructor that you can use:
Note the following considerations:
If you provide the optional defaultClientData argument, any menu item that does not provide a client data argument uses this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks. This is described in "Constructing Menus" .
To specify the string that is displayed as the options menu's label, you must set the XmNlabelString resource for the menu's label widget. To do so you can:
Because most option menus will be named "optionMenu", if you set the label through a resource value you should qualify the resource specifications with the name of a parent widget or component so that the X resource database can distinguish between instances of VkOptionMenu. For example, you could use resource specifications such as "*mainWindow*optionMenu*labelString" and "*graphWindow*optionMenu*labelString" to distinguish between an option menu that is a descendant of a "mainWindow" component and one that is a descendant of a "graphWindow" component respectively.
You can programmatically set the selected item in an option menu using VkOptionMenu::set():
You can specify the selected item either by a pointer to the item, the item's component name, or the item's index (position) in the option menu, where the top item in the menu has an index of zero.
There are two functions that you can use to determine which item is selected in an option menu.
You can retrieve the index (position) of the currently selected menu item using VkOptionMenu::getIndex():
int getIndex()
getIndex() returns the index (position) of the selected item, where the top item in the menu has an index of zero.
You can retrieve a pointer to the currently selected menu item using VkOptionMenu::getItem():
VkMenuItem *getItem()
Usually, the width of the option menu is set to the width of the largest item it contains. You can force the option menu to a different width with VkOptionMenu::forceWidth()
void forceWidth(int width)
forceWidth() sets all items in the option menu to be width pixels wide.
Example 28. shows an example of using a VkOptionMenu class.
The VkPopupMenu class supports popup menus. You can attach a ViewKit ObjectPak popup menu to one or more widgets in your application so that it pops up automatically whenever the user clicks on any of those widgets with the right mouse button. You can also pop up the menu programmatically.
There are four different versions of the VkPopupMenu constructor:
The forms of the constructor that do not take a name argument automatically use the name "popupMenu".
If you provide the optional menuDesc argument, the constructor creates a menu from the VkMenuDesc structure you provide.
If you provide the optional defaultClientData argument, any menu item that does not provide a client data argument uses this argument instead. This allows menus to be specified statically, while still allowing an instance pointer to be used with callbacks. This is described in "Constructing Menus" .
If you use a form of the VkPopupMenu constructor that accepts a parent argument, the constructor automatically attaches the menu to the widget. This builds the menu as a child of the widget and installs an event handler to pop up the menu whenever the user clicks on the widget with the right mouse button. For more information on attaching a popup menu to a widget, see the description of VkPopupMenu::attach() in "Popup Menus" .
The VkPopupMenu::attach() function attaches a popup menu to a widget:
virtual void attach(Widget w)
The first call to attach() creates all widgets in the popup menu, using the given widget as the parent of the menu. attach() then adds an event handler to post the menu automatically whenever the user clicks on the widget with the right mouse button. Subsequent calls to attach() add the ability to post the menu over additional widgets.
Once you have attached a popup menu to one or more widgets in your application, ObjectPak automatically posts the menu whenever the user clicks on any of those widgets with the right mouse button.
You can also post the menu programmatically even if you have not attached the popup menu to a widget.
If you have not attached the popup menu to a widget, you must first build the menu using VkPopupMenu::build():
virtual void build(Widget parent)
build() builds the menu as a child of the parent widget, but does not install an event handler to post the menu.
Once you have built the menu, you can post it with VkPopupMenu::show():
virtual void show(XEvent *buttonPressEvent)
show() requires an X ButtonPress event as an argument to position the menu on the screen. This requires you to register your own event handler to handle the ButtonPress events.
build() and show() supports applications that wish to control the posting of menus directly. Normally, attach() provides an easier way to use popup menus.
Example 29. shows an example of using a VkPopupMenu class.
The Help menu, implemented by the VkHelpPane class, provides a simple user interface to a help system. VkHelpPane does not actually implement a help system; you must link an external help system to your application. For more information on integrating a help system with your application, refer to Appendix C-- Using a Help System.
VkHelpPane is a subclass of VkSubMenu. VkHelpPane automatically provides five standard menu items, as shown in Figure 20.
Figure 20. ViewKit ObjectPak Help Menu
The first four items interface to an external help system. The help system must provide help request handling and appropriate help messages for the menu item selected:
Help Menu Item |
Description |
---|---|
"Click for Help" |
Provides context-sensitive help. When the user selects this item, the cursor changes into a question mark. The user can then click on any widget in the application. |
"Overview" |
Requests request overview help |
"Index" |
Requests an index of available help topics |
"Keys & Shortcuts" |
Requests help on keys and shortcuts |
"Product Info" |
Displays the Product Information dialog described in "Maintaining Product and Version Information" . The Product Information dialog has no connection to the help system. |
Because VkHelpPane is a subclass of VkSubMenu, you can also use the functions provided by VkSubMenu to add custom Help menu items and delete predefined Help menu items.
The VkMenuBar constructor, described in "Menu Bar" , accepts a showHelpPane argument. If this argument is TRUE, the default, then the VkMenuBar constructor automatically creates a VkHelpPane object and installs it in the menu bar.
You can create a VkHelpPane object and add it to another menu, for example a popup menu, but you should rarely need to do this.
The following table describes the X resources that affect the appearance and behavior of the VkHelpPane class:
As stated earlier, ViewKit ObjectPak does not replace OSF/Motif. It uses OSF/Motif widgets to implement all of its user interface components, and you are free to make X and OSF/Motif calls directly in a ViewKit ObjectPak application. ViewKit ObjectPak doesn't do anything that you can't do yourself using OSF/Motif directly, but the advantage of using ViewKit ObjectPak is that many commonly-needed services are already implemented for you.
Naturally, not all ViewKit services are appropriate for all applications at all times. If a situation arises in which a ViewKit ObjectPak facility doesn't meet your needs, you can use the lower-level OSF/Motif, Xt, or Xlib facilities to perform the desired operation yourself.
Most ViewKit classes are optional; however, you should be aware that certain ViewKit classes depend on other classes. In particular, most classes depend on the existence of an instance of the VkApp class for application management. If you plan to use any ViewKit ObjectPak facilities, you should not attempt to bypass VkApp and open your own connection to the X server, or directly call XtAppInitialize() or an equivalent function. For best results, you should always allow VkApp to handle the Xt initialization and event dispatching. VkApp is described in detail in Chapter 3--Application Class.
Also, use VkSimpleWindow or VkWindow for all top-level windows. These classes are described in detail in Chapter 4--ViewKit Windows.
As an example of some optional classes, consider the ViewKit dialog management facilities. These are intended to let you use dialogs easily and effectively. ViewKit ObjectPak automatically recycles dialogs (reusing the same dialog over and over for multiple purposes), which uses less memory and can lead to faster response times. It is also easy to add additional buttons to any dialog, to provide context-sensitive help on individual dialogs, and much more. The ViewKit ObjectPak dialog management facility is designed to be as flexible as possible, while minimizing the amount of work required of you. You can even write your own custom dialogs that take advantage of the dialog manager.
However, because the design of the ViewKit ObjectPak dialog management classes makes assumptions about the way typical applications use dialogs, the ViewKit ObjectPak dialog manager can't offer the same control that you could obtain by directly constructing and manipulating an OSF/Motif dialog. Should you encounter a situation where the behavior of the dialog manager doesn't match your application's needs, you can always take the same approach you would have to take if the dialog manager didn't exist: create and manipulate your own OSF/Motif dialog directly using OSF/Motif and Xt functions. This doesn't interfere with ViewKit ObjectPak in any way.
Before implementing your own mechanisms, you should be sure you understand the support offered by ViewKit ObjectPak. Situations in which it's necessary to duplicate functionality supported by ViewKit ObjectPak should be rare. On the other hand, extending the class library by deriving new classes, or writing completely new classes to meet application-specific needs, is a natural part of developing any application based on ViewKit ObjectPak or any C++ class library.
This section describes the features of each ViewKit ObjectPak dialog subclass. In addition to specific member functions listed, each class also supports all functions provided by the VkDialogManager class.
The VkInfoDialog class supports standard OSF/Motif information dialogs. The global pointer to the information dialog manager, declared in <Vk/VkInfoDialog.h>, is theInfoDialog.
Use information dialogs to display useful information. Do not use information dialogs to display error messages, which should be handled by the VkErrorDialog, VkWarningDialog, or VkFatalErrorDialog class.
Because the message contained in an information dialog should not require any decision to be made by the user, information dialogs display only the OK button by default. If you need the user to make a selection, you should use another dialog class such as VkQuestionDialog.
VkInfoDialog does not provide any additional functions beyond those offered by the VkDialogManager.
Example 33. illustrates a simple example of posting an information dialog. Note that the window subclass that posts the dialog defines the dialog title and message as resource values.
Figure 27. shows the appearance of the resulting dialog.
Figure 27. Example of an Information Dialog
The VkWarningDialog class supports standard OSF/Motif warning dialogs. The global pointer to the warning dialog manager, declared in <Vk/VkWarningDialog.h>, is theWarningDialog.
Use VkWarningDialog to warn the user of the consequences of an action. For example, VkWarningDialog is appropriate for warning the user that an action will irretrievably delete information.
By default, the dialogs posted by VkWarningDialog contain only an OK button; however, according to Open Software Foundation style guidelines, if you have posted a warning dialog to warn the user about an unrecoverable action, you must allow the user to cancel the destructive action. To add a Cancel button to your warning dialog, simply provide a cancel callback function when you post the dialog.
Hint: If you perform the action in the warning dialog's OK callback, you can simply define an empty function as a cancel callback. If the user clicks on the warning dialog's OK, button, the ok callback performs the action; if the user clicks on the Cancel button, ObjectPak dismisses the dialog without performing any action.
Note: VkWarningDialog does not provide any additional functions beyond those offered by the VkDialogManager.
The VkErrorDialog class supports standard OSF/Motif error dialogs. The global pointer to the error dialog manager, declared in <Vk/VkErrorDialog.h>, is theErrorDialog.
Use VkErrorDialog to inform the user of an invalid action (such as entering out-of-range data) or potentially dangerous condition (for example, the inability to create a backup file).
The messages contained in the error dialogs should not require any decision to be made by the user. Therefore, the error dialogs display only the OK button by default. If you need the user to make a selection, you should use another dialog class such as VkQuestionDialog.
VkErrorDialog does not provide any additional functions beyond those offered by the VkDialogManager.
The VkFatalErrorDialog class supports an error dialog that terminates the application when the user dismisses it. The global pointer to the fatal error dialog manager, declared in <Vk/VkFatalErrorDialog.h>, is theFatalErrorDialog.
Use VkFatalErrorDialog only for those errors from which your program cannot recover. For example, VkFatalErrorDialog is appropriate if an application terminates because it cannot open a necessary data file. When the user acknowledges the dialog posted by VkFatalErrorDialog, the application terminates by calling VkApp::terminate() with an error value of 1. "Exiting ViewKit ObjectPak Applications" describes the terminate() function.
The messages contained in a fatal error dialog should not require any decision to be made by the user. Therefore, the fatal error dialog displays only the OK button by default.
VkFatalErrorDialog does not provide any additional functions beyond those offered by the VkDialogManager.
The VkBusyDialog class supports a busy dialog (also called a working dialog in OSF/Motif) that is displayed when the application is busy. The global pointer to the busy dialog manager, declared in <Vk/VkBusyDialog.h>, is theBusyDialog.
Unlike most other dialog classes, you should not directly post and unpost the busy dialog. VkBusyDialog is used by the VkApp object to display a busy dialog when you place the application in a busy state. The busy dialog is displayed automatically when you call VkApp::busy(), and dismissed automatically when you make a corresponding call to VkApp::notBusy(). VkApp also allows you to use the VkApp::setBusyDialog() function to use a busy dialog other than that provided by VkBusyDialog. Consult "Supporting Busy States" for more information about how VkApp handles busy states.
Because the busy dialog is intended to lock out user input during a busy state, by default the busy dialog does not display any buttons. If you want to allow the user to interrupt the busy state, you should use the VkApp::setBusyDialog() function to substitute the VkInterruptDialog class object for the normal busy dialog.
VkBusyDialog does not provide any additional functions beyond those offered by the VkDialogManager.
The VkInterruptDialog class supports an interruptible busy dialog that you can substitute for the normal busy dialog. The dialog posted by the VkInterruptDialog class includes a Cancel button that the user can click on to cancel the current action. The global pointer to the interruptible busy dialog manager, declared in <Vk/VkInterruptDialog.h>, is theInterruptDialog.
In addition to those functions offered by the VkDialogManager class, VkInterruptDialog provides the wasInterrupted() member function:
Boolean wasInterrupted()
Applications that use VkInterruptDialog must periodically call wasInterrupted() to determine whether the user has clicked on the dialog's Cancel button since the last time the function was called. The period of time between checks is up to the application, which must weigh responsiveness against time spent checking.
Note that wasInterrupted() also calls VkApp::handlePendingEvents() to process any events that have occurred while the application was busy. Because checking for interrupts involves entering a secondary event loop for a short time, you should beware of any problems with re-entrant code in any callbacks that could be invoked.
Also note that you are responsible for performing any cleanup operations required by your application if the user interrupts a process before it is finished (that is, before you would normally call VkApp::notBusy() to end the busy state).
VkInterruptDialog also provides the ObjectPak callback VkInterruptDialog::interruptedCallback. This callback allows objects to register a member function to be called when the user selects the Cancel button of a VkInterruptDialog dialog. This callback can be called only if the application calls VkInterruptDialog::wasInterrupted().
Unlike most other dialog classes, you should not directly post and unpost the interruptible busy dialog. You can use the VkApp::setBusyDialog() function to instruct the VkApp object to use the interruptible busy dialog rather than the normal busy dialog provided by the VkBusyDialog class. The following line shows how you could do this in a program:
theApplication->setBusyDialog(theInterruptDialog);
The following line instructs the VkApp object to revert back to the normal busy dialog:
theApplication->setBusyDialog(NULL);
If you instruct the VkApp object to use the interruptible busy dialog, it is displayed automatically when you call VkApp::busy(), and dismissed automatically when you make a corresponding call to VkApp::notBusy(). Consult "Supporting Busy States" for more information about how VkApp handles busy states.
The code fragment in Example 34. installs the interruptible busy dialog and performs a simulated lengthy task, checking for interrupts periodically. After completing the task, the code reinstalls the normal busy dialog.
The VkQuestionDialog class supports standard OSF/Motif question dialogs. These allow the user to select among simple choices by clicking on pushbuttons. The global pointer to the question dialog manager, declared in <Vk/VkQuestionDialog.h>, is theQuestionDialog.
As described in "Posting Dialogs" , the post(), postModal(), and postBlocked() functions allow you to specify callback functions to be executed when the user clicks on the OK, Cancel, or Apply button. These callbacks apply only to the dialog posted by the current function call; they do not affect any subsequent dialog postings. You can also provide client data that is passed to all of the callbacks. Following ObjectPak conventions as described in "Using Xt Callbacks with Components" , you should normally pass the this pointer as client data so that the callback functions can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.
For the postAndWait() function, instead of providing callbacks, you simply pass a Boolean value for each button specifying whether or not it is displayed. Unlike the other posting functions, the value returned by postAndWait() is an enumerated constant of type VkDialogReason (defined in VkDialogManager). This value is CANCEL, OK, or APPLY, corresponding to the button the user clicked on.
By default, VkQuestionDialog displays only the OK and Cancel buttons. VkQuestionDialog displays the Apply button only if you provide a callback for that button.
VkQuestionDialog does not provide any additional functions beyond those offered by the VkDialogManager.
The VkPromptDialog supports standard OSF/Motif prompt dialogs that allow the user to enter a text string. The global pointer to the prompt dialog manager, declared in <Vk/VkPromptDialog.h>, is thePromptDialog.
You can use VkPromptDialog any time you need to prompt the user to enter a single piece of information. If you need the user to enter more than one value, you should consider whether it is more appropriate to create a preference dialog as described in Chapter 8--Preference Dialogs. Another option is to create your own custom dialog using VkGenericDialog (refer to "Deriving New Dialog Classes Using the Generic Dialog" .)
By default, VkPromptDialog displays only the OK and Cancel buttons. VkPromptDialog displays the Apply button only if you provide a callback for that button.
One method of obtaining the prompt dialog's text string is to extract it, and use it in the OK callback function (and the apply callback function if you provide one). Example 35. demonstrates this technique.
Alternatively, call VkPromptDialog::text() to obtain the text string after the user dismisses the dialog:
const char *text()
If the user clicks on the OK button, the dialog accepts the currently displayed text as input and uses that string as the return value of text(). If the user clicks the Cancel button, the dialog discards the currently displayed value, and any previously-displayed string the dialog might contain is returned as the value of text(). Do not attempt to free the string returned by text(). Typically, you should call text() only if you post the dialog using postAndWait(). postAndWait() returns a value of VkDialogManager::OK.
Note: Do not use text() from within one of the VkPromptDialog callback functions. VkPromptDialog sets the value returned by text() using its own OK callback function. Because OSF/Motif does not guarantee the calling order of callback functions, you cannot be certain that text() will return the correct value from within another callback function.
Caution: Subsequent posting of thePromptDialog can alter the text value. In rare conditions, if you post non-modal, non-blocking dialogs, this could occur even before you retrieved the value using text(). To prevent this, retrieve the text string in the OK callback function as shown in Example 35., or call text() only after posting the dialog using postAndWait() and verifying that postAndWait() returned the value VkDialogManager::OK).
The VkFileSelectionDialog class supports standard OSF/Motif file selection dialogs (as shown in in Figure 28.) that allow the user to browse and select a file or directory. The global pointer to the file selection dialog manager, is theFileSelectionDialog (declared in <Vk/VkFileSelectionDialog.h>):
Figure 28. Example of a File Selection Dialog
To set the initial directory displayed by the dialog, use VkFileSelectionDialog::setDirectory():
void setDirectory(const char *directory)
Unless you explicitly set a directory, the dialog defaults to the current directory.
To set the initial filter pattern used by the dialog (which determines the files displayed in the list box) use VkFileSelectionDialog::setFilterPattern():
void setFilterPattern(const char *pattern)
Unless you explicitly set a selection, the dialog displays all files in a directory.
To set the initial dialog selection, use VkFileSelectionDialog::setSelection():
void setSelection(const char *selection)
One method of obtaining the selection string of the file selection dialog is to extract it and use it in the OK callback function, as shown in Example 36.
Alternatively, call VkFileSelectionDialog::fileName() to obtain the selection string after the user has dismissed the dialog:
const char* fileName()
If the user clicks on the OK button, the dialog accepts the currently displayed text as input and uses that string as the return value of fileName(). If the user clicks the Cancel button, the dialog discards the currently displayed value, and any previously-displayed string the dialog might have contained is returned as the value of fileName(). Do not attempt to free the string returned by fileName(). Typically,you should call fileName() only if you post the dialog using postAndWait() and it returns a value of VkDialogManager::OK.
Note: Do not use fileName() from within a VkFileSelectionDialog callback function. VkFileSelectionDialog sets the value returned by fileName() using its own OK callback function. Because OSF/Motif does not guarantee the calling order of callback functions, you cannot be certain that fileName() will return the correct value from within another callback function.
Caution: Subsequent posting of theFileSelectionDialog can alter the selection value. In rare conditions, if you post non-modal, non-blocking dialogs, this could occur even before you retrieve the value using fileName(). To prevent this, either retrieve the selection string in the OK callback function, or call fileName() only after posting the dialog using postAndWait(), and verifying that postAndWait() returned the value VkDialogManager::OK).
The following code fragment shows a simple example of using the VkFileSelectionDialog class:
The VkGenericDialog class is an abstract subclass of VkDialogManager. It provides a convenient interface for creating custom dialogs that use the ObjectPak interface. Custom dialogs that you derive from this class automatically support caching and all the other features supported by VkDialogManager. You can post and manipulate your custom dialogs using the functions provided by VkDialogManager.
Minimally, when you derive a new dialog class, you must override the VkGenericDialog::createDialog() function to create the dialog used by your class:
virtual Widget createDialog(Widget parent)
ViewKit ObjectPak passes to createDialog() the parent widget for the dialog, and createDialog() must return the dialog you create. Your overriding function must first call VkGenericDialog::createDialog(), which creates a MessageBox dialog template. By default, the dialog displays OK and Cancel buttons. Then, you simply add the interface to the MessageBox widget.
To change the default display buttons and other characteristics for your custom dialog, set the protected data members listed in the following table:
Also, by default ObjectPak dismisses your dialog whenever the user clicks on either the OK or Cancel button, and keeps the dialog posted whenever the user clicks on the Apply button. You can change this behavior by overriding the functions VkDialogManager::ok(), VkDialogManager::cancel(), and VkDialogManager::apply() respectively:
ObjectPak calls these functions whenever the user clicks on one of the buttons in the dialog. By default, ok() and cancel() unpost the dialog and apply() is empty. You can override these functions to change the unposting behavior or to perform any other actions you want.
This chapter includes the following sections:
Figure 3 depicts the inheritance graph for the ViewKit ObjectPak classes VkCallbackObject and VkCallbackObject.
Figure 3. Inheritance Graph for VkCallbackObject and VkComponent
This section describes the abstract VkMenu class, which provides the basic features of the ViewKit ObjectPak menu classes. It describes how to construct menus, manipulate items contained in the menus, and use the menu access functions. Because all ViewKit ObjectPak menu classes are derived from VkMenu, the functions and techniques described in this section apply to all menu classes.
The methods of constructing menus are the same for all types of menus (menu bars, options menus, etc.). The examples in this section use the VkMenuBar class, but the principles are similar for any of the ObjectPak menu classes.
You can build menus either by passing a static menu description to the class constructor for a menu, or by adding items dynamically through function calls. You can mix the two approaches, initially defining a static menu structure and then dynamically adding items as needed.
To construct a menu from a static description, create a VkMenuDesc array that describes the contents of the menu and then pass that array as an argument to an appropriate menu constructor. This section describes the format of the VkMenuDesc structure and provides examples of its use.
The definition for the VkMenuDesc structure is:
The following table describes the VkMenuDesc fields:
VkMenuDesc Field |
Description |
---|---|
menuType |
The type of menu item. The value of this field must be one of the enumerated constants listed in the following table. |
name |
The menu item's name, which is also used as the menu item's default label. |
callback |
An Xt-style callback procedure that is executed when this menu item is activated. |
submenu |
A pointer to an array of a VkMenuDesc structures that describes the contents of a submenu. |
clientData |
Data that is passed to the callback procedure when it is executed. |
undoCallback |
A callback procedure that can be executed to undo the effects of the actions of the activation callback. Implementation of support for undoing actions is described in "Undo Management" . |
The menu type parameter is an enumerated value of type VkMenuItemType. The following table describes the VkMenuType parameter values:
Not all fields are used for each menu item type. The following table summarizes the optional and required fields for each menu item type.
menuType |
name |
callback |
submenu |
clientData1 |
undoCallback |
---|---|---|---|---|---|
ACTION |
R |
O2 |
I |
O |
O |
CONFIRMFIRSTACTION |
R |
Ob |
I |
O |
I |
TOGGLE |
R |
Ob |
I |
O |
I |
LABEL |
R |
I |
I |
I |
I |
SEPARATOR |
I |
I |
I |
I |
I |
SUBMENU |
R |
I |
R |
O3 |
I |
RADIOSUBMENU |
R |
I |
R |
Oc |
I |
END |
R |
I |
I |
I |
I |
R = Required parameter |
|||||
O = Optional parameter |
|||||
I = Ignored parameter |
1
The value you provide as default client data argument to the menu constructor is used for all menu items (unless you explicitly provide a client data parameter). 2 While this menu item is optional, the menu item is useless unless you provide a callback function. 3 The value you provide as a client data parameter is the default value for all menu items in the submenu. |
For example, consider the following array definition:
The editMenu array describes a simple menu for editing in an application. The menu consists of five actions and a separator. The menu's "Cut" item calls the cutCallback() function when it is activated with no client data passed to it. "Cut" also supports an undo action through the undoCutCallback() function. The "Copy" and "Paste" items work similarly.
The "Search" action does not support an undo action. Presumably, the action performed by this item is either too complex to undo or is meaningless to undo.
The "Revert" item is implemented as a CONFIRMFIRSTACTION. When the user activates this item, the application posts a confirmation dialog to warn the user that the action cannot be undone.
As a more complex example, consider a menu that contains two submenus, each of which contains two selectable items. You could describe this menu with definitions such as:
After constructing a static menu description, you create it by passing it as an argument to a menu constructor. For example, to implement the menus defined above as a menu bar, you could execute:
VkMenuBar *menubar = new VkMenuBar(menu);
You can implement the same menu as a popup menu simply by passing the definition to a popup menu constructor:
VkPopupMenu *popup = new VkPopupMenu(menu);
When using Xt-style callbacks in ObjectPak (as described in "Using Xt Callbacks with Components" ), pass the this pointer as client data to all Xt callback functions. Callback functions retrieve this pointer, cast it to the expected component type, and call a corresponding member function.
However, you cannot use the this pointer when defining a static data member. As a workaround, menu constructors accept a defaultClientData argument. If you provide a value for this argument, any menu item that does not provide a client data argument uses this argument instead. This allows you to specify menus statically, while allowing you to use an instance pointer with Xt callbacks. Example 23. illustrates this technique.
Note: VkWindow::addMenuPane(), VkWindow::addRadioMenuPane(), and the form of the VkWindow::setMenuBar() function that accepts a VkMenuDesc array as an argument all automatically use the this pointer as default client data for the menu bars and menu panes that they create.
Example 24. Illustrates using a static description of a menu tree to create a menu bar. The program creates its main window using MyWindow, a subclass of VkWindow. The menu description and all menu callbacks are contained within the MyWindow subclass definition.
When you run this program, you see the window shown in Figure 17..
Figure 17. Main Window with Menu Bar Created by Static Description
The first pane, shown in Figure 18., contains three selectable entries (actions), followed by a separator, followed by a fourth action. The first three menu items invoke a stub function when selected. The fourth item calls quitCallback(), which exits the application.
Figure 18. A Menu Pane Created by a Static Description
The second menu pane, shown in Figure 19., demonstrates a non-selectable label, a separator, and a cascading submenu.
Figure 19. A Menu Pane Containing a Label and a Submenu
In addition to these application-defined menu panes, ObjectPak can automatically add a Help menu to a menu bar, which provides a user interface to a help system. "ViewKit Help Menu" describes the Help menu. Appendix C-- Using a Help System describes how to interface an external help system to a ViewKit ObjectPak application.
In addition to the static description approach demonstrated in the previous section, ObjectPak allows applications to construct menus and menu items dynamically using functions defined in VkMenu. This section describes the menu-creation functions and provides examples of their use.
The VkMenu class provides a number of member functions for creating menus. Each function adds a single menu item to a given menu. You can use these functions at any time in your program. Even if you created a menu using a static definition, you can use these functions to add items to the menu.
VkMenu::addAction() adds to a menu a selectable menu action, implemented as a VkMenuAction object:
addAction() creates a VkMenuAction object named name and adds it to the menu. By default, addAction() adds the item to the end of the menu; if you specify a value for position, addAction() adds the item at that position. actionCallback is the callback function that performs the item's action and undoCallback is the callback function that undoes the action. If you do not provide an undo callback, the action cannot be undone and does not participate in the ViewKit undo mechanism as described in Chapter 6--Undo Management and Command Classes. Client data is client data passed to the callback functions. Following ObjectPak conventions as described in "Using Xt Callbacks with Components" , you should pass the this pointer as client data so that the callback functions can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.
VkMenu::addConfirmFirstAction() adds to a menu a selectable menu action, implemented as a VkMenuConfirmFirstAction object:
addConfirmFirstAction() creates a VkMenuConfirmFirstAction object named name and adds it to the menu. By default, addConfirmFirstAction() adds the item to the end of the menu; if you specify a value for position, addConfirmFirstAction() adds the item at that position. actionCallback is the callback function that performs the item's action and clientData is client data passed to the callback function. As described above, you should pass the this pointer as client data.
VkMenu::addToggle() adds to a menu a selectable menu toggle, implemented as a VkMenuToggle object:
addToggle() creates a VkMenuToggle object named name and adds it to the menu. By default, addToggle() adds the item to the end of the menu; if you specify a value for position, addToggle() adds the item at that position. If you provide a state argument, addToggle() sets the initial state of the toggle to that value. actionCallback is the callback function that performs the item's action and clientData is client data passed to the callback function. As described above, you should pass the this pointer as client data.
VkMenu::addLabel() adds to a menu a non-selectable menu label, implemented as a VkMenuLabel object:
addLabel() creates a VkMenuLabel object named name and adds it to the menu. By default, addLabel() adds the item to the end of the menu; if you specify a value for position, addLabel() adds the item at that position.
VkMenu::addSeparator() adds to a menu a non-selectable menu separator, implemented as a VkMenuSeparator object:
addSeparator() creates a VkMenuSeparator object named name and adds it to the menu. By default, addSeparator() adds the item to the end of the menu; if you specify a value for position, addSeparator() adds the item at that position.
VkMenu::addSubmenu() adds to a menu a submenu, implemented as a VkSubMenu object:
addSubmenu() is overloaded so that you can: 1) add an existing VkSubMenu object; 2) create and add a VkSubMenu object containing no items; or 3) create and add a VkSubMenu object from the static menu description, menuDesc. If you create and add the submenu using the static menu description, you can also provide a defaultClientData value that is used as the default client data for all items contained by the submenu. By default, addSubmenu() adds the item to the end of the menu; if you specify a value for position, addSubmenu() adds the item at that position.
Note: The "m" in addSubmenu() is lower case, whereas the "M" in VkSubMenu is in upper case.
VkMenu::addRadioSubmenu() adds to a menu a submenu that enforces radio-style behavior on the toggle items it contains:
addRadioSubmenu() is overloaded so that you can: 1) add an existing VkRadioSubMenu object; 2) create and add a VkRadioSubMenu object containing no items; or 3) create and add a VkRadioSubMenu object from the static menu description, menuDesc. If you create and add the submenu using the static menu description, you can also provide a defaultClientData value that is used as the default client data for all items contained by the submenu. By default, addSubmenu() adds the item to the end of the menu; if you specify a value for position, addSubmenu() adds the item at that position.
Note: The "m" in addRadioSubmenu() is lower case, whereas the "M" in VkRadioSubMenu is in upper case.
VkMenu::add() adds an existing menu item to a menu:
void add(VkMenuItem *item, int position = -1)
By default, add() adds the item to the end of the menu; if you specify a value for position, add() adds the item at that position. Though you can use add() to add any type of menu item to a menu, you typically need it to add only the ViewKit ObjectPak 5undo manager and VkMenuActionObject objects. "Undo Management" describes the ViewKit ObjectPak undo manager and "Command Classes" describes the VkMenuActionObject class.
Example 25., functionally equivalent to Example 24., constructs a menu by adding items one at a time to the window's menu bar and to individual menu panes.
One of the advantages of the ViewKit ObjectPak menu system is the ability to manipulate the items in a menu after the menu has been created. The ObjectPak menu system allows menu items to be manipulated by sending messages to any menu item. Menu items can also be found and manipulated by name.
The VkMenu::findNamedItem() function allows you to find an item in a menu given its component name:
findNamedItem() finds and returns a pointer to a menu item of the specified name belonging to the menu object or any submenus of the menu object. You can also pass an optional Boolean argument specifying whether or not the search is case-sensitive. If findNamedItem() finds no menu item with the given name, it returns NULL. If multiple instances of the same name exist, findNamedItem() returns the first name found in a depth-first search.
Note: Remember that you need to cast the return value if you need to access a member function provided by a VkMenuItem subclass. For example, if you search for a toggle item, remember to cast the return value to VkMenuToggle before calling a member function such as VkMenuToggle::setVisualState().
The VkMenu::activateItem() function makes a menu item sensitive so that it accepts user input (that is, a user can select the item):
VkMenuItem *activateItem(const char *name)
You provide as an argument to activateItem() the name of the menu item to activate. This is the same name that you gave the menu item when you created it. activateItem() returns a VkMenuItem pointer to the item activated (or NULL if you did not provide a valid menu item name). By default, all menu items are activated (sensitive) when they are created.
The VkMenu::deactivateItem() function makes a menu item insensitive so that it does not accept user input (that is, a user cannot select the item):
VkMenuItem *deactivateItem(const char *name)
You provide as an argument to deactivateItem() the name of the menu item to deactivate. This is the same name that you gave the menu item when you created it. deactivateItem() returns a VkMenuItem pointer to the item deactivated (or NULL if you did not provide a valid menu item name).When insensitive, the menu item appears "grayed out" when you display the menu. To reactivate a menu item, call deactivateItem() on that item.
Note that instead of using VkMenu::activateItem() and VkMenu::deactivateItem() to activate and deactivate menu items, you could retain pointers to all of your menu items and use VkMenuItem::activate() and VkMenuItem::deactivate() respectively. The effect is the same no matter which functions you use, though typically you will find it easier to use the VkMenu functions. "Activating and deactivating menu items" describes VkMenuItem::activate() and VkMenuItem::deactivate().
If you want to remove a menu item from a menu, you can call VkMenu::removeItem():
VkMenuItem *removeItem(const char *name)
You provide as an argument to removeItem() the name of the menu item to remove from the menu. This is the same name that you gave the menu item when you created it. removeItem() returns a VkMenuItem pointer to the item removed. removeItem() does not destroy a menu item, it simply removes the item from the menu hierarchy.
Note: Iinstead of using VkMenu::removeItem(), you could retain pointers to all of your menu items and use VkMenuItem::remove(). The effect is the same no matter which functions you use, though typically you will find it easier to use the VkMenu functions. "Common Features of Menu Items" on page 125 describes VkMenuItem::remove().
You can replace an item in a menu with another menu item using VkMenu::replace():
VkMenuItem *replace(const char *name, VkMenuItem *newItem)
replace() first uses VkMenu::findNamedItem to find the item specified by name. Then it removes that item from the menu and adds the menu item specified by newItem in its place. replace() returns a pointer to the menu item that you replaced.
The program in Example 26. allows users to dynamically add and remove items from a menu, and also to activate and deactivate items.
The VkMenu class also provides some access functions to help manipulate menu items.
To determine the number of items currently associated with a menu, use VkMenu::numItems():
int numItems() const
To determine an item's position in a menu, use VkMenu::getItemPosition():
You can specify the menu item by pointer, name, or widget. getItemPosition() returns the position of the item within the menu, with zero representing the first position in the menu.
As a convenience, you can also access items in a menu using standard array subscript notation:
VkMenuItem * operator[] (int index) const
For example, you can use VkMenu::numItems() with the array subscript notation to loop through an entire menu and perform some operation on all of the items it contains. For example, if menubar is a menu, the following code prints the name and class of each item in the menubar menu:
OSF/Motif provides the components for building menus (buttons, menu shells, and so on) but does little to make menu construction easy. ViewKit ObjectPak provides a set of classes that facilitate common operations on menus, including creating menu bars, menu panes, popup menus, option menus, and cascading menu panes. The ObjectPak menu package also provides an object-oriented interface for activating and deactivating menu items, dynamically adding, removing, or replacing menus items or menu panes, and performing other operations.
The basis for all ViewKit ObjectPak menu classes is the abstract class VkMenuItem, which is derived from VkComponent. There are two types of classes derived from VkMenuItem. The first serve as containers and correspond to the menu types supported by OSF/Motif: popup menus, pulldown menu panes, menu bars, and option menus. The second type of derived classes are individual menu items: actions, toggles, labels, and separators.
The classes derived from VkMenuItem correspond closely with OSF/Motif widgets and gadgets. For example, an action implemented as a VkMenuAction object represents a XmPushButton gadget along with an associated callback. However, the ObjectPak menus offer several advantages over directly using OSF/Motif widgets and gadgets. You can manipulate the menu objects more easily than widgets. You can display, activate, and deactivate items with a single function call. You can also easily move or replace items.
Caution: ObjectPak implements menu items as gadgets rather than widgets. This causes a problem in callbacks and other situations if you try to use certain Xt functions (such as XtDisplay(3), XtScreen(3), and XtWindow(3)) that expect widgets as arguments. Therefore, you should use the more general functions (such as XtDisplayofObject(3), XtScreenofObject(3), and XtWindowofObject(3)) when you need to obtain this information for ObjectPak menu items.
VkMenu, derived from VkMenuItem, is the abstract base class that implements the functionality needed to create and manipulate menus. It provides support for creating menus and adding, removing, replacing, finding, activating, and deactivating menu items.
Separate subclasses of VkMenu implement the various types of menus supported by ObjectPak:
Individual menu items are implemented as subclasses derived from VkMenuItem, as shown in the following table:
This section describes the features of the ObjectPak menu item classes. First it describes the features implemented by VkMenuItem, which are common to all the menu item classes. Then it describes the unique features of each individual menu item class.
For more detailed information about submenus, refer to "Submenus" and "Radio Submenus" .
Note: The header file <Vk/VkMenuItem.h> contains the declarations for all menu item classes.
VkMenuItem provides a standard set of functions for accessing and manipulating menu items. Individual menu items are implemented as subclasses derived from VkMenuItem. Unlike many ObjectPak classes, you should never need to directly instantiate a menu item class. ObjectPak automatically instantiates menu item objects when you create menus (refer to "Constructing Menus" for more detailed information). This guide does not describe the menu item constructors and destructors.
ViewKit implements menu items as gadgets rather than widgets. If you want to directly access menu item gadgets, use Xt functions that accept gadgets, as well as widgets, as arguments.
The VkMenuItem::show() function makes a menu item visible when you display the menu to which it belongs:
void show()
By default, all menu items are visible when created (that is, they appear when you display the menu to which they belong). An explicit call to a menu item's show() function is not necessary to display it. You can call show() to display a menu item after you have hidden it with hide().
The VkMenuItem::hide() function makes a menu item invisible when you display the menu to which it belongs:
void hide()
hide() does not remove the menu item from the menu, but unmanages the widget or gadget associated with a menu item. You can display a hidden menu item by calling its show() function.
To remove a menu item from a menu, call VkMenuItem::remove():
void remove()
remove() does not destroy a menu item, but removes the item from the menu hierarchy.
Note: Instead of retaining pointers to all your menu items and using VkMenuItem::remove() to remove menu items, use VkMenu::removeItem(). The effect is the same regardless of the function you use. Typically, using the VkMenu function is easier. Refer to "Manipulating Items in Menu" on page 146 for a more detailed description of VkMenu::removeItem().
The VkMenuItem::activate() function makes a menu item sensitive so that it accepts user input (that is, a user can select the item):
void activate()
By default, all menu items are activated (sensitive) when they are created.
The VkMenuItem::deactivate() function makes a menu item insensitive so that it does not accept user input (that is, a user cannot select the item):
void deactivate()
When insensitive, the menu item appears "grayed out" when you display the menu to which it belongs. You can re-activate a menu item by calling its activate() function.
Note: Instead of retaining pointers to all of your menu items and using VkMenuItem::activate() and VkMenuItem::deactivate() to activate and deactivate menu items, you can instead use VkMenu::activateItem() and VkMenu::deactivateItem() respectively. The effect is the same no matter which functions you use, though typically you will find it easier to use the VkMenu functions. "Activating and deactivating items in a menu" on page 147 describes VkMenuItem::activate() and VkMenuItem::deactivate().
Generally, you should set the label for a menu item by setting a value in the resource database for that item's XmNlabelString resource. For example, if you have a menu item named "addPage", you could set the label for that item by including a resource specification such as:
*addPage.labelString: Add Page
If you do not set the menu item's XmNlabelString resource, ObjectPak uses the item's name.
In some cases, you might need to set the label of an item programmatically. For example, in a page layout system you might want to change the labels for the items in an Edit menu to reflect the type of object the user has currently selected. You can change a menu item's label programmatically with the setLabel() function:
virtual void setLabel(const char * str)
The string is treated first as a resource name that setLabel() looks up relative to the menu item's widget. If the resource exists, its value is used as the item's label. If the resource does not exist, or if the string contains spaces or newline characters, setLabel() uses the string itself as the item's label. This allows applications to dynamically set and change menu item labels without hard-coding the exact label strings in the application code.
By default, ObjectPak inserts items into a menu in the order you specify them. Therefore, the easiest way to set the positions of menu items is to add them to the menu in the order that you want them to appear.
Occasionally you might need to explicitly set the position of a menu item. To do so, use VkMenuItem::setPosition():
void setPosition(int position)
setPosition() sets the item's position in the menu. You can specify any integer value from zero to the number of items in the menu; a value of zero specifies the first position in the menu. setPosition() ignores invalid values.
Note: setPosition() is effective only before ObjectPak realizes the menu to which the menu item belongs. If you call setPosition() after realizing a menu, it has no effect. For example, if you create a menu bar in a window's constructor, you can use setPosition() to position menu items; however, after calling the window's show() function, setPosition() has no effect.
You can use MenuItem::menuType() to determine the specific menu item type when given a pointer to a VkMenuItem object:
virtual VkMenuItemType menuType()
menuType() returns one of the following enumerated values of type VkMenuItem::VkMenuItemType:
Enumerated Value |
Description |
---|---|
ACTION |
A VkMenuAction object |
CONFIRMFIRSTACTION |
A VkMenuConfirmFirstAction object |
TOGGLE |
A VkMenuToggle object |
LABEL |
A VkMenuLabel object |
SEPARATOR |
A VkMenuSeparator object |
SUBMENU |
A VkSubMenu object |
RADIOSUBMENU |
A VkRadioSubMenu object |
BAR |
A VkMenuBar object |
OPTION |
A VkOptionMenu object |
POPUP |
A VkPopupMenu object |
OBJECT |
A user-defined subclass of |
You can also determine when an object pointed to by a VkMenuItem pointer is a menu by calling MenuItem::isContainer():
virtual Boolean isContainer()
isContainer() returns TRUE if the VkMenuItem object is an item that can "contain" other menu items (in other words, a menu).
VkMenuAction class provides a selectable menu item that performs an action. A VkMenuAction object is implemented as a PushButtonGadget.
A VkMenuAction object is associated with a callback function that performs an operation and, optionally, a callback function that "undoes" the operation. You specify these callback functions when you add the item to a menu using one of the methods described in "Constructing Menus" . Consult the section for information on using VkMenuAction objects in a menu. VkMenuAction provides public functions in addition to those implemented by VkMenuItem.
You can determine whether an action has an undo callback associated with it by calling VkMenuAction::hasUndo():
Boolean hasUndo()
hasUndo() returns TRUE if the object has an associated undo callback function.
If an object has an undo callback function, you can call it programmatically using VkMenuAction::undo():
virtual void undo()
Typically, you will not need to call undo() explicitly. ObjectPak provides automatic undo handling for your application using the VkUndoManager class, as described in Chapter 6--Undo Management and Command Classes. All you have to do is provide undo callback functions for your VkMenuAction objects and create an instance of VkUndoManager as described Chapter 6--Undo Management and Command Classes.
The VkMenuConfirmFirstAction class, derived from VkMenuAction, provides a selectable menu item that performs an action. When the user selects this type of menu item, the application posts a question dialog asking the user for confirmation. The application performs the action only if the user confirms it.
The VkMenuConfirmFirstAction class is intended for irrecoverable actions (for example, deleting a file), VkMenuConfirmFirstAction objects do not support undo callback functions.
The VkMenuConfirmFirstAction class uses a PushButtonGadget to implement the menu choice and the VkQuestionDialog(3) to implement the question dialog. (See "Question Dialog" for more information on the VkQuestionDialog class.)
The question displayed in the confirmation dialog is determined by the value of the "noUndoQuestion" resource, which ObjectPak looks up relative to the menu item's widget. For example, if you have a menu item named "quit", you can set the question text for that item by including a resource specification such as:
*quit.noUndoQuestion: Do you really want to quit?
If you do not provide a value for this resource, ObjectPak uses the default question: "This action cannot be undone. Do you want to proceed anyway?"
The VkMenuToggle class, which is derived from VkMenuAction, provides a two-state toggle as a menu item. To enforce radio behavior on a group of toggles, you must add them to a VkRadioSubMenu object; otherwise, VkMenuToggle object exhibit simple checkbox-style behavior. A VkMenuToggle object is implemented as a ToggleButtonGadget.
In addition to the public functions provided by VkMenuItem, VkMenuToggle provides functions for setting and retrieving the toggle state.
You can set the visual state of a VkMenuToggle object, without activating its associated callback, using VkMenuToggle::setVisualState():
void setVisualState(Boolean state)
setVisualState() selects the toggle if state is TRUE and deselects the toggle if state is FALSE.
You can set the visual state of a VkMenuToggle object and activate its associated callback with VkMenuToggle::setStateAndNotify():
void setStateAndNotify(Boolean state)
Call VkMenuToggle::getState() to retrieve the current value of a VkMenuToggle object:
Boolean getState()
getState() returns TRUE if the toggle is currently selected and FALSE if it is currently deselected.
The VkMenuLabel class provides a non-selectable label as a menu item. A VkMenuLabel object is implemented as a LabelGadget.
The VkMenuLabel class does not provide any public functions other than those implemented by VkMenuItem.
The VkMenuSeparator class provides a non-selectable separator as a menu item. A VkMenuSeparator object is implemented as a SeparatorGadget.
The VkMenuSeparator class does not provide any public functions other than those implemented by VkMenuItem.
VkGraph requires that all nodes that it contains be instances of either the VkNode class or a subclass of VkNode. The VkNode class is responsible for tracking the connectivity, display characteristics, and other features of the nodes. VkNode is a subclass of VkComponent.
The VkNode class provides basic support for interacting with the node widget. Specifically, you can set the string displayed as a label through the VkNode constructor. However, you can create subclasses of VkNode that support any widget type (refer to "Creating Node Subclasses" .)
This section describes the basic functionality provided of the VkNode class. Most VkNode functions other than the constructor are for use by VkGraph. However, you might use some utility and access functions useful.
The VkNode constructor has the following two forms:
name is the node's component name (provide unique names for all nodes). label is the label that the node displays when visible in a graph. If you do not provide a label, the node uses the component name as the label. You can optionally provide a pointer to an existing node, which the constructor uses as a parent node for the new node.
For example, the following line of code creates the node state19 with the internal name "state19" and the label "Indiana":
VkNode state19 = new VkNode("state19", "Indiana");
The following line of code creates a new node, city41, as a child of state19. The name of the new node is "city41" and the label is "Terre Haute":
VkNode city41 = new VkNode("city41", state19, "Terre Haute");
Note: The VkNode constructor initializes internal variables, it does not create any widgets. The VkGraph object (of which a VkNode object is a member) can create and destroy node widgets as necessary. The VkGraph object calls the protected member function VkNode::build() when creating a node's widget is necessary. Refer to "Creating Node Subclasses" on page 261 for more detailed information on build().
The VkNode destructor destroys the node's widget if it exists and deallocates all other internal storage.
VkNode maintains a list of child nodes that you can access using the access functions described in "Basic Node Functionality" . By default, the order of the child nodes in this list depends on the order in which you specified the child relationships. The first child node you specify has an index of 0, the second 1, and so on.You can use the VkNode::sortChildren() to sort the immediate child nodes of a node:
void sortChildren()
The default algorithm used by sortChildren() sorts nodes alphabetically by their internal node names (not their labels).
You can direct VkNode to use a different sort comparison function with VkNode::setSortFunction():
static void setSortFunction(VkNodeSortFunction func)
The type definition of VkNodeSortFunction is:
typedef int (*VkNodeSortFunction)(VkNode *, VkNode *)
The function you provide must be a static function that accepts as arguments two nodes, and returns an integer value less than zero if the first node comes before the second node, zero if the two nodes are equal, and greater than zero if the second node comes before the first node. For example, the following function sorts nodes by their label strings:
Refer to "Basic Node Functionality" for more detailed information about VkNode::label().
VkNode provides a number of access functions for obtaining values associated with a node.
You can retrieve the node's component name using VkNode::name():
char *name() const
You can retrieve the node's label string with VkNode::label():
virtual char *label()
If you did not provide a label string in the node constructor, the value of the label string is the same as the component's name.
You can determine the number of parent and child nodes with VkNode::nParents() and VkNode::nChildren() respectively:
int nParents() const
int nChildren() const
You can retrieve a specific parent or child node using VkNode::parent() and VkNode::child() respectively:
VkNode *parent(int index) const
VkNode *child(int index) const
By default, the order of the parent and child nodes depends on the order in which you specified the parent or child relationships. The first parent node specified has an index of 0, the second 1, and so on. Initially, the child nodes are numbered similarly. However, if you sort the child nodes using the sortChildren() function, the nodes are reordered according to the sort function you used. For example, if you sorted the child nodes alphabetically by component name, the first child node alphabetically has an index of 0, the second 1, and so on.
You can find a particular parent or child node by component name using VkNode::findParent() and VkNode::findChild() respectively:
VkNode *findParent(char *name)
VkNode *findChild(char *name)
These functions return a pointer to the node if found, and NULL if they do not find the node. These functions search only immediate parent or child nodes, not all ancestor or descendent nodes.
You can create subclasses of VkNode to extend its features to maintain additional data, or to change the way the node displays itself in a graph:
You have a great deal of flexibility in deciding how to extend the VkNode class. The important restriction that you must keep in mind is that the VkGraph object of which a VkNode object is a member can create and destroy node widgets as needed. Therefore, in your subclass function definitions you cannot assume that your node's widget exists.
The VkGraph object calls a protected member function, VkNode::build(), whenever it needs to create a node's widget. If you want to use the additional features of the default SgIconGadget widget or if you want to use a different widget in you subclass, you must override build():
virtual void build(Widget parent)
If you simply want to use the additional features of the default SgIconGadget widget, you can call VkNode::build() from within your subclass's build() function to create the SgIconGadget widget and set the widget's label. Then, you can perform any additional operations you want. (Consult the SgIconGadget(3) reference page for more information on using this widget.) For example:
void MyNode::build(Widget parent)
{
VkNode::build(parent);
// Additional setup...
}
If you want to use your own widget or widget hierarchy, create the widget(s) using parent as the parent widget, and assign the widget or root of a widget hierarchy to the _baseWidget data member. After creating the _baseWidget, call installDestroyHandler(), as described in "Handling Component Widget Destruction" .
From within a VkNode subclass you can also access the _label data member:
char *_label
_label contains the node's label string as set by the VkNode constructor.
ViewKit ObjectPak is a C++ toolkit that makes it easier for you to develop applications. It provides a collection of high-level user interface components and other support facilities that you typically must implement in every application. For example, it provides high-level user interface components, such as windows, menus, and dialogs.
ViewKit ObjectPak does not replace OSF/Motif or any other user interface toolkit. In fact, it uses OSF/Motif widgets to implement all of its user interface components; also, you can directly call OSF/Motif functions to create and manipulate widgets in a ViewKit ObjectPak application. The ViewKit ObjectPak architecture helps mask much of the complexity of programming with OSF/Motif.
ViewKit ObjectPak offers the following advantages:
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.
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:
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:
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:
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:
There is also a version of addCallback() for registering non-member functions. It's syntax is:
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:
You could register it for an ObjectPak callback with the line such as
The "(VkCallbackFunction)" cast for the callback function is required.
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:
The following version of removeCallback() removes a non-member function registered as a callback:
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:
The removeAllCallbacks() function removes multiple ObjectPak callbacks:
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 :
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:
The callCallbacks() member function triggers a specified callback, invoking all member functions registered for that callback:
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:
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.
The following code illustrates how to use deleteCallback to prevent dangling pointers:
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.
This section describes the dialog management features provided by the abstract VkDialogManager base class. It describes how to post dialogs, unpost dialogs, set dialog titles, and set dialog button labels. Because all ViewKit ObjectPak dialog management classes are derived from VkDialogManager, the functions and techniques described in this section apply to all dialog management classes.
This section describes the various methods of posting dialogs and provides some simple examples.
The following table describes the four functions ViewKit ObjectPak offers for posting dialogs:
post(), postModal(), and postBlocked() accept the same arguments, and are also overloaded identically to allow for almost any combination of arguments without resorting to using NULLs as placeholders. Consult the VkDialogManager(3) reference page for a complete listing of the overloaded versions of the post(), postModal(), and postBlocked() functions.
The following code shows the most general form of the post() function:
The arguments for these methods are:
msg The message to display in the dialog. This string is first treated as a resource name which is looked up relative to the dialog widget. If it exists, the resource value is used as the message. If the resource does not exist, or if the string contains spaces or newline characters, the string itself is used as the message.
Most dialogs are not useful if you do not provide a message argument: they display no text. VkFileDialog and VkPreferenceDialog are exceptions in that they provide their own complex interfaces.
okCB An Xt-style callback function executed when the user clicks on the OK button. (All dialogs except for the VkBusyDialog and VkInterruptDialog dialogs display an OK button by default.)
cancelCB An Xt-style callback function executed when the user clicks on the Cancel button. For many of the dialog classes, ViewKit ObjectPak does not display a Cancel button unless you provide this callback.
applyCB An Xt-style callback function executed when the user clicks on the Apply button. For many of the dialog classes, ObjectPak does not display an Apply button unless you provide this callback.
clientData Client data to pass to the button callback functions. Following ObjectPak conventions as described in "Using Xt Callbacks with Components" , you should normally pass the this pointer as client data so that the callback functions can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.
helpString A help string to pass to the help system. See Appendix C-- Using a Help System for information on the help system. If you provide a string, the dialog displays a Help button.
parent The widget over which ViewKit ObjectPak should display the dialog. If you do not provide a widget, or if the given widget is hidden or iconified, ObjectPak posts the dialog over the main window if it is managed and not iconified. ("Managing Top-Level Windows" describes how the main window is determined.) If both the widget you specify and the main window are hidden or iconified, ObjectPak posts the dialog as a child of the hidden application shell created by the VkApp class. Also see the description of VkDialogManager::centerOnScreen() in "Dialog Access and Utility Functions" .
All versions of the post(), postModal(), and postBlocked() functions return the widget ID of the posted dialog. You should rarely need to use this value.
Note: The arguments that you provide apply only to the dialog posted by the current call to post(), postModal(), and postBlocked(); they have no effect on subsequent dialogs. For example, if you provide an apply callback function to a call to post(), it is used only for the dialog posted by that call. If you want to use that callback for subsequent dialogs, you must provide it as an argument every time you post a dialog.
postAndWait() provides a simpler method for posting blocking, application-modal dialogs than using postBlocked(). The most general form of the postAndWait() function is:
msg is the message to display in the dialog. As with the other posting functions, postAndWait() first treats the string as a resource name which it looks up relative to the dialog widget. If the resource exists, postAndWait() uses the resource value as the message. If postAndWait() finds no resource, or if the string contains spaces or newline characters, it uses the string itself as the message. The next three arguments determine which buttons the dialog should display. A TRUE value displays the button and a FALSE value hides the button. helpString and parent specify a help string and a parent window, just as with the other posting functions.
Note: The arguments that you provide apply only to the dialog posted by the current call to postAndWait() and have no effect on subsequent dialogs.
When you call postAndWait(), ObjectPak posts the dialog, enters a secondary event loop, and does not return until the user dismisses the dialog. Unlike postBlocked(), postAndWait() handles all callbacks internally and simply returns an enumerated value of type VkDialogReason indicating which button the user selected. The possible return values are VkDialogManager::OK, VkDialogManager::CANCEL, or VkDialogManager::APPLY. postAndWait() is useful for cases in which it is necessary or convenient not to go on to the next line of code until the user dismisses the dialog. For example:
Note: postAndWait() posts dialogs as full-application modal dialogs to minimize potential problems that can be caused by the secondary event loop, but you should be aware that the second event loop is used and be sure that no non-re-entrant code can be called.
As with the other functions for posting a dialog, postAndWait() is overloaded to allow for almost any combination of arguments without resorting to using NULLs as placeholders. Consult the VkDialogManager reference page for a complete listing of the overloaded versions of postAndWait().
The following line posts a simple non-modal, non-blocking information dialog over the application's main window:
Figure 23. shows the appearance of this dialog when posted. Because the call did not provide any callback for the OK button, when the user clicks on the button, ObjectPak simply dismisses the dialog.
Figure 23. Example Information Dialog
You could also specify the message as an X resource. In the above example, you could name the resource something such as newMailMessage and set it in a resource file with the line:
*newMailMessage: You have new mail in your system mailbox
Then you could use the following line to post the information dialog:
theInfoDialog->post("newMailMessage");
The following line displays a non-modal, non-blocking question dialog over the application's main window:
Figure 24. shows the appearance of this dialog when posted. If the user clicks on the OK button, the program dismisses the dialog and executes the MailWindow::readMailCallback() function. Following ObjectPak conventions as described in "Using Xt Callbacks with Components" , the client data argument is set to the value of the this pointer so that MailWindow::readMailCallback() can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.
Figure 24. Example Question Dialog
Because the call to post() did not provide any callback for the Cancel button, when the user clicks on the button, ObjectPak simply dismisses the dialog. If instead you needed to perform some type of cleanup operation when the user clicks on the Cancel button, you would need to provide a callback for the Cancel button:
In general, try to encapsulate all dialog callbacks and related information in the subclass of the object with which they are associated. For example, for dialogs that are associated with a specific window, you include all the code related to those dialogs in the subclass definition for that window.
This technique is illustrated in Example 32., which shows a simple example of using the VkWarningDialog class to post a warning dialog.
After posting a dialog, you might encounter situations in which you want to unpost it even though the user has not acknowledged and dismissed it. For example, your application might post an information dialog that the user doesn't bother to acknowledge. At some later point, the information presented in the dialog might no longer be valid, in which case the application should unpost the dialog.
In situations such as these, you can use the VkDialogManager::unpost() function to remove the dialog:
If you provide the widget ID of a specific dialog, unpost() dismisses that dialog. Otherwise, unpost() dismisses the most recent dialog of that class posted.
If you want to dismiss all dialogs of a given class, you can call the VkDialogManager::unpostAll() function:
void unpostAll()
For example, the following dismisses all information dialogs currently posted:
theInformationDialog->unpostAll();
By default, ViewKit ObjectPak sets the title of a dialog (displayed in the window manager title bar for the dialog) to the name of the application; however, you have the ability to set dialog titles on both a per-class and per-dialog basis.
If you want all dialogs of a certain class to have a title other than the default, you can specify the title with an X resource. For example, you could set the title of all warning dialogs in an application to "Warning" by including the following line in a resource file:
*warningDialog.dialogTitle: Warning
You can use the VkDialogManager::setTitle() function to set the title for the next dialog of that class that you post:
void setTitle(const char *nextTitle = NULL)
setTitle() accepts as an argument a character string. setTitle() first treats the string as a resource name which it looks up relative to the dialog widget. If the resource exists, setTitle() uses the resource value as the dialog title. If setTitle() finds no resource, or if the string contains spaces or newline characters, it uses the string itself as the dialog title.
setTitle() affects only the next dialog posted; subsequent dialogs revert to the default title for that class.
For example, imagine an editor that uses the question dialog to post two dialogs, one that asks "Do you really want to replace the current buffer?" and one that asks "Do you really want to exit?" If you want different titles for each dialog, you could define resources for each:
Then to post the question dialog for replacing the buffer, call:
Figure 25. shows the resulting dialog.
Figure 25. Example of Setting the Dialog Title
To post the exit question dialog as a modal dialog, call:
Figure 26. shows the resulting dialog.
Figure 26. Another Example of Setting the Dialog Title
The button labels (the text that appears on the buttons) used for a dialog are controlled by the XmNokLabelString, XmNcancelLabelString, and XmNapplyLabelString resources. The default values of these resources are respectively "OK", "Cancel", and "Apply".
You can use the VkDialogManager::setButtonLabels() function to set the button labels for the next dialog that you post:
setButtonLabels() accepts as arguments up to three character strings: the first string controls the label for the OK button, the second the label for the Cancel button, and the third the label for the Apply button. If you pass NULL as an argument for any of these strings, the corresponding button uses the default label. setTitle() first treats each string as a resource name which it looks up relative to the dialog widget. If the resource exists, setTitle() uses the resource value as the button label. If setTitle() finds no resource, or if the string contains spaces or newline characters, it uses the string itself as the button label.
setButtonLabels() affects only the next dialog posted; subsequent dialogs revert to the default button labels.
The VkDialogManager class also provides some access and utility functions to help manipulate dialogs.
The VkDialogManager::centerOnScreen() function controls the algorithm that ObjectPak uses to determine where on the screen to post a dialog:
void centerOnScreen(Boolean flag)
If flag is TRUE, ObjectPak uses the following algorithm:
If flag is FALSE, ObjectPak uses the following algorithm, the default algorithm:
The VkDialogManager::lastPosted() returns the widget ID of the last dialog posted of that class:
Widget lastPosted()
ObjectPak encapsulates dialog management, including caching, in the abstract VkDialogManager class that serves as a base class for other, specific dialog classes. Each type of dialog in ViewKit ObjectPak has a separate class derived from VkDialogManager. Each class is responsible for managing its own type of dialog (for example, each class maintains its own dialog cache).
The dialog classes provided by ObjectPak fall into the following three categories:
The information and error dialogs provide feedback to the user about actions or conditions in the application. The following table describes the dialog classes in this category:
The busy dialogs inform the user that an action is underway which might take considerable time. While a busy dialog is displayed, the user cannot interact with the application. The following table describes the dialog classes in this category:
Dialog Class |
Description |
---|---|
VkBusyDialog |
Busy or working dialog displayed while the application is busy. |
VkInterruptDialog |
Busy or working dialog that allows the user to interrupt the action. |
The data input dialogs allow the application to request input from the user. The following table describes the dialog classes in this category:
Additionally, ViewKit ObjectPak provides the VkGenericDialog class, an abstract class providing a convenient interface for creating custom dialogs that use the ObjectPak interface.
Do not directly instantiate dialog manager objects in your program for the predefined dialog types. ObjectPak automatically creates an instance of an appropriate dialog manager if you attempt to use a predefined dialog type in your program.
The header file for each dialog class provides a global pointer to the instance of that class's dialog manager. The name of the pointer consists of "the" followed by the dialog type. For example, the global pointer to the information dialog manager declared in <Vk/VkInfoDialog.h> is theInfoDialog, the global pointer to the error dialog manager declared in <Vk/VkErrorDialog.h> is theErrorDialog, and so forth. To access the dialog managers in your application, use these global pointers.1
Note: VkGenericDialog, being an abstract class designed for creating customized dialogs, does not automatically create a dialog manager or provide a global pointer.
Creating all the dialogs your application uses when you start the application is inefficient. The dialogs, which might or might not be displayed, take time to create, consume memory, and tie up server resources. If an application does not create a dialog until necessary, the application is smaller and has faster initial startup time. However, depending on the performance of the system, an unacceptable delay in posting each dialog might occur because the application must create a new dialog for each message.
ViewKit's compromise is to cache dialogs upon creation. When a particular dialog is no longer required, the application unmanages that dialog but retains in cache. If the cache contains an unused dialog widget when the application needs to post a dialog, the application reuses the cached dialog widget; otherwise, it creates a new dialog widget. ViewKit ObjectPak caches up to one dialog of each class for each window in the application (for example, information dialogs and question dialogs are cached separately).
ViewKit ObjectPak dialog classes also offer the following features:
You can think of ViewKit ObjectPak as consisting of several sets of classes: framework classes, interface components, network licensing, interapplication communication, and convenience utilities. The following sections discuss these groups.
ViewKit ObjectPak provides a small set of classes that are either essential for all applications or provide fundamental support for all other classes. The most basic of these classes is the VkComponent class, which defines the basic structure of all user interface components. All user interface classes are derived from VkComponent.
The framework classes also include support for features needed by nearly all applications, including application management and X server setup, top-level windows, menus, and dialog management. All classes are designed to implement as many typical features as possible. For example: all top-level windows and dialogs handle the window manager quit/close protocol; dialogs are cached to balance memory use and display speed; the menu system goes beyond simply constructing menus to support dynamically adding, removing, replacing items, and more.
The classes that make up the framework of ViewKit ObjectPak are closely integrated and work together to support essential features required by most applications as automatically as possible. Among the basic services supported by the core ViewKit ObjectPak framework are: single and multi-level undo; interruptible tasks; and an application-level callback mechanism that allows C++ classes to dynamically register member functions to be invoked by other C++ classes.
In addition to the basic user interface support provided by the core framework classes, ViewKit provides an assortment of ready-to-use interface components. Examples of these components are a graph viewer/editor, an input field that supports name expansion, and an outliner component for displaying and manipulating hierarchical information.
Use the architecture of ViewKit ObjectPak to create new components and extend existing components. Creating reusable, high-level components promotes consistency throughout a set of applications by providing elements that users can learn once and then easily recognize in multiple applications.
Many applications need to be able to communicate with other applications to work effectively. ViewKit ObjectPak builds on the ToolTalkTM interapplication communications service to support simple and effective interapplication communication.
ViewKit ObjectPak provides various utility functions and classes for your convenience. These utilities include simple functions that make it easier to load resources (including automatic type conversion), classes that support the use of icons, and other miscellaneous utilities.
The VkApp::run() function is ObjectPak's main event loop. run() implements the event handling normally supported by XtAppMainLoop() or XtMainLoop(). Additionally, run() also supports events not normally handled by the Xt dispatch mechanism. For example, run() can handle events registered for non-widgets (such as a PropertyNotify event on the root window).
When run() receives an event not handled by the Xt dispatch mechanism, it calls the virtual function VkApp::handleRawEvent():
virtual void handleRawEvent(XEvent *event)
The default action of VkApp::handleRawEvent() is to pass the event to the handleRawEvent() function of each instance of VkSimpleWindow (or subclass) in the application. By default, these members function are empty.
To handle events through this mechanism, call XSelectInput(3) to select the events that you want to receive, and override handleRawEvent() in a VkApp or VkSimpleWindow subclass to implement your event processing. Generally, in keeping with object-oriented practice, you should override handleRawEvent() in a VkSimpleWindow subclass rather than a VkApp subclass, unless your event processing has an application-wide effect. If you override VkApp::handleRawEvent() in a derived class, call the base class's handleRawEvent() function after performing your event processing.
Note: If you explicitly call XtNextEvent(3) and XtDispatchEvent(3) in your application, you should pass any undispatched events to handleRawEvent().
In addition to the automatic event dispatching provided by run(), you can force your application to handle all pending events immediately by calling VkApp::handlePendingEvents():
virtual void handlePendingEvents()
This function retrieves and dispatches all X events as long as there are events pending. Unlike XmUpdateDisplay(3Xm), which handles only Expose events, handlePendingEvents() handles all events. In other words, handlePendingEvents() does not just refresh windows, it also handles all pending events including user input. You might want to call this function periodically to process events during a time-consuming task.
This section describes how to build and manipulate graphs using the VkGraph class.
Minimally, you must perform the following actions to build and display an ObjectPak graph:
The VkGraph constructor is simple with few arguments. You must provide a name and the parent widget for the graph:
VkGraph(char *name, Widget parent)
The VkGraph destructor destroys the graph. It does not destroy any VkNode objects that are part of the graph.
After you create nodes, you must add them to the graph object you created. Also, if you didn't specify the parent-child relationship for the nodes when you created them, you should supply the remaining connectivity information when adding the nodes to the graph. (See "ViewKit Node Class" for information on creating nodes.)
The VkGraph::add() function adds nodes to a graph object:
virtual int add(VkNode *node)
virtual void add(VkNode *parent, VkNode *child,
char *attribute = NULL)
If you supply only one node pointer as an argument, add() simply adds the node to the graph. If you have already added the node to the graph, add() does nothing.
If you supply two node pointers as arguments, add() adds both nodes to the graph and establishes the first node as the parent of the second node. If you have already added either node to the graph, add() does not add the node again, but it does establish the parent-child relationship between the nodes.
Note: The second form of add() establishes the parent-child relationship between nodes even if one already exists. Thus, it is possible to have more than one connection between nodes. By default, the graph displays only a single arc between connected nodes, even if you define multiple connections between the nodes. However, as described in "Interactive Viewing Features Provided by VkGraph" on page 256, by clicking on the graph's Multiple Arcs button the user can force the graph to an arc for each connection you defined. To turn off multiple-arc display, the user can click again on the Multiple Arcs button.
When specifying a parent/child connection using add(), you can specify an attribute for that connection. An attribute is an arbitrary name that you can use to control the appearance of the arc widget that connects the two nodes. For example, assume that you add two nodes to a graph as follows:
graph->add(parent, child, "primary");
graph->add(parent, child, "secondary");
The resulting graph displays two connecting arcs between the two nodes.
You can now specify X resources to control various aspects of the arc. For example:
*primary*foreground: red
*primary*arcDirection: bidirected
*secondary*foreground: blue
*secondary*arcDirection: undirected
*secondary*style: LineOnOffDash
You can use this method to set many of the resources supported by the SgArc widget. The resources you can specify are: XmNforeground, XmNtoSide, XmNfromSide, XmNfromPosition, XmNtoPosition, XmNarcDirection, XmNfontList, XmNarcWidth, XmNstyle, and XmNdashes. See the SgArc(3) man page for details on these resources.
The following code fragment creates a graph, creates two nodes, establishes a parent-child relationship between the nodes, and adds the nodes to the graph:
graph = new VkGraph("graph", parent);
p_node = new VkNode("parentNode", "Parent");
c1_node = new VkNode("childNode1", p_node, "Child 1");
graph->add(p_node);
graph->add(c1_node);
Note: In this example, the connection between the two nodes is established when you create c1_node. Therefore, you must add the nodes to the graph using separate calls to add().
If, instead of the two separate calls, you execute:
graph->add(p_node, c1_node);
then you not only add the two nodes to the graph, but you establish a second connection between the nodes.
You can accomplish the same result as above by creating the nodes without providing the parent-child relationship, and then specifying the connection when you add the nodes to the graph. The following code fragment is functionally equivalent to that shown above:
graph = new VkGraph("graph", parent);
p_node = new VkNode("parentNode", "Parent");
c1_node = new VkNode("childNode1", "Child 1");
graph->add(p_node, c1_node);
You can remove nodes from a graph using VkGraph::remove():
virtual void remove(VkNode *node, Boolean deleteNode = FALSE)
By default, remove() removes the node from the graph but does not delete it. If you set the deleteNode argument to TRUE, remove() deletes the node when it removes it.
Once you have added all nodes to a graph and specified their connectivity, you must indicate which nodes the graph should display. VkGraph provides many functions that allow you to display or hide all of the graph, individual nodes, and portions of node subtrees.
After displaying nodes, you should call one of the graph layout member functions as described in "Laying Out the Graph" . Otherwise, the nodes might not display in desired locations.
The basic display functions are VkGraph::displayAll() and VkGraph::clearAll():
displayAll() displays all nodes and clearAll() hides all nodes. Typically, after creating your graph, you execute displayAll() to display all of the nodes. For example:
graph->displayAll();
Sometimes you might want to display only portions of your graph. VkGraph provides functions to operate on either single nodes or subtrees of nodes.
The VkGraph::display() function displays a single node:
virtual void display(VkNode *child)
virtual VkNode *display(char *name)
You can provide display() with either a pointer to the node or the component name of the node. If you provide the node's name, this function returns a pointer to the node.
VkGraph::undisplay() hides a single node:
virtual void undisplay(VkNode *node)
virtual void hideNode(VkNode *node)
VkGraph::hideNode() is equivalent to undisplay().
VkGraph also provides a large number of functions that display or hide portions of the graph:
virtual void displayWithParents(VkNode *node)
virtual VkNode *displayWithParents(char *name)
virtual void displayWithAllParents(VkNode *node)
virtual VkNode *displayWithAllParents(char *name)
virtual void hideParents(VkNode *node)
virtual void displayParentsAndChildren(VkNode *node)
virtual VkNode *displayParentsAndChildren(char *name)
virtual void hideParentsAndChildren(VkNode *node)
You can also create your own functions for determining whether or not nodes are displayed and then use the VkGraph::displayIf() function to apply those functions:
virtual void displayIf(VkGraphFilterProc)
The type definition of VkGraphFilterProc is:
typedef Boolean (*VkGraphFilterProc) (VkNode *)
The function you provide must be a static function that accepts a node as an arguments and returns TRUE if the node should be displayed.
Note: displayIf() does not hide (that is, call undisplay()) if the filter function returns FALSE for a node. Therefore, to display only those nodes for which the filter function returns TRUE, you must first call clearAll().
For example, the following function displays only those nodes whose names begin with the string "state":
The final step in displaying a graph is to lay it out. Laying out the graph arranges the widgets in a logical manner and then manages the widgets.
To lay out the entire graph, call the VkGraph::doLayout() function, which applies the layout algorithm to the entire graph and then manages all widgets associated with the graph:
void doLayout()
If you modify the graph after displaying it, or if you allow the user to edit the graph interactively, the graph might become cluttered and you might want to lay out the graph again. To do so you can call doLayout() again to force the graph to reapply the layout algorithm to the graph to clean up the display. As an example, the Realign button provided on the graph command panel simply calls doLayout() whenever the user clicks on the button.
If, after displaying the graph, you display any additional nodes (for example, using the VkGraph::display() function), you must force a layout of the graph to manage all the widgets you created. You can call doLayout() again to do so, but this applies the layout algorithm to the entire graph. Doing so could produce major changes in the layout of the entire graph, which could be disruptive and undesired if the user has previously moved nodes. Also, it could take considerable time if the graph is large. In this case, you can instead call the VkGraph::doSubtreeLayout() function which, given a root node, applies the layout algorithm to just a subtree of the graph:
void doSubtreeLayout(VkNode *node)
For example, the following code fragment illustrates displaying a graph, graph, and then displaying another node, newNode:
VkGraph::doSparseLayout() is a special-purpose build and layout function that displays the relationship between a node and its grandparent nodes even if the node's parents are not displayed:
void doSparseLayout()
doSparseLayout() performs a special build of the graph and whenever it finds a node with an undisplayed parent node, it checks to see whether there are any displayed grandparent nodes. If doSparseLayout() finds such grandparent nodes, it creates a dashed-line arc (instead of a solid-line arc) to connect the node and its grandparent nodes. After finishing the build process, doSparseLayout() performs a layout of the entire graph and manages all widgets associated with the graph.
So far, this chapter discussed creating tree graphs using the VkGraph class. However, VkGraph also supports butterfly graphs, which display only a central node and its immediate parent and child nodes. The central node of a butterfly graph is called the butterfly node.
VkGraph can construct a butterfly graph from any graph specification. Call VkGraph::displayButterfly() to specify one node as the butterfly node; VkGraph automatically determines which nodes to display:
Then call VkGraph::doLayout() to lay out the graph as you normally would. For example, assuming that you have already defined a graph specification for a graph called graph, the following code fragment would instruct the graph object to display a butterfly graph centered on the node centerNode:
After displaying a butterfly graph, you can use displayButterfly() to specify a new butterfly node and display a different butterfly graph given the same graph specification. For example, the following code fragment illustrates setting a new butterfly node, newCenter, after displaying the butterfly graph in the example above:
After displaying a butterfly graph, you can return to displaying a normal tree graph by setting the layout style to XmGRAPH using the VkGraph::setLayoutStyle() function:
virtual void setLayoutStyle(char type)
For example, the following code fragment illustrates displaying the entire graph specified by graph after displaying the butterfly graphs above:
As discussed in "Interactive Viewing Features Provided by VkGraph" , by clicking on the Graph Overview button in the graph command panel, a user can display an overview of all a graph's visible nodes.
You can also display the overview window programmatically using VkGraph::showOverview():
void showOverview()
Call VkGraph::hideOverview() to programmatically hide the overview window:
void hideOverview()
You can obtain a pointer to the overview window's VkWindow object using VkGraph::overviewWindow():
VkWindow *overviewWindow()
VkGraph provides the following utility functions:
virtual void setZoomOption(int index)
void sortAll()
typedef void (*VkGraphNodeProc) (VkNode *)
The function you provide must be a static function that accepts a node as an arguments and has a void return value.
virtual void forAllNodesDo(VkGraphNodeProc function)
void makeNodeVisible(VkNode *node)
void saveToFile()
void setSize(int entries)
VkGraph provides the following access functions for obtaining values associated with the graph:
int numNodes()
VkNode *find(char *name)
Widget graphWidget()
Widget workArea()
Widget twinsButton()
Widget relayButton()
Widget reorientButton()
Occasionally, after displaying one graph, you might want to display an entirely different graph. The simplest method of accomplishing this is to create another VkGraph object for the new graph.
However, creating a new graph object entails the overhead of creating many new widgets and data structures. Sometimes it is simpler, faster, and more appropriate to re-use the existing graph object. For example, consider a window in which you are displaying a graph of C++ class hierarchies associated with a program. The window might contain controls that allow the user to select other programs to examine. If the user selects a new program to examine, the most convenient thing to do would be to keep the existing graph object but "clear it" of all existing information.
The VkGraph::tearDownGraph() function provides this ability:
virtual void tearDownGraph()
It tears down the graph by destroying all arc and node widgets and deleting all VkNode objects associated with the graph. This function is equivalent to deleting all VkNode objects associated with the graph, deleting the graph object, and creating a new graph object with the same name, but entails less overhead processing than if you were to explicitly perform these actions separately.
The VkGraph class declares two ObjectPak member function callbacks and activates the VkGraph::arcCreatedCallback whenever the graph creates a SgArc widget to connect two nodes. The arcCreatedCallback callback includes as call data the newly created SgArc widget. See the SgArc(3) reference pages for information on the SgArc widget.
VkGraph activates the VkGraph::arcDestroyedCallback whenever the graph destroys all arc widgets as a result of a call to VkGraph::clearAll() (see "Indicating Display Nodes" ). VkGraph activates the arcDestroyedCallback callback once for every arc destroyed, including as call data the SgArc widget destroyed. See the SgArc(3) reference pages for information on the SgArc widget.
VkGraph sets several X resources that specify the labels of its popup menus. You can override these values in an app-defaults file if you want to provide your own labels. The resources and their default values are:
VkGraph provides much of the functionality required for displaying and manipulating graphs. In most other cases, you can use the graphWidget() access function to obtain a pointer to the SgGraph widget for direct operations on the widget.
However, you might want to perform additional processing when certain actions occur. You can create a subclass of VkGraph which provides several hook" functions that you can override to implement additional functionality:
virtual void buildCmdPanel(Widget parent)
virtual void buildZoomMenu(Widget parent)
virtual void addMenuItems(VkPopupMenu *menu)
You can override this function if you want to change its behavior or support any additional menu items that you added by overriding addMenuItems().
virtual void popupMenu(VkNode *node, XEvent *event)
virtual void addDesktopMenuItems(VkPopupMenu *menu)
virtual void twinsVisibleHook(Boolean state)
VkGraph is a self-contained ObjectPak component for displaying and manipulating complex arc-and-node graphs. The graph can be disconnected and can contain cycles. VkGraph can arrange the nodes horizontally or vertically and change the orientation interactively. VkGraph also provides controls for interactive zooming, node repositioning, and node alignment. Figure 42. shows an example of a graph created using the VkGraph component.
Figure 42. Example of a Graph Created with VkGraph
All nodes displayed by a VkGraph component must be instances of the VkNode class or subclasses that you derive from it. By default, VkNode creates a SgIconGadget(3), but if you create a subclass VkNode, you can use any widget for a node.
The basis of the VkGraph class is the SgGraph widget, which manages and displays the graph. This section provides an overview of the SgGraph widget. For in-depth information on interacting with the graph widget, consult the SgGraph(3) man page.
A primary responsibility of the SgGraph widget is to clearly and systematically lay out the nodes. The graph layout algorithm is a simple and efficient tree layout algorithm designed to handle forests of nodes. It lays out nodes as a multi-rooted tree.
By default, the graph widget created by the VkGraph class operates in a read-only mode in which the graph widget is used primarily as a layout manager for arranging the node widgets. By modifying certain SgGraph resources, you can also interactively edit the displayed graph, creating and moving arcs and nodes. However, to support most of the functionality of the edit mode, you must provide callback functions and other information to the graph widget so that you can interpret the edit operations and use them in your program. Refer to the SgGraph(3) man page for details on the resources and callbacks used for edit mode.
The process of building and displaying a graph using the VkGraph component consists of the following steps:
Example 40. illustrates this process by showing the code used to create the graph shown in Figure 42.
This example creates a VkWindow subclass to contain the graph. The graph itself is created in the GraphWindow constructor, as follows:
In addition to displaying a graph, VkGraph automatically provides controls for interactively manipulating the graph. One set of controls is contained in the control panel, shown in Figure 43., which appears along the bottom of the graph.
Figure 43. Graph Command Panel
The control panel contains buttons and a menu that allow the user to interactively control various characteristics of the graph's display.
Using the control panel, the user can achieve the following results:
Additionally, VkGraph automatically creates popup menus that contain commands that allow the user to hide and display nodes in the graph.
VkGraph provides eight preset zoom settings that allow the user to shrink or enlarge the size of the graph. The user can directly set the zoom value using the Zoom menu shown in Figure 44.
Figure 44. Interactively Changing the Graph Zoom Value
Clicking the Zoom Out button (the down-arrow button immediately to the right of the Zoom menu) changes the zoom setting to the next lower value, and clicking on the Zoom In button (the up-arrow button to the right of the Zoom Out button) changes the zoom setting to the next higher value.
The user can display an overview of all a graph's visible nodes by clicking on the Graph Overview button.
Within the overview window is a viewport that represents the boundaries of the graph visible in the main graph window. The user can click on the viewport and drag it to a new location to change the area visible in the main graph window. As the user drags the viewport, the main graph window scrolls to match the viewport's location in the overview.
The overview window also contains an Admin menu with the commands listed in the following table:
By default, the graph displays only a single arc between nodes, even if you define multiple connections between the nodes. The user can click on the Multiple Arcs button to display multiple arcs between nodes; the graph displays an arc for each connection you defined. The user can turn off multiple-arc display by clicking again on the Multiple Arcs button.
Occasionally, as a result of moving or displaying nodes, your graph display might become cluttered. The Realign button "cleans up" the graph display by laying out all visible nodes again.
The default graph orientation is horizontal. The user can change to a vertical orientation by clicking on the Rotate Graph button. The user can return to the horizontal orientation by clicking again on the Rotate Graph button.
VkGraph provides controls that allow the user to hide a single node, reveal a node's parents or children, or collapse the part of the graph that branches from a node. To perform any of these actions, the user moves the pointer onto the node and presses the right mouse button to open the popup Node menu. The Node menu contains four commands; only commands applicable to that node are made available. Nonapplicable commands are grayed.
The following table lists the Node menu commands:
There are additional operations that a user can perform if you set the graph to edit mode, as described in "Graph Widget" . By default, the graph widget created by the VkGraph class operates in read-only mode. You can set the graph widget to edit mode in a VkGraph subclass.
Note: To support much of the functionality of the edit mode, you must provide callback functions and other information to the graph widget so that you can interpret the edit operations and use them in your program. Refer to the SgGraph(3) man page for details on the resources and callbacks used for edit mode.
You must select one or more nodes before you can perform an operation on it. You can select nodes only if the graph is in edit mode. By default, the graph is created in display-only mode.
To perform most operations in edit mode, the user must first select one or more nodes.
The user can perform the following operations on nodes:
The popup Selected Nodes menu allows the user to perform an operation on all selected nodes. To open the Selected Nodes menu, the user moves the pointer to any blank area of the graph, and presses the right mouse button.
The following table lists the Selected Nodes menu commands:
ObjectPak provides a rudimentary help library, libvkhelp, that you can use if you do not want to implement your own. The ObjectPak help library does not provide extensive help browsing or other search features; however, it does allow you to include help messages for your application by defining them in the X resource database.
The source for the ObjectPak help library is included in ${ObjectPak}/Utilities/VkHelpAPI.C.Examine this source to get ideas for implementing your own help library.
The ObjectPak help library is simple enough not to require any initialization, so SGIHelpInit() is defined to simply return the value 1.
Both SGIHelpMsg() and SGIHelpIndexMsg() are defined to accept the in_key character token argument and look up the resource "in_key.helpText" in the X resource library. They then display the help text retrieved from the resource database in an OSF/Motif information dialog. If these function cannot find an appropriate resource value, they display the message "Sorry, no help available on this topic" in the dialog.
The following lines show how you create the help message specifications for an application:
In this example, the "*helpText" resource specification provides a default help message for the entire application. If a widget does not have a more specific help message resource specification, the application displays this default help message.
The "*row1*helpText" and "*row2*helpText" resource specifications provide help messages for these widgets and their descendants. For example, you could use a specification like this to provide a help message for a group of toggles or pushbuttons in a RowColumn widget.
The "*row2*start*helpText" specification provides a help message for a "start" widget, a descendant of the "row2" widget. It overrides the "*row2*helpText" message.
"*overview*helpText" provides a message that the application displays when the user selects "Overview" from the Help menu.
The VkMeter class supports simple compound bar charts, displayed in either vertical or horizontal mode. If you display multiple values, the data is presented in layers, with the bar representing the second value starting where the first value ends.
The VkMeter accepts the standard ViewKit ObjectPak component constructor arguments: a component name and a parent widget:
VkMeter(const char *name, Widget parent)
You should rarely need to create subclasses of VkMeter.
The VkMeter destructor frees all space associated with the meter:
~VkMeter()
Before adding any items for display to a VkMeter object, you must call VkMeter::reset() to reset the meter:
void reset(int peak = -1)
First value peak sets the initial peak value displayed by the meter. All items displayed by the meter are scaled relative to the peak value. For example, if the peak value is 200 and one of your items is 40 units long, that item scaled to take 20% of the meter's total length. Default peak size is 100 units.
Note: To change meter values or otherwise update a meter object, you must call reset() and then add the items to the meter again.
You add items for a VkMeter object to display with VkMeter:add():
The value argument is the item's value. When displayed, the VkMeter class scales this value relative to the peak value set by reset(). For example, if the peak value is 500 and one of your items is 80 units long, that item will be scaled to take 16% of the meter's total length.
When you use these forms of the add() function, the VkMeter object displays the items sequentially. For example, if you have set the peak value to 100 and you add three items with values of 20, 10, and 30 in that order, the meter displays three bars: the first ranging from 0 to 20, the second from 20 to 30, and the third from 30 to 60.
All data items must have an associated color. You can specify the color as a Pixel value, pixel, or as a string, color. If you provide a string, add() first treats the string as the name of a resource that add() looks up relative to the component and converts to the desired color. If add() finds no such resource, it uses the string itself as the name of a color. For example, the following adds an item with the color "red":
add(10, "red");
The following adds an item with the color specified by the resource name "criticalColor":
add(20, "criticalColor");
You can specify the width of an item by providing a width argument, expressed in pixels. If you do not provide a width, the width of the item is the same as the width of the meter.
Two more complex forms of add() allow you to precisely control the position of bars in a meter, and even display bars side by side:
In these forms of add(), the first value, start, specifies the starting position of the bar, and the second value, size, specifies the size (length) of the bar. VkMeter scales these values relative to the peak value set by reset(). The third argument, sideValue, and the fourth argument, width, specify values in the opposite dimension. VkMeter does not scale these values relative to the meter's peak value.
For example, consider a meter with a peak value of 100. The following lines add four bars to the meter:
If you display this meter vertically, it shows three vertical bars ranging from 0 to 20 side by side in red, blue, and green. Above them is a yellow bar spanning all of them and ranging from 20 to 40.
After adding all items to a meter, call the VkMeter::update() function to update the meter's display:
void update()
Note: Remember that if you want to change the meter display, you must first call reset() and then add each item in the new display.
The meter you create can have either a fixed size or it can attempt to resize itself dynamically as it requires more or less room to display the items it contains. You can specify the meter's resize policy with VkMeter::setResizePolicy():
void setResizePolicy( unsigned char policy )
You can provide any of the following values:
XmRESIZE_NONE The meter never attempts to resize itself. The application, or managing widget, is in complete control of the meter's size.
XmRESIZE_GROW The meter calls XtSetValues() on the widget used to display the meter to attempt to grow as needed. The success of the call to XtSetValues() depends on the parent widget's geometry management policy.
XmRESIZE_ANY The meter calls XtSetValues() on the widget used to display the meter to attempt to grow or shrink as needed. The success of the call to XtSetValues() depends on the parent widget's geometry management policy.
You can determine the dimensions that a meter needs to display itself completely by calling VkMeter::neededWidth() and VkMeter::neededHeight():
The following X resources are associated with the VkMeter class:
XmNorientation Determines the orientation of the meter. The default value is XmVERTICAL which specifies a vertical meter. Set the value of the resource to XmHORIZONTAL for a horizontal meter.
XmNresizePolicy Determines the resize policy of the meter, as described in "Setting the Meter's Resize Policy" . The default value is XmRESIZE_NONE.
XmNdrawBorder Determines whether bars are drawn with borders. The default value is FALSE, in which case bars do not have borders. If you set the value to TRUE, bars have borders drawn in the color specified by the XmNborderColor resource.
The VkOutline component, derived from VkComponent, displays a textual outline. VkOutline automatically indents items according to their depth in the outline. Figure 61. shows an example of a VkOutline component containing three top-level items, each with several subitems.
Figure 61. Example of a VkOutline Component
If there is not sufficient space to display the entire outline, the VkOutline component automatically displays a scrollbar, as shown in Figure 62..
Figure 62. Example of a VkOutline Component with the Scrollbar Visible
The VkOutline component displays a control icon to the left of each outline item that contains subitems. The control icon denotes whether the sub-tree under the item is displayed (open) or not (closed). The user can click the left mouse button on the control icon to toggle between the open and closed states.
Figure 63. shows the results of closing the item "Subheading 2B," shown in the previous figure.
Figure 63. Example of Closing a Heading in a VkOutline Component
The VkOutline constructor accepts the standard ViewKit ObjectPak component constructor arguments: a component name and a parent widget:
VkOutline (const char *name, Widget parent)
You can add items to the outline in a simple parent-child relation with VkOutline::add():
void add(char* parentName, char* childName)
The actions performed by add() depend on whether either or both of the items already exist in the outline.
parentName and childName are used both as item names and the text displayed in the outline. Note that you must use unique names for each item in the outline.
To add multiple subitems to an existing item, use VkOutline::addChildren():
The character string array parentPath specifies the complete path of the parent item through the outline. The first element of the parentPath array is the name of the top-most item of the outline containing the specified item, the second element is the name of the second-highest item, and so on, with the name of the item itself appearing last. You must NULL-terminate the array.
The character string array childNames contains the names of the subitems to add to the specified parent item. Note that you must use unique names for each item in the outline.
In the second form of addChildren(), you can provide childLabels, an array of character strings that provide display labels for the subitem you add. VkOutline displays these labels for the items instead of the item names.
In the second form of addChildren(), you can also provide childData, an array of pointers to arbitrary data. You can retrieve a pointer to the data associated with an item using VkOutline::getHookAt(), described in "Outline Utility and Access Functions" . Usually you need to use this data only if you create a subclass of VkOutline. In a subclass, you can add callbacks so that when the user selects an outline item, you can retrieve the data associated with that item and perform some action.
VkOutline::createPath() creates or extends a path in the outline:
void createPath(char** itemLabels, char** itemNames)
The character string array itemNames specifies a path through the outline. The first element of the itemNames array is the name of the top-most item of the outline containing the specified item, the second element is the name of the second-highest item, and so on, with the name of the item itself appearing last. You must NULL-terminate the array.
If path does not exist, then createPath() creates a new set of items with the first element in the path as the top-level item, the second element a subitem of the first, and so on. If createPath() finds a partial match in the existing outline, where the first element of itemNames matches the name of an existing top-level item and one or more lower-level items match succeeding elements of itemNames, createPath() adds those items needed to fully extend the path.
For those items that createPath() adds, it uses the corresponding elements from the itemLabels character string array as the display labels for those items. VkOutline displays these labels for the items instead of the item names.
Note: createPath() does not alter the labels for any existing items. createPath() uses the labels only when adding new items.
Whenever you add items to the outline, no matter which function you use to add them, you must call VkOutline::displayAll() to update the outline display:
void displayAll()
VkOutline allows you to designate items as "keywords" and display them in a different foreground color, background color, and/or font. You can also define up to four custom item highlights, each wi7th its own foreground color, background color, and font attributes.
Use VkOutline::setKeywordAttributes() to define the keyword display attributes:
fg is the foreground color for the item's text. bg is the background color for the item. font is the font used to display the item's text.
Use VkOutline::displayAsKeyword() to display an item with the keyword display attributes:
void displayAsKeyword(char** path)
You specify the complete path of the item through the outline as an array of character strings. The first element of the path array is the name of the top-most item of the outline containing the specified item, the second element is the name of the second-highest item, and so on, with the name of the item itself appearing last. You must NULL-terminate the array. Note that displayAsKeyword() requires the item names, not their display labels.
Use VkOutline::setHighlightAttributes() to define the display attributes of a custom highlight:
fg is the foreground color for the item's text. bg is the background color for the item. font is the font used to display the item's text. setHighlightAttributes() returns an integer identifier for the highlight. You use this identifier to apply the highlight to outline items with the highlight() function described below. If setHighlightAttributes() could not allocate a custom highlight, it returns 0.
Use VkOutline::highlight() to display one or more items with display attributes of a custom highlight:
In the first form of highlight(), you specify the position index in the outline of the item you want to highlight. Items are numbered sequentially from the top of the outline starting with zero. attribID is the attribute identifier returned by setHighlightAttributes() of the custom highlight that you want to assign to the items.
In the second form of highlight(), items is an array of strings specifying the names of items to highlight. highlight() requires the item names (not display labels. attribID is the attribute identifier (returned by setHighlightAttributes()) of the custom highlight that you want to assign to the items.
You cannot remove a custom highlight from individual items; you can only remove the highlight from all items to which you have applied it. VkOutline::unhighlight() removes a custom highlight:
void unhighlight(int attribID)
attribID is the attribute identifier (returned by setHighlightAttributes()) of the custom highlight that you want to assign to the items.
To programmatically toggle an outline item open or closed, use VkOutline::toggleChildren():
virtual void toggleChildren(int position)
position is the item's position in the SgList widget. Items are numbered sequentially from the top of the outline starting with zero.
To determine the effects of the last toggle operation, whether a result of user interaction or a call to toggleChildren(), call VkOutline::effectOfLastToggle():
int effectOfLastToggle(int& from, int& count)
If the last toggle operation opened an item (and therefore inserted items into the SgList widget), effectOfLastToggle() returns 1, sets the value of from to the position of the toggled item in the list, and sets the value of count to the number of items displayed by opening the item. If the last toggle operation closed an item (deleting items from the SgList widget), effectOfLastToggle() returns 0, sets the value of from to the position of the toggled item in the list, and sets the value of count to the number of items deleted from the list by closing the item.
To determine whether a given item is closed, use VkOutline::isPathClosed():
int isPathClosed(char** path)
The character string array path specifies the complete path of the item through the outline. The first element of the path array is the name of the top-most item of the outline containing the specified item, the second element is the name of the second-highest item, and so on, with the name of the item itself appearing last. You must NULL-terminate the array.
isPathClosed() returns 1 if item is closed, 0 if item is open, and -1 if item has no subitems.
VkOutline provides the following utility and access functions:
void setIndentationWidth(int width)
VkOutline::setIndentationWidth() sets indentation width for future displays. The indentation width is the number of pixels to the right that the outline offsets a child item from its parent item.
void printTree()
VkOutline::printTree() prints the outline on the application's standard output.
void reset()
VkOutline::reset() re-initializes the outline, deleting all items. reset() retains any display attributes you created.
Widget listWidget()
VkOutline::listWidget() returns the widget ID of the SgList widget that the VkOutline uses to display the outline. Consult the SgList(3) reference page for more information on the SgList widget.
void select(int position)
VkOutline::select() selects the string displayed at the given position of the SgList widget.
void getHookAt(int position)
VkOutline::getHookAt() retrieves the pointer to the data associated with an item given the item's position in the SgList widget. This is the data that you provided as the childData argument to addChildren() (see "Adding Items to an Outline" ).
Usually, you need to use this data only if you create a subclass of VkOutline. In a subclass, you can add callbacks to the SgList widget so that when the user selects an outline item, you can retrieve the data associated with that item and perform some action.
Using ViewKit and X and OSF Motif Functions
Compiling and Linking ViewKit Programs
ViewKit ObjectPak Callback Support
Deriving Subclasses to Create New Components
Running ViewKit ObjectPak Applications
ViewKit ObjectPak Event Handling
Exiting ViewKit ObjectPak Applications
Maintaining Product and Version Information
Application Data Access Functions
Undo Management and Command Classes
ViewKit ObejectPak Dialog Management
ViewKit ObjectPak Dialog Class
ViewKit ObjectPak Dialog Base Class
Using the ViewKit ObjectPak Dialog Subclasses
Overview of ViewKit ObjectPak Preference Dialogs
ViewKit Preference Item Base Class
ViewKit Preference Item Classes
ViewKit Preference Dialog Class
Miscellaneous Display Classes
ViewKit Support for Double-Buffered Graphics
Text Completion Field Component
Management Classes for Controlling Component and Widget Operation
Overview of ViewKit ObjectPak's ToolTalk Support
Establishing a Connection to the ToolTalk Service
Sending and Receiving ToolTalk Messages
Supporting Messaging in Application Windows
Supporting Messaging in Components
Registering Services for Autostart
ViewKit ObjectPak Meter Component
ViewKit ObjectPak Pie Chart Component
ViewKit ObjectPak Outline Component
User Interfaces to the Help System
ViewKit's Programmatic Interface to a Help Library
ViewKit ObjectPak help Library
ObjectPak Support for Building Help
The base preference dialog class, VkPrefDialog, is a subclass of VkGenericDialog, which is in turn a subclass of VkDialogManager. Thus, the VkPrefDialog class inherits all of the functions and data members provided by these base classes. For example, you post preference dialogs using the various post() variants, you set a preference dialog's title using the setTitle() function, and you set its button labels using the setButtonLabels() function.
Unlike the other dialog classes, VkPrefDialog does not create a global instance of a preference dialog. Instead, you must create a separate instance of VkPrefDialog for each preference dialog that you want to display in your program. For very simple preference dialogs (for example, just a few toggle buttons), you might be able to directly instantiate a VkPrefDialog object; however, in most cases you should create a separate subclass of VkPrefDialog for each preference dialog in your application. This is described in "Creating Preference Dialog Subclasses" .
The form of the VkPrefDialog constructor is:
VkPrefDialog(const char *name, VkPrefItem *item = NULL)
The VkPrefDialog constructor expects as its first argument the name of the preference dialog. The second argument is an optional pointer to a preference item that the dialog should use as the top-level preference item. See "Setting the Preference Items for a Preference Dialog" for more information on setting the top-level preference item.
For example, the following line creates a preference dialog named "simplePref":
A preference dialog can have only one top-level preference item. In most cases, you use a group item such as VkPrefList as the top-level item.
As described in "Creating a Preference Dialog" , you can set the top-level preference item in the VkPrefDialog constructor. You can also set the top-level item with the setItem() function:
void setItem(VkPrefItem *item)
Note: If the preference dialog already has a top-level preference item associated with it, setItem() replaces that item with the new item, but does not delete the old item. This allows you to reuse the old preference item later.
For example, the following line sets the item docList as the top-level item of the preference dialog simplePref:
simplePref->setItem(docList);
The item() function returns a pointer to the top-level item associated with a preference dialog:
VkPrefItem *item()
You post preference dialogs using any of the various post() variants provided by the base ViewKit ObjectPak dialog classes. You should not pass a message string argument to the post() function when posting a preference dialog.
For example, the following line posts the simplePref dialog as a non-modal, non-blocking dialog:
simplePref->post();
You should rarely have to unpost a preference dialog programmatically. ObjectPak automatically dismisses a preference dialog when the user clicks on either the OK or Cancel button. If for some reason you do need to unpost a preference dialog from your program, use the unpost() function.
When the user clicks on the OK or Apply button on a preference dialog, the dialog automatically applies any change of values to the preference dialog's items by setting the items's internally-stored values so that they match whatever is currently displayed on the screen. If the user clicks on the OK button, the preference dialog calls its hide() function to remove itself from the screen. If the user clicks on the Apply button, the preference dialog remains visible on the screen.
When the user clicks on the Cancel button on a preference dialog, the dialog automatically resets all of the dialog's preference items's on-screen values so that they match the items's internally-stored values. Additionally, the preference dialog calls its hide() function to remove itself from the screen.
The VkPrefDialog class also supplies an ObjectPak member function callback named prefCallback. The preference dialog activates this callback whenever the user clicks on the dialog's Apply, OK, or Cancel button. The callback passes as call data an enumerated value of type VkDialogReason, which is defined in VkDialogManager. The value can be any of VkDialogManager::OK, VkDialogManager::APPLY, or VkDialogManager::CANCEL, corresponding to the button that the user clicked on. To notify components in your application when the user changes preferences associated with a preference dialog, register member functions with this ObjectPak callback.
Note: When the user clicks on the OK button, ObjectPak first updates the preference items's internally stored values and activates the prefCallback callback with VkDialogManager::APPLY as the call data. Then, ObjectPak activates the prefCallback callback with VkDialogManager::OK as the call data. This is somewhat analogous to a Motif pushButton performing an activate() action followed by a disarm() action when a user clicks on it. Use this feature to perform certain actions whenever the user updates preference values by clicking on either the Apply or OK button, and a separate set of actions when the user dismisses the preference dialog by clicking on the OK button.
For example, consider a window, myWindow, that is a member of the subclass MyWindow, derived from VkWindow. In this example, assume that there is a preference dialog, displayPrefs, that is a member of the subclass DisplayPrefDialog, derived from VkPrefDialog, that allows the user to specify certain display parameters such as the font. myWindow could register its member function MyWindow::fontChanged() to be called whenever the user clicks a button in the preference dialog displayPrefs, by using the following line of code:
When MyWindow::fontChanged() is called, it checks to see if any of the parameters in which it is interested have changed and, if so, performs whatever processing is needed. For example:
To retrieve the value of a preference item, call the item's getValue() function. This implies that preference items must be accessible to all components that need to use preference values. For example, if you create a subclass for a preference dialog, declare as "public" those preference items that you want to access outside that dialog.
Example 39. shows the header for a NamePref subclass in which two preference items, firstName and lastName, are declared "public." These two preference items can be accessed by other components in the applications.
The NamePref subclass also contains a group, nameGroup, which is declared "protected." In most cases, outside components would not need to access a group item. One case in which it could be useful to make a group item publicly accessible is if you want other components to be able to activate and deactivate a group of preference items by calling the activate() and deactivate() functions on that group item.
The preferred method of handling preference dialogs in ViewKit applications is to create a separate subclass for each preference dialog in the application. Properly designed, a preference dialog can serve as a self-contained component that you can use in multiple applications.
The first step in creating a preference dialog subclass is to decide what preference items to include. List all of the information you want to be able to set with the preference dialog and determine which preference item class is appropriate for each item. For example, an item requiring text input is an obvious candidate for a VkPrefText item. However, an item allowing the user to choose one of several options can be handled by either a single VkPrefOption item or a number of VkPrefToggle items grouped with a VkPrefRadio item. Presumably, you want all of these preference items to be accessible outside of the preference dialog, so you want to declare these items in the "public" section of your class declaration.
Then determine the layout you want for the preference dialog. You should group similar items together so that a user can easily find and set related items. The layout determines what group items you need. Usually, you can define these items in the "private" or "protected" section of your class declaration; however, in some cases, you might want to declare some groups as "public." For example, you might want to be able to activate and deactivate a group of preference items by calling the activate() and deactivate() functions on that group item.
Then determine how you want to "publicize" changes in preference items to other components in your application. In many cases, those components can simply call the getValue() functions for appropriate items as needed. However, some components need to be notified immediately whenever certain preference items change. In most cases, these components can register ViewKit member function callbacks with the preference dialog that are called whenever the user clicks on one of the dialog's buttons. The components can then test for changes in preference item values in their callback functions and react accordingly.
In some cases, you might need to perform special processing when the user clicks on one of the preference dialog's buttons. In that case, you can override the default ok(), apply(), or cancel() function for the dialog. These functions are called whenever the user clicks on the corresponding button. In your override definition, you should perform whatever processing is needed and then call the base VkPrefDialog::ok(), VkPrefDialog::apply(), of VkPrefDialog::cancel() function as appropriate.
Usually you should also provide a set of default resource values to serve as labels for all the dialog's preference items. To do so, you must override the createDialog() function, which creates and manages all of the widgets in a preference dialog.
Your preference dialog's createDialog() function must perform the following tasks in order:
Example 37. shows a complete example of a preference dialog subclass. You could include DocPrefDialog dialogs in any application that needed to set various document parameters.
All preference items are derived from an abstract base class, VkPrefItem, which defines the structure of ViewKit ObjectPak preference items and provides a common set of manipulation functions.
Most preference items contain two top-level widgets: a base widget and a label widget. The base widget implements the control mechanism for the preference item (for example, a text field, an option menu, or a toggle button). The label widget (implemented as a gadget) displays a text label for the item.
The name of the base widget is the string "Base" appended to the name of the preference item as given in its constructor. The name of the label widget is the string "Label" appended to the name of the preference item as given in its constructor. So, if you create a VkPrefText object named "firstName," the name of the base widget is "firstNameBase" and the name of the label widget is "firstNameLabel."
To specify the string that is displayed as the label, you must set the XmNlabelString resource for the label widget. To set the XmNlabelString resource, use one of the following methods:
The code fragment in Example 38. sets the labels for two VkPrefText items using the first method.
Not all items display a label, for example VkPrefSeparator. Some preference items, such as VkPrefGroup, allow you to specify in the constructor whether or not you want to display a label for the item. The latter sections of this chapter describe individual preference items and how each item uses its label widget.
Preference items that allow the user to input information--VkPrefText, VkPrefToggle, and VkPrefOption--have values associated with them. Each such item stores its own value internally. This value might or might not match the value currently displayed in the preference dialog. Because users can click on the Cancel button to return all preferences to their last applied values, a preference item must not immediately store a new value that a user enters. Only when the user clicks on the dialog's Apply button or OK button do preference items update their internally-stored values to match the values displayed on the screen.
Preference items provide a getValue() function that updates the internally-stored value with the currently displayed value and returns the updated value. The getValue() function is not actually declared in the VkPrefItem base class because different types of preference items use different types of values (for example, VkPrefToggle uses a Boolean value whereas VkPrefText uses a character string). Each preference item with an associated value provides its own definition of getValue().
The setValue() function allows you to programmatically set the internally-stored value of a preference item. The setValue() function automatically updates the displayed value to reflect the new internal value. As with the getValue() function, setValue() is not actually declared in the VkPrefItem base class; each preference item with an associated value provides its own definition of setValue().
The VkPrefItem::changed() function checks to see whether or not the user has changed the value displayed on the screen so that it no longer matches the item's internally-stored value:
virtual Boolean changed()
If the value has changed, changed() returns the Boolean value TRUE; otherwise, it returns FALSE. You should use changed() as a test to determine whether or not you need to call getValue() for a preference item.
The activate() and deactivate() functions control whether or not a preference item is activated:
If the item is deactivated, the item is grayed out on the screen, and the user cannot change the item's value. Call activate() to activate an item and deactivate() to deactivate an item.
Occasionally, you might want to achieve certain effects by manually setting the height of a preference item's label or base widget. The setLabelHeight() and setBaseHeight() functions each accept an Xt Dimension value as and argument and set the item's label and base widget, respectively, to the entered height:
The labelHeight() function returns the current height of the item's label widget, and the baseHeight() function returns the current height of the item's base widget, each expressed as an Xt Dimension value:
The labelWidget() function returns the item's label widget:
labelWidget() returns NULL if an item does not have a label widget.
The type() function returns an enumerated value of type VkPrefItemType that identifies an item's type:
Valid return values include the following:
PI_group |
PI_text |
PI_empty |
PI_custom |
PI_list |
PI_toggle |
PI_label |
PI_none |
PI_radio |
PI_option |
PI_separator |
The isContainer() function returns TRUE if the preference item is one used to group (or contain) other items:
isContainer() returns true for VkPrefGroup, VkPrefRadio, and VkPrefList.
The following sections describe preference item classes provided by ViewKit ObjectPak. In addition to the specific member functions listed, each class also supports all functions provided by the VkPrefItem class.
The VkPrefText class supports text field preference items, allowing users to enter text strings. Figure 31. shows a simple preference dialog containing a text field preference item.
Figure 31. Example of a Text Field Preference Item
The VkPrefText constructor has the following form:
VkPrefText(const char *name, int columns = 5)
The VkPrefText constructor expects as its first argument the name of the preference item. You can optionally provide as a second argument an integer value specifying the default number of columns for the text field.
For example, creating the text field shown in Figure 31. requires only the following line:
VkPrefText *name = new VkPrefText("name");
To set the label for the text field, you must set the XmNlabelString resource of the preference item's label widget. Therefore, to set the label as shown in Figure 31., you must set the following resource:
*nameLabel.labelString: Enter your name:
Refer to "Preference Item Labels" for more information on setting the label of a preference item.
To retrieve the internally-stored text field value, use the getValue() function:
char *getValue()
getValue() duplicates the internal value and then returns a pointer to the duplicate string. (You should free this string when you no longer need it.) For example, the following line retrieves the value of the name text field shown above:
userName = name->getValue();
To programmatically set the text field value, use the setValue() function:
void setValue(const char *str)
setValue() copies the string that you pass as an argument, sets the internally-stored value to that string, and updates the value displayed by the text field. For example, the following line sets the value of the name text field shown above to "John Doe:"
name->setValue("John Doe");
The VkPrefToggle class supports a single toggle button preference item. You can group multiple toggle buttons using a VkPrefGroup or VkPrefList item, and you can enforce radio-style behavior on a group of toggles by grouping them in a VkPrefRadio item. These classes are discussed later in this chapter.
Figure 32. shows a simple preference dialog containing a single toggle button preference item.
Figure 32. Example of a Toggle Button Preference Item
The VkPrefToggle constructor has the following form:
VkPrefToggle(const char *name, Boolean forceLabelFormat = FALSE)
The first argument the VkPrefToggle constructor expects is the name of the preference item. For example, creating the toggle button shown in Figure 32. requires only the line:
VkPrefToggle *erase = new VkPrefToggle("erase");
You can provide an optional Boolean value as a second argument to the VkPrefToggle constructor. A TRUE value forces the VkPrefToggle object to create and use a label widget as described in "Preference Item Labels" . Otherwise, if the value is FALSE, the behavior of the label is determined as described below in "Toggle Buttons." The default value is FALSE.
Setting the label for a toggle preference item is more complex than with other preference items. Unlike many of the other preference items, the ToggleButton widget that is the base widget of the VkPrefToggle item includes a text label. Therefore, to set that label, you must set the XmNlabelString resource of the preference item's base widget instead of its label widget. For example, to set the label as shown in Figure 32., you must set the resource:
*eraseBase.labelString: History Erase
This works for all cases except for when a toggle is an item in a vertical VkPrefGroup or VkPrefRadio item that contains items other than toggles. (A group that contains more than one type of preference item is a non-homogenous group; a group that contains only one type of preference item is a homogenous group.) To understand why this is done, consider first a simple vertical VkPrefGroup containing only two toggles, as shown in Figure 33. In this case, the labels appear to the right side of the buttons as they normally do.
Figure 33. Toggle Preference Items in a Homogenous Vertical Group
When toggle items appear in a homogenous group like the one shown in Figure 33., you should set the XmNlabelString resources for the base widgets of the toggle items. For example:
However, the labels for most other preference items appear to the left of the items. Left uncorrected, if a vertical, non-homogenous VkPrefGroup or VkPrefRadio contained a toggle item, the label for the toggle would not align with the other labels.
Therefore, in the case of a non-homogenous vertical VkPrefGroup or VkPrefRadio, ObjectPak sets the XmNlabelString resource of all toggle items' base widgets to NULL and instead displays their label widgets.
The result is that all of the preference items' labels correctly align, as shown in Figure 34.:
Figure 34. Toggle Preference Items in a Non-Homogenous Vertical Group
When toggle items appear in a non-homogenous, vertical group like the one shown in Figure 34., you should set the XmNlabelString resources for the label widgets of the toggle items rather than the base widgets. For example:
Refer to "Preference Item Labels" for more information on setting the label of a preference item.
Use the getValue() function to retrieve the Boolean value of the toggle:
Boolean getValue()
For example, the following line retrieves the value of the firstToggle toggle shown above:
toggleSet = firstToggle->getValue();
Use the setValue() function to programmatically set the value of the toggle:
void setValue(Boolean value)
setValue() sets the internally-stored value to the Boolean value you pass as an argument, and updates the value displayed by the toggle. For example, the following line sets the value of the secondToggle toggle shown above to TRUE:
secondToggle->setValue(TRUE);
The VkPrefOption class supports option menu preference items, allowing users to select an option from a menu. Figure 35. shows a simple preference dialog containing an option menu preference item.
Figure 35. Example of an Option Menu Preference Item
The VkPrefOption constructor has the following form:
VkPrefOption(const char *name, int numEntries)
The VkPrefOption constructor expects as its first argument the name of the preference item. The second argument is an integer value specifying the number of entries in the option menu.
For example, you can create the option menu shown in Figure 35. with the line:
VkPrefOption *align = new VkPrefOption("align", 3);
To set the label for the option menu you must set the XmNlabelString resource of the preference item's label widget. Therefore, to set the label as shown in Figure 35., you must set the resource:
*alignLabel.labelString: Alignment
Refer to "Preference Item Labels" for more detailed information.
To set the labels for the individual items in the option menu, use the setLabel() function:
void setLabel(int index, const char *label)
setLabel() expects two arguments. The first is an integer value specifying the index of the of the menu item. Menu items are numbered starting with 0.
The second setLabel() argument is a character string, first treated as a resource name looked up relative to the menu item's widget. If the resource value exists, it is used as the label. If no resource is found, or if the string contains spaces or newline characters, the string itself is used as the label.
For example, the following lines set the labels for the option menu items shown in Figure 35. directly:
On the other hand, the following lines set the labels using resource values:
In the second case, you would also have to set the appropriate resource values. You could do so using the setDefaultResources() function, or you could include the following lines in a resource file:
You can retrieve the label for a given item using the getLabel() function:
char *getLabel(int index)
index is the index of the menu item.
Note: getLabel() returns the same string that you passed to setLabel() when setting the item's label. Therefore, if you set the item's label by specifying a resource name, getLabel() returns the resource name, not the value of the resource.
In the VkPrefOption constructor, you must provide an argument specifying the number of elements in the option menu. However, after creating an option menu preference item, you can resize it as needed using the setSize() function:
void setSize(int numEntries)
setSize() accepts an integer argument specifying the new size of the option menu. If the new size is smaller than the old size, setSize() automatically deletes all unneeded widgets. If the new size is larger, setSize() automatically creates and manages any additional widgets needed.
You can determine the current size of an option menu preference item using the size() function:
int size()
You can access any of the button widgets contained in the option menu with the getButton() function:
Widget getButton(int index)
Simply specify the index of the button you want and getButton() returns the appropriate widget.
Use the getValue() function to retrieve the internally-stored value of the option menu:
int getValue()
getValue() returns an integer value specifying the index of the selected menu entry. For example, the following line retrieves the value of the align text field shown above:
alignment = align->getValue();
Use the setValue() function to programmatically set the value of the option menu:
void setValue(int index)
setValue() sets the internally-stored value to the index value you pass as an argument, and updates the value displayed by the option menu. For example, the following line sets the value of the alignment text field shown above to 1, corresponding to the "Align Center" option:
align->setValue(1);
The VkPrefFigure 36.Label class supports text labels for preference dialogs.
Note: VkPrefLabel is useful only in conjunction with VkPrefList. Do not use VkPrefLabel with either VkPrefGroup or VkPrefRadio; VkPrefLabel does not create a label widget and, therefore, it does not align properly with other items contained in a VkPrefGroup or VkPrefRadio item.
Figure 36. shows a preference dialog that contains a label preference item.
Figure 36. Example of a Label Preference Item
The VkPrefLabel constructor expects the name of the preference item as a value:
VkPrefLabel(const char *name)
For example, creating the label shown in Figure 36. requires only the line:
VkPrefLabel *dialogName = new VkPrefLabel("dialogName");
Many other ObjectPak preference items include label widgets in addition to their base widget; however, in the case of the VkPrefLabel item, the label is the base widget. Therefore, in preference item groups, a VkPrefLabel item aligns with other base widgets, not with other label widgets.
Because the label that is displayed for a VkPrefLabel item is the base widget, you set the label's text by setting the XmNlabelString resource of the item's base widget. Therefore, to set the label as shown in Figure 36., you must set the resource:
*dialogNameBase.labelString: Document Properties
Refer to "Preference Item Labels" for more detailed information.
The VkPrefSeparator class supports a simple separator for use in preference dialogs.
Note: VkPrefSeparator is useful only in conjunction with VkPrefList. You should not use VkPrefSeparator with either VkPrefGroup or VkPrefRadio; VkPrefSeparator does not create a label widget and therefore it does not align properly with other items contained in a VkPrefGroup or VkPrefRadio item.
The only argument the VkPrefSeparator constructor expects is the name of the preference item:
VkPrefSeparator(const char *name)
For example:
VkPrefSeparator *sep = new VkPrefSeparator("sep");
The VkPrefEmpty class provides a "null" item that you can use to add extra space between other items. This preference item is useful only in conjunction with one of the grouping preference items: VkPrefGroup, VkPrefRadio, or VkPrefList.
The VkPrefEmpty constructor accepts no arguments:
VkPrefEmpty()
For example:
VkPrefEmpty *space = new VkPrefEmpty();
ViewKit ObjectPak provides three classes for creating groups of items: VkPrefGroup, VkPrefRadio, and VkPrefList. Both VkPrefRadio and VkPrefList are implemented as subclasses of VkPrefGroup.
VkPrefGroup defines a group of related items. You can specify either vertical or horizontal layout; the default is vertical. With a vertical layout, VkPrefGroup pads items so that they take equal space. You have the option of displaying a label for the group.
Figure 37. shows an example of a vertical VkPrefGroup item with a label. The label is the group item's label widget, not a VkPrefLabel item. The VkPrefGroup item right-aligns the labels for all of the items it contains. (Because the VkPrefToggle items are part of a non-homogenous VkPrefGroup item, you must set the XmNlabelString resources of their label widgets instead of their base widgets, as described in "Toggle Buttons" .) Also, all items are allocated the same amount of vertical space. If you were to add a larger item to this group, the group item would allocate for each item the same amount of vertical space.
Figure 37. Example of a Vertical VkPrefGroup Item with Label
Figure 38. shows the same preference items grouped by a horizontal VkPrefGroup item with a label.
Figure 38. Example of a Horizontal VkPrefGroup Item with Label
VkPrefList is similar to VkPrefGroup; however, it supports only a vertical orientation and it does not support displaying a group label. Unlike VkPrefGroup, VkPrefList does not pad its items so that they take equal space; instead, each item takes only as much space as it needs. Typically, you use a VkPrefList item to group other group items. For example, in Example 37., the top-level VkPrefList item contained a VkPrefLabel item and two VkPrefGroup items--one vertical and one horizontal--separated by two VkPrefSeparator items.
VkPrefList is also the only grouping item to which you should add VkPrefLabel or VkPrefSeparator items. You should not use VkPrefLabel or VkPrefSeparator with either VkPrefGroup or VkPrefRadio; they do not create label widgets and therefore do not align properly with other items contained in a VkPrefGroup or VkPrefRadio item.
Figure 39. shows an example of a VkPrefList.
Figure 39. Example of a VkPrefList Item
Note: The VkPrefList item does not contain a group label; if you want to provide a label for a VkPrefList item, you can include a VkPrefLabel item in it. Also. the VkPrefList item does not align the labels of the items it contains. (Because the VkPrefToggle items are part of a VkPrefList item, you must set the XmNlabelString resources of their base widgets instead of their label widgets, as described in "Toggle Buttons" on page 230.) Each item is allocated only as much vertical space as it needs. If you were to add a larger item to this group, it would not affect the vertical spacing of the other items.
VkPrefRadio is almost identical to VkPrefGroup except that you use it only for enforcing radio-style behavior on the VkPrefToggle items that it contains. You should add only VkPrefToggle items to a VkPrefRadio item. Otherwise, VkPrefRadio supports the same functionality as VkPrefGroup.
Figure 40. shows an example of a vertical VkPrefRadio item with a label. The label is the group item's label widget, not a VkPrefLabel item. Because the VkPrefToggle items are part of a homogenous VkPrefRadio item, you must set the XmNlabelString resources of their base widgets instead of their label widgets, as described in "Toggle Buttons" .
Figure 40. Example of a Vertical VkPrefRadio Item with Label
The VkPrefGroup constructor has the following form:
The VkPrefGroup constructor expects as its first argument the name of the preference item. The second argument is an optional Boolean value that determines the orientation of the group; FALSE, the default value, specifies a vertical orientation and TRUE specifies a horizontal orientation. The third argument is an optional Boolean value that determines whether or not to display a label for the group; FALSE, the default value, specifies that the group should display the label and TRUE specifies that the group should not display the label.
For instance, Example 37. contained the following constructor:
VkPrefGroup *numberGroup = new VkPrefGroup("numberGroup");
This created a new VkPrefGroup item named "numberGroup" with a vertical orientation and a visible label. Example 37. also contained the following constructor:
This created a new VkPrefGroup item named "horizGroup" with a horizontal orientation and no visible label.
The VkPrefRadio constructor accepts the same arguments as the VkPrefGroup constructor:
For instance, Example 37. contained the following constructor:
This created a new VkPrefRadio item named "paginationGroup" with a vertical orientation and a visible label.
VkPrefList accepts only one argument, a character string specifying the name of the item:
VkPrefList(const char *name)
As noted earlier, all VkPrefList items have a vertical orientation and do not display a label. Example 37. created a VkPrefList item as the top-level preference item to contain all other preference items:
VkPrefList *docList = new VkPrefList("docList");
After creating a group item, you can add other items to it with the addItem() function:
void addItem(VkPrefItem *item)
Preference items appear in the order in which you add them. Example 37. added five preference items to the docList preference item:
Once you have added items to a group item, you can access an individual child item with the item() function:
VkPrefItem *item(int item)
Provide an integer index value as an argument and item() returns a pointer to the desired preference item. The numbering of preference items within a group begins with 0, so to retrieve a pointer to the numberGroup item added above to docList, you could use the line:
item = docList->index(2);
The size() function returns the number of preference items currently associated with a group item:
int size()
The deleteChildren() function deletes all the items contained by a group item:
virtual void deleteChildren()
Note that this function does not just disassociate the items from the parent group item, it actually deletes the items. This is useful for freeing memory in a destructor. ObjectPak does not provide any means of disassociating preference items without deleting them or of deleting individual items in a group. This should not pose a problem as most applications create preference dialogs at startup and almost never need to modify them afterwards.
The group preference items provide a changed() function just like all other preference items; however changed() operates differently with group items than it does with individual preference items. In group items, changed() calls the changed() functions of all child items in the group and returns TRUE if any of the child items have changed.
To set the label for a VkPrefGroup or VkPrefRadio item, you must set the XmNlabelString resource of the preference item's label widget. (Remember that VkPrefList items do not display labels.) Example 37. illustrated this by setting the labels for numerous group items:
Refer to "Preference Item Labels" for more information on setting the label of a preference item.
VkDoubleBuffer is an abstract class that provides support for components that need to display double-buffered graphics.
Note: VkDoubleBuffer provides software double-buffering only; it does not use the hardware double-buffering available on many workstations. As a result, you might notice some flickering in your VkDoubleBuffer animations.
You must create a separate subclass of VkDoubleBuffer for each double-buffered display component in your application. In each subclass, you include the Xlib calls to create the text or graphics that the component displays. You do not have to worry about handling Expose events or resize requests as VkDoubleBuffer handles these automatically.
The public interface to VkDoubleBuffer consists simply of a function that your application calls whenever it needs to update the component's display. For example, to drive an animation, you could set a timer to update a component at a desired interval.
The VkDoubleBuffer constructor accepts the standard ObjectPak component constructor arguments, a component name and a parent widget:
The constructor creates the various widgets and Pixmaps used by the component and installs callbacks to handle Expose events and resize requests. In your subclass constructor, you can initialize any graphics contexts and other data that your component requires.
The VkDoubleBuffer destructor frees the widgets and Pixmaps allocated by the VkDoubleBuffer constructor:
In your subclass destructor you should free any graphics contexts and other data allocated by your component.
The VkDoubleBuffer class calls your component's draw() function when your component needs to draw a new frame:
draw() is declared by VkDoubleBuffer as a pure virtual function, and it is the only function you must override when creating a derived class of VkDoubleBuffer. The draw() function should use Xlib calls to display text or graphics by drawing to the _canvas data member:
The derived class always draws to the back buffer, although the derived class does not need to be aware of this. The VkDoubleBuffer class copies the contents of this Pixmap to the front buffer as needed.
VkDoubleBuffer::update() is the public member function that the application calls to update the component's display:
update() calls your component's draw() function to obtain a new frame. Then it swaps buffers, and if the component is currently displayed, updates the screen with the contents of the front buffer. Finally, update() clears the back buffer by filling it with the component's background color.
VkDoubleBuffer automatically handles window resize requests, resizing the front and back buffers and filling them with the component's background color. If you need to perform additional operations in your derived class, you can override the virtual function VkDoubleBuffer::resize():
VkDoubleBuffer calls resize() after resizing and reinitializing the buffers. The new height and width of the drawing area are contained in the _width and _height data members:
This section describes how ViewKit ObjectPak supports multiple top-level windows in an application and the ViewKit ObjectPak classes that implement these windows.
There are several possible models for multi-window applications in Xt. One approach is to create a single top-level window used as the main window of the application. All other windows are then popup shells whose parent is the main window. Another approach is to create a single shell that never appears on the screen. All other windows are then popup children of the main shell. In this model, all top-level windows are treated equally, as siblings. One window may logically be the top-level window of the application, but as far as Xt is concerned, all windows are equal.
ViewKit ObjectPak follows the second model. The VkApp class, described in Chapter 3--Application Class creates a single widget that serves as the parent of all top-level windows created by the program. The VkApp base widget does not appear on the screen.
All top-level windows in a ViewKit ObjectPak application must be instances of VkSimpleWindow, VkWindow, or a subclass of one of these classes. The VkSimpleWindow class supports a top-level window that does not include a menu bar. The VkWindow class, derived from VkSimpleWindow, adds support for a menu bar along the top of the window. You must create a separate instance of VkSimpleWindow, VkWindow, or a subclass of one of these classes for each top-level window in your application.
Instantiating a VkSimpleWindow or VkWindow object creates a popup shell as a child of the invisible shell created by your application's instance of VkApp. VkSimpleWindow and VkWindow also create a XmMainWindow widget as a child of the popup shell. You define the contents of a window by creating a widget or ObjectPak component to use as the work area (or view) for the XmMainWindow widget.
Typically, you create several widgets and/or ObjectPak components as children of a container widget and then assign that container widget as the view of the XmMainWindow widget. "Creating the Window Interface" describes how to assign a view to a window. Figure 11. illustrates widget hierarchy for the top-level windows of a simple ObjectPak application with two top-level windows.
Figure 11. Widget Hierarchy of Top-level Widgets in ObjectPak Applications
In most cases, directly instantiating a VkSimpleWindow or VkWindow object is inappropriate.1 In addition to the widgets and components composing the window's interface, most windows require other data and support functions. In accordance with good object-oriented programming style, the functions and data associated with a window should be contained within that window's class. Therefore, the best practice to follow when creating a ViewKit ObjectPak application is to create a separate subclass for each window in your application. You can derive these subclasses from VkWindow for those windows that require menu bars, and from VkSimpleWindow for those windows that do not. "Deriving Window Subclasses" describes the process of deriving window subclasses.
In addition to creating shell and XmMainWindow widgets, the VkSimpleWindow and VkWindow classes set up various properties on the shell window and provide simple hooks for window manager interactions. "Window Manager Interface" discusses the built-in window manager support.
The VkSimpleWindow and VkWindow classes provide simple functions to raise, lower, iconify, and open windows, as described in "Manipulating Windows" . The classes also provide several convenience function for determining a window's state (for example, whether it is visible, iconified, and so on) and for retrieving other window information. These access functions are described in "Window Data Access Functions" .
The VkSimpleWindow and VkWindow classes also register their windows with the application's VkApp instance to support application-wide services such as setting the cursor for all of an application's windows, entering busy states, and manipulating all windows in an application. Chapter 3--Application Class describes how to use these application-wide services.
This chapter includes the following sections:
Figure 10. shows the inheritance graph for the ViewKit ObjectPak classes VkSimpleWindow and VkWindow.
Figure 10. The Inheritance Graph for VkSimpleWindow and VkWindow
ViewKit ObjectPak applications interact with a help library through three C functions. To interface an ObjectPak application to a help system, you need only to implement these three functions. ObjectPak also provides a rudimentary help library that you can use if you do not want to implement your own. "ViewKit ObjectPak Help Library" describes this library.
SGIHelpInit() initializes the help system:
int SGIHelpInit(Display *display, char *appClass, char *)
VkApp calls SGIHelpInit() from its constructor. display is the application's Display structure, and appClass is the application's class name. The third argument to SGIHelpInit() is reserved for future use. A return value of 0 indicates failure.
An ObjectPak application calls SGIHelpMsg() when it needs to request help:
int SGIHelpMsg(char *in_key, char *, char *)
in_key is a character token that SGIHelpMsg() uses to look up help material. The value of in_key depends on how the user requested help. The subsections that follow describe how the value is determined. The other argument to SGIHelpMsg() is reserved for future use. A return value of 0 indicates failure.
An ObjectPak application calls SGIHelpIndexMsg() when it needs to display an index of help available:
int SGIHelpIndexMsg(char *in_key, char *)
in_key is a character token that SGIHelpIndexMsg() uses to look up a help index. The value of in_key depends on how the user requested help. The subsections that follow describe how the value is determined. The other arguments to SGIHelpIndexMsg() are reserved for future use. A return value of 0 indicates failure.
When you post a dialog as described in "Posting Dialogs" , you have the option of providing a helpString argument. If you provide a helpString argument, the dialog posted displays a Help button.
When the user clicks on the Help button, your application calls SGIHelpMsg(), passing the helpString as the in_key character token.
When the user presses the <F1> key while the mouse pointer is over a widget, as long as you have not provided XmNhelpCallback functions for widgets in your application, your applications calls SGIHelpMsg(). The in_key character token that your application provides to SGIHelpMsg() is the fully-qualified instance name hierarchy for the widget.
As noted in "ViewKit Help Menu" , the Help menu implemented by the VkHelpPane class contains five selectable items: "Click for Help," "Overview," "Index," "Keys & Shortcuts," and "Product Info."
"Click for Help" provides another method of obtaining context-sensitive help. When the user selects this item, the cursor changes into a question mark. The user can then click on any widget in the application. When the user clicks on a widget, the application calls SGIHelpMsg(). The in_key character token that your application provides to SGIHelpMsg() is the fully-qualified instance name hierarchy for the widget.
"Overview" calls SGIHelpMsg() to request overview help. If the VkHelpPane object is a descendant of a shell widget, the in_key character token that your application provides to SGIHelpMsg() is "shellName.overview" where shellName is the name of the shell widget. In most cases, the VkHelpPane object is a pane in a window's menu bar, so the shellName is the name of the window. If the VkHelpPane object is not a descendent of a shell widget, the in_key character token that your application provides to SGIHelpMsg() is simply "overview".
"Index" calls SGIHelpIndexMsg() to request an index of available help topics. The in_key character token that your application provides to SGIHelpIndexMsg() is the application's class name as it is returned by VkApp::applicationClassName().
"Keys & Shortcuts" calls SGIHelpMsg() to request help on keys and shortcuts. If the VkHelpPane object is a descendant of a shell widget, the in_key character token that your application provides to SGIHelpMsg() is "shellName.keys" where shellName is the name of the shell widget. In most cases, the VkHelpPane object is a pane in a window's menu bar, so the shellName is the name of the window. If the VkHelpPane object is not a descendent of a shell widget, the in_key character token that your application provides to SGIHelpMsg() is simply "keys".
"Product Info" displays the Product Information dialog described in "Maintaining Product and Version Information" . The Product Information dialog has no connection to the help library.
In all ViewKit ObjectPak applications you must create a single instance of the VkApp class (or a derived class) before instantiating any other ObjectPak objects. The VkApp constructor initializes the Xt Intrinsics and creates an invisible shell to serve as the parent for all the application's main windows. ViewKit ObjectPak supports the commonly-used multi-shell architecture described in X Window System Toolkit (Asente and Swick, 1990). ObjectPak creates all windows (using the VkSimpleWindow and VkWindow classes described in Chapter 4--ViewKit Windows) as popup children of the shell created by VkApp.
When you create an instance of the VkApp class, the constructor assigns a pointer to the VkApp object to the global variable theApplication. The <Vk/VkApp.h> header file declares this global variable as follows:
As a result, the theApplication pointer is available in any file that includes the <Vk/VkApp.h> header file. This provides easy use of VkApp's facilities and data throughout your program.
The syntax of the VkApp constructor is:
The appClassName argument designates the application class name, which is used when loading application resources. Note that VkApp differs from other ViewKit ObjectPak components in that you provide the application class name as an argument to the constructor rather than overriding the className() function. This allows you to set the application class name without creating a subclass of VkApp.VkApp also differs from other ViewKit ObjectPak components in that you do not provide a component name in the constructor; instead, ViewKit ObjectPak uses the command that you used to invoke your application (argv[0]) as the component name.
The second and third arguments to the VkApp constructor must be pointers to argc and the application's argv array. The VkApp constructor passes these arguments to XtOpenDisplay(3), which parses the command line according to the standard Xt command-line options, loads recognized options into the application's resource database, and modifies argc and argv to remove all recognized options.
You can specify additional command-line options to parse by passing an XrmOptionDescRec(3) table as the options argument and specifying the number of entries in the table with the numOptions argument. This is sufficient for setting simple resource values from the command line.
However, to set application-level variables using either the command line or resource values, you must perform the following tasks:
Example 16. in "Deriving Classes from VkApp" illustrates this process.
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:
The VkOutlineASB class, a subclass of VkOutline, provides the same functionality as VkOutline except that it uses an annotated scrollbar. With VkOutlineASB, you can display colored bars in the scrollbar to indicate the positions of highlighted items in the outline.
All functions that VkOutlineASB inherits from VkOutline operate identically. VkOutlineASB provides one additional function, VkOutlineASB::setAnnotation():
void setAnnotation(int attribID, Boolean state)
setAnnotation() determines whether or not the scrollbar displays annotations for a given display highlight. attribID is the attribute identifier returned by setHighlightAttributes() of a particular custom highlight. If state is TRUE, the scrollbar displays annotations for the given display highlight; if state is FALSE, the scrollbar does not display annotations for the given display highlight.
VkSimpleWindow and VkWindow constructors both have the same form:
Unlike most other ObjectPak components, the VkSimpleWindow and VkWindow constructors do not require a parent widget as an argument: all ObjectPak windows are automatically created as children of the invisible shell created by your application's instance of VkApp. You must specify a name for your window. Optionally, you can also provide a standard Xt argument list that the constructor will use when creating the window's popup shell.
Every application has a main window. By default, the first window you create is treated as the main window. To specify a different window to use as the main window, use the VkApp::setMainWindow() function described in "Managing Top-Level Windows" .
Because the first window you create is by default the main window, the window class constructors also set some shell resources on the popup shell widget of that window. The constructors obtain the geometry of the invisible application shell created by VkApp and assign that geometry to the window's popup shell widget. The constructors also set the XmNargc and XmNargv resources on the popup shell to the values of VkApp::argc() and VkApp::argv() respectively. ("Application Data Access Functions" describes VkApp::argc() and VkApp::argv().)
Finally, for all windows, the window class constructors register a callback function to handle messages from the window manager. The default action of upon receiving a WM_DELETE_WINDOW message is to delete the window object. To change this behavior, override the handleWmDeleteMessage() member function as described in "Window Properties and Shell Resources" . The default action of upon receiving a WM_QUIT_APP message is to quit the application. To change this behavior, override the handleWmQuitMessage() member function as described in "Window Properties and Shell Resources".
The VkSimpleWindow and VkWindow destructors delete all privately allocated data and destroy the views associated with the windows. The VkWindow destructor also destroys any menu bar associated with the window, no matter how you added it (see "Menu Bar Support" ). If you created a subclass, you should provide a destructor to free any space that you explicitly allocated in the derived class.
The VkSimpleWindow and VkWindow destructors also remove the window from the application's list of windows. If this window is the only window still associated with the application (for example, if it is the only window created or all other windows have also been deleted), then your application automatically calls VkApp::terminate() to quit itself. "Exiting ViewKit ObjectPak Applications" describes VkApp::terminate().
VkSimpleWindow and VkWindow classes support the following data access functions:
Returns the XmMainWindow widget created by the window constructor. Often, you use mainWindowWidget() to obtain a parent widget for creating a view widget or component. Use this function to access and configure the window's XmMainWindow widget. For example, ViewKit window classes configure the window's XmMainWindow widget to not display scrollbars (by default). Use mainWindowWidget() to obtain the XmMainWindow widget, then XtSetValues(3) to enable scrollbars.
Returns the widget currently installed as the window's view.
Returns TRUE if window is currently displayed and FALSE if hidden.
Returns TRUE if the window is currently iconified and FALSE if not.
The VkSimpleWindow and VkWindow classes set up various properties on the shell window and provide simple hooks for window manager interactions.
The VkSimpleWindow and VkWindow classes provide easy-to-use functions to set your application's window and icon titles.
The setTitle() function sets the title of a window:
void setTitle(const char *newTitle)
The string is treated first as a resource name that setTitle() looks up relative to the window. If the resource exists, its value is used as the window title. If the resource does not exist, or if the string contains spaces or newline characters, setTitle() uses the string itself as the window title. This allows applications to dynamically change a window title without hard-coding the exact title names in the application code. Example 21. shows an example of setting a window title using a resource value.
You can retrieve the current window title using getTitle():
const char *getTitle()
The setIconName() function sets the title of a window's icon:
void setIconName(const char *newTitle)
The string is treated first as a resource name that setIconName() looks up relative to the window. If the resource exists, its value is used as the window's icon title. If the resource does not exist, or if the string contains spaces or newline characters, setIconName() uses the string itself as the icon title. This allows applications to dynamically change a window's icon title without hard-coding the exact title names in the application code. Example 21. shows an example of setting a window's icon title using a resource value.
The window class constructors automatically set up various window properties and shell resources when you create a window. The window classes also provide some hooks to allow you to set your own properties or change the window manager message handling in a derived class.
Because the first window you create is by default the main window, the window class constructors also set some shell resources on the popup shell widget of that window. The constructors obtain the geometry of the invisible application shell created by VkApp and assign that geometry to the window's popup shell widget. The constructors also set the XmNargc and XmNargv resources on the popup shell to the values of VkApp::argc() and VkApp::argv() respectively. ("Application Data Access Functions" describes VkApp::argc() and VkApp::argv().)
For all windows, the window class constructors register a callback function to handle WM_DELETE_WINDOW messages from the window manager. This callback function calls handleWmDeleteMessage():
virtual void handleWmDeleteMessage()
By default, handleWmDeleteMessage() calls the window's okToQuit() function. If okToQuit() returns TRUE, then handleWmDeleteMessage() deletes the window. You can override handleWmDeleteMessage() to change how your window handles a WM_DELETE_WINDOW message. In most cases, you should simply perform any additional actions that you desire and then call the base class's handleWmDeleteMessage() function.
The window class constructors also register a callback function to handle WM_QUIT_APP messages from the window manager. This callback function calls handleWmQuitMessage():
virtual void handleWmQuitMessage()
By default, handleWmQuitMessage() calls the application's quitYourself() function to quit the application. You can override handleWmQuitMessage() to change how your windows handles a WM_QUIT_APP message. In most cases, you should simply perform any additional actions that you desire and then call the base class's handleWmQuitMessage() function to exit your application.
If you want to set any additional properties on a window, you can override setUpWindowProperties():
virtual void setUpWindowProperties()
setUpWindowProperties() is called after realizing a window's popup shell widget but before mapping it. Subclasses that wish to store other properties on windows can override this function and perform additional actions. If you override this function, you should set all desired properties and then call the base class's setUpWindowProperties() function.
Note: Use setUpWindowProperties() to set window properties instead of VkComponent::afterRealizeHook() as described in "Displaying and Hiding Components" on page 19. The difference between the two is that setUpWindowProperties() is guaranteed to be called before the window manager is notified of the window's existence. Because of race conditions, this might not be true of afterRealizeHook().
You can also change the value of the window manager class hint stored on a window using setClassHint():
void setClassHint(const char *className)
setClassHint() sets the class resource element of the XA_WM_CLASS property stored on this window to the string you pass as an argument.