ViewKit ObjectPak Programmer's Guide

 

Integrated Computer

Solutions, Incorporated

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

Tel: 617.621.0060

Fax: 617.621.9555

E-mail: info@ics.com

WWW: http://www.ics.com

Trademarks

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.

First printing

December 1996

Documentation Type: 

A

aboutDialog() (in VkApp)
activate() (in VkCompletionField)
activate() (in VkMenuItem)
activate() (in VkPrefItem)
activateItem() (in VkMenu)
activating
command classes
menu items [2]
preference items
add() (in VkAlignmentGroup)
add() (in VkCompletionField)
add() (in VkGangedGroup)
add() (in VkGraph)
add() (in VkMenu)
add() (in VkMenuUndoManager)
add() (in VkMeter)
add() (in VkRadioGroup)
addAction() (in VkMenu)
addAction() (in VkMsgClient)
addCallback() (in VkCallbackObject)
addConfirmFirstAction() (in VkMenu)
addDesktopMenuItems() (in VkGraph)
adding
buttons to radio group
items to meter component
nodes to graphs
pixmaps to tabs
scrollbars to a ganged group
tabs to tab panel
toggles to check box
widgets to alignment group
addItem() (in VkCheckBox)
addItem() (in VkPrefGroup)
addLabel() (in VkMenu)
addLabel() (in VkTickMarks)
addMenuItems() (in VkGraph)
addMenuPane() (in VkWindow)
addRadioMenuPane() (in VkWindow)
addRadioSubmenu() (in VkMenu)
addSeparator() (in VkMenu)
addSubmenu() (in VkMenu)
addTab() (in VkTabPanel)
addTabs() (in VkTabPanel)
addToggle() (in VkMenu)
addView() (in VkSimpleWindow)
adjustGeometry() (in VkModifiedAttachment)
Admin menu (in graph overview window)
afterRealizeHook() (in VkApp)
afterRealizeHook() (in VkComponent)
afterRealizeHook() (in VkSimpleWindow) [2]
alignBottom() (in VkAlignmentGroup)
alignHeight() (in VkAlignmentGroup)
aligning
nodes in graphs [2]
widgets
alignLeft() (in VkAlignmentGroup)
alignment groups
adding widgets
aligning widgets
removing widgets
alignRight() (in VkAlignmentGroup)
alignTop() (in VkAlignmentGroup)
alignWidth() (in VkAlignmentGroup)
appContext() (in VkApp)
applicationClassName() (in VkApp)
applications
busy states [2]
busy dialog [2]
entering
example
exiting
nested
class name [2]
command-line options, parsing [2]
example
cursors
busy, animated [2]
busy, fixed
default [2]
normal
temporary
Display structure
event handling
during postAndWait()
during sendSyncRequest()
during wasInterrupted()
pending events
raw events [2]
name [2]
pointer
product information
quitting [2] [3] [4] [5] [6]
running
shell [2] [3]
geometry
version information
windows, managing [2]
XtAppContext structure
Apply button, dialogs
apply() (in VkDialogManager)
arcCreatedCallback (in VkGraph)
arcDestroyedCallback (in VkGraph)
arcs (in graphs)
attributes
area1() (in VkTabPanel)
area2() (in VkTabPanel)
argc (in main()) [2]
argc() (in VkApp)
argv (in main()) [2]
argv() (in VkApp)
attach() (in VkModifiedAttachment)
attach() (in VkPopupMenu)
attach() (in VkResizer)
attachments [2]
alignment groups
ganged scrollbars
modified text
radio-style toggles
resizers
attributes
arcs in graphs

Application Class

Application Data Access Functions

VkApp provides the following data access functions for retrieving data useful for your application:

 

 

Function

Description

char *name() const

Returns command name you used to invoke the application (argv[0]).

char *applicationClassName() const

Returns application class name set in the VkApp constructor. This application class name is used when loading application resources.

virtual const char *className() const

Returns class name of VkApp (or subclass) instance being used. By default, this is "VkApp". Unlike all other ViewKit ObjectPak components, the VkApp class does not use the value returned by className() when loading resources; instead, it uses the application class name that you provide as an argument to the VkApp constructor. This allows you to set the application class name without creating a subclass of VkApp.

XtAppContext appContext() const

Returns application's XtAppContext structure, is required by many OSF/Motif and Xt functions.

Display *display() const

Returns a pointer to X Display structure associated with application's X server connection.

char *shellGeometry() const

Returns a string containing the geometry of the application's base shell. You may want to use this information to size other windows in your application.

int argc() const

Returns number of items remaining in the argv array after all arguments recognized by Xt have been removed.

char **argv() const

Called without arguments, returns pointer to the argv array after all arguments recognized by Xt have been removed.

char *argv(int index) const

Called with an integer argument, returns a single argv array item (after all arguments recognized by Xt have been removed) specified by the index argument.

Boolean startupIconified() const

Called with no arguments, returns value TRUE if the application starts with all windows iconified and FALSE if it starts with all windows displayed normally.

Widget baseWidget()

For the VkApp class, baseWidget() returns the hidden shell widget.

 

 


B

C

callCallbacks() (in VkCallbackObject)
Cancel button, dialogs
cancel() (in VkDialogManager)
centering algorithm, dialogs
centerOnScreen() (in VkDialogManager)
changed() (in VkPrefGroup)
changed() (in VkPrefItem)
check box component
example
setting labels
toggles
adding
detecting value changes
getting values
setting values
child() (in VkNode)
class hints
class name
application [2]
components [2]
classes
dependencies
management [2]
alignment groups
ganged scrollbars
modified text
radio-style toggles
resizers
className() (in VkApp)
className() (in VkComponent) [2]
clear() (in VkCompletionField)
clearAll() (in VkGraph)
clearing
completion field expansion list
undo stack
client data, Xt callbacks
components
static menu descriptions
command classes
activating
constructors
executing
menu items
overview
setting labels
command-line options, parsing [2]
example
compiling ViewKit programs
example
completion fields
activation, responding
clearing expansion list
replacing expansion list
retrieving contents
setting expansion list
components
base widget [2] [3] [4]
deletion, handling
realization, detecting
characteristics
class name [2]
constructor
definition
destructor
displaying
hiding
managing widgets [2]
multiple pointers to
name [2] [3] [4]
overview
parent widget [2]
resource support
data members, initializing
default values, setting
global values, setting
requirements
resource values, setting
values, retrieving
widget resources, note
static member functions and Xt callbacks [2]
example
naming convention
this pointer
subclassing
constructor
examples
summary
testing for valid
ViewKit callbacks
creating
defining
invoking
overview
registering callback functions
removing callback functions
triggering
unregistering callback functions
widget destruction [2] [3] [4]
widgets [2]
Xt callbacks [2]
example
naming convention
this pointer
composeAdd() (in VkMsgClient)
composeBegin() (in VkMsgClient)
concepts
suggested reading
constructing menus
dynamically
example
static description, from
example
VkMenuDesc structure
Xt callback client data
context-sensitive help [2]
conventions [2]
inheritance graphs
reference pages
typographical
createAction() (in VkMsgClient)
createCursor() (in VkCursorList)
createDialog() (in VkGenericDialog)
creating
ViewKit callbacks
window interfaces
cursors
busy, animated
animating
example
busy, fixed
default [2]
normal
temporary
custom dialog

Check Box Component

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.

Creating a Check Box

The VkCheckBox constructor accepts the standard ObjectPak component name and parent widget arguments:

VkCheckBox(const char *name, Widget parent)

The constructor creates an empty, labeled component.

Adding Toggles to the Check Box

You add toggles to the check box using the VkCheckBox::addItem() function:

Widget addItem(char *name, Boolean state = FALSE,
XtCallbackProc proc = NULL,
XtPointer clientData = NULL)

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.

Setting Check Box and Toggle Labels

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.

Setting labels

Set the XmNlabelString resource of the check box label and its toggles to set their labels:

  • Use the VkComponent::setDefaultResources() function to provide default resource values as described in "Setting Default Resource Values for a Component" .
  • Set resource values in an external app-defaults resource file. Any values you provide in an external file will override values that you set using the VkComponent::setDefaultResources() function. This is useful when your application must support multiple languages; you can provide a separate resource file for each language supported.
  • Set the resource values directly using the XtSetValues() function. Values you set using this method override any values set using either of the above two methods. You should avoid using this method as it "hard codes" the resource values into the code, making them more difficult to change.

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.

Example 41. Creating a Check Box

Code

#include <Vk/VkApp.h>
#include <Vk/VkSimpleWindow.h>
#include <Vk/VkCheckBox.h>
class CheckBoxWindow: public VkSimpleWindow {
protected:
virtual Widget setUpInterface ( Widget parent );
static String _defaultResources[];
public:
CheckBoxWindow ( const char *name ) : VkSimpleWindow ( name ) { }
~CheckBoxWindow();
virtual const char* className();
};
CheckBoxWindow:: ~CheckBoxWindow()
{ }
const char* CheckBoxWindow::className() { return "CheckBoxWindow"; }
String CheckBoxWindow::_defaultResources[] = {
"*check*label.labelString: Selections:",
"*check*one*labelString: First choice",
"*check*two*labelString: Second choice",
"*check*three*labelString: Third choice",
"*check*four*labelString: Fourth choice",
NULL
};
Widget CheckBoxWindow::setUpInterface ( Widget parent )
{
setDefaultResources(parent, _defaultResources);
VkCheckBox *cb = new VkCheckBox("check", parent);
cb->addItem("one");
cb->addItem("two");
cb->addItem("three");
cb->addItem("four");
cb->show();
return cb->baseWidget();
}
void main ( int argc, char **argv )
{
VkApp *cbApp = new VkApp("checkBoxApp", &argc, argv);
CheckBoxWindow *cbWin = new CheckBoxWindow("checkbox");
cbWin->show();
cbApp->run();
}

Setting and Obtaining Check Box Toggle Values

After creation, you can programmatically set the state of any toggle with the VkCheckBox::setValue() function:

void setValue(int index, Boolean newValue)

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.


Setting values of multiple toggles

You can set the values of multiple toggles using the VkCheckBox::setValues() function:

void setValues(Boolean *values, int numValues)

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.


Retrieving a specific toggle value

You can retrieve the value of a specific toggle with the VkCheckBox::getValue() function:

int getValue(int index)

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.

Recognizing Changes in Check Box Toggle Values

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.

Using Xt-Style callbacks to change check box toggle values

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::MyComponent(const char *name, Widget parent) : VkComponent (name)
{
// ...
parametersBox->addItem("lineNumbers", FALSE,
&MyComponent::toggleLineNumbersCallback(),
(XtPointer) this );
// ...
}

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:

void MyComponent::toggleLineNumbersCallback(Widget,
XtPointer clientData,
XtPointer callData )
{
MyComponent *obj = (MyComponent) clientData;
XmToggleButtonCallbackStruct *cb =
(XmToggleButtonCallbackStruct) callData;
// Call MyComponent::toggleLineNumbers(), a regular
// member function to either display or hide line numbers
// based on the value of the toggle.
obj->toggleLineNumbers(cb->set);
}

Using ObjectPak callbacks to change check box toggle values

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:

MyComponent::MyComponent(const char *name, Widget parent) : VkComponent (name)
{
// ...
parametersBox->addCallback(VkCheckBox::itemChanged, this,
(VkCallbackMethod) &MyComponent::parameterChanged );
// ...
}

Note this example lacks client data.

The definition of parameterChanged() could look like this:

void MyComponent::parameterChanged(VkComponent *obj, void *,
void *callData )
{
VkCheckBox *checkBox = (VkCheckBox) obj;
int index = (int) callData;
switch (index) {
// ...
// Assume that the constant LINE_NUMBER_INDEX is set to the index of
// the "lineNumber" toggle. If the "lineNumber" toggle value changed,
// Call MyComponent::toggleLineNumbers(), a regular member function to
// either display or hide line numbers based on the value of the toggle
case LINE_NUMBER_INDEX:
toggleLineNumbers( checkBox->getValue(index) );
// ...
}
}

Using subclassing to change check box toggle values

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():

virtual void valueChanged(int index, Boolean newValue)

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.

Protected data members

Derived classes have access to the following protected data members of the VkCheckBox class:

  • An instance of the ObjectPak WidgetList(3) class that contains all toggle buttons added to the check box
    VkWidgetList *_widgetList

  • The RowColumn widget that contains the toggle buttons
    Widget _rc

  • The label widget for the check box
    Widget _label

Command Classes

This section describes the VkAction class, which supports ViewKit ObjectPak command classes. Command classes allow you to implement actions as objects.

Overview

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.

Advantages of representing commands

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.

Using Command Classes in ViewKit ObjectPak

To use command classes in ViewKit ObjectPak, you must create a separate subclass for each command in your application.

Command class constructors

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.

Overriding virtual functions

Both VkAction and VkMenuActionObject have two protected pure virtual functions that you must override: doit() and undoit():

virtual void doit()
virtual void undoit()

doit() performs the command class's action; undoit() undoes the action.

Using command classes as menu items

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" .

Activating command classes

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:

void execute()

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.


Setting the label used by command classes

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.

Compiling and Linking ViewKit Programs

This section describes how to compile ViewKit ObjectPak programs.

Required Packages

To compile and link with the ViewKit ObjectPak libraries, you must have a C++ compiler, X11R5 and OSF/Motif 1.2 libraries.

Required Header Files

Depending on whether or not you made links, all ViewKit ObjectPak header files appear in the following directories:

  • /usr/include/Vk (if you made system links)
  • ${ObjectPak}/include/Vk (if you did not make links)

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.

Required Libraries

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:

CC -o hello hello.C -lvk -lvkhelp -lXm -lXt -lX11

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.

Component Resource Support

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

Resource management

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

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

Guidelines

Use the following guidelines to ensure proper ViewKit resource support:

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

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

Setting Resource Values by Class or Individual Component

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

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

*name*resource

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

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

*status*verbose: TRUE

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

*className*resource

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

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

*ProcessMonitor*verbose: TRUE

Initializing Data Members Based on Resource Values

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

void getResources ( const XtResourceList resources,
const int numResources )

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

getResources

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

*name.resource: value

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

Setting resource values for component classes

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

*className.resource: value

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

Example 5. Initializing a Data Member from the Resource Database

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

Code

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

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

*ProcessMonitor.verbose: TRUE

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

*conversionMonitor.verbose: TRUE

Setting Default Resource Values for a Component

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

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

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

The setDefaultResources() function has the following syntax:

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

Aruguments

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

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

Encapsulating resource sets

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


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


Example 6. Setting a Component's Default Resource Values

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

Code

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

Convenience Function for Retrieving Resource Values

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

VkGetResource() has two forms. The first is:

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

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


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


The second form of VkGetResource() is:

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

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


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


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

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

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

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

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

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

 

Contributed Classes: Overview

Overview

This appendix includes the following sections:

Conventions

 

This section describes the conventions used for presenting information in this book.

Notation

These type conventions and symbols are used in this guide:

Bold

C++ class names, C++ member functions, C++ data members, function names, literal command-line arguments (options and flags)

Italics

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

Screen type

Onscreen text, prompts, error messages, examples, and code listings

Bold screen type

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.

Overview

The first page of each chapter provides a map of the sections included in the chapter under the heading Overview.

Class Inheritance Graph Conventions

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.

Creating Menus: Overview

This chapter includes the following sections:

Inheritance graph

Figure 16. shows the inheritance graph for the classes required to create and manipulate menus.

Figure 16. Inheritance Graph for ViewKit ObjectPak Menu Classes

Documentation Type: 

Creating the Window Interface

This section describes the following three methods that you can use to create the contents of a window:

  • Create a subclass of VkSimpleWindow or VkWindow and define the interface in the class constructor
  • Create a subclass of VkSimpleWindow or VkWindow and define the interface by overriding the virtual function setUpInterface(),
  • Create an instance of VkSimpleWindow or VkWindow, define the interface separately, and then add the interface as the window's view

The following sections describe each method, and the advantages and disadvantages of each approach.

Creating the Window Interface in the Constructor

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():

void addView(Widget w)
void addView(VkComponent *component)

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.


Example 17. Creating a Window Interface in the Class Constructor

Code

///////////////////////////
// ScaleWindow.h
///////////////////////////
#include <Vk/VkSimpleWindow.h>
class ScaleWindow: public VkSimpleWindow {
public:
ScaleWindow (const char *);
~ScaleWindow();
virtual const char* className();
private:
static String _defaultResources[];
};
///////////////////////////
// ScaleWindow.C
///////////////////////////
#include "ScaleWindow.h"
#include <Xm/RowColumn.h>
#include <Xm/Scale.h>
String ScaleWindow::_defaultResources[] = {
"*dayScale.titleString: Days",
"*weekScale.titleString: Weeks",
"*monthScale.titleString: Months",
NULL };
ScaleWindow::ScaleWindow (const char *name) : VkSimpleWindow (name)
{
setDefaultResources(mainWindowWidget(), _defaultResources);
Widget scales = XtCreateWidget("scales", xmRowColumnWidgetClass,
mainWindowWidget(), NULL, 0);
Widget dayScale = XtCreateManagedWidget("dayScale", xmScaleWidgetClass,
scales, NULL, 0);
XtVaSetValues(dayScale,
XmNorientation, XmHORIZONTAL,
XmNminimum, 1,
XmNmaximum, 7,
XmNvalue, 1,
XmNshowValue, TRUE,
NULL);
Widget weekScale = XtCreateManagedWidget("weekScale", xmScaleWidgetClass,
scales, NULL, 0);
XtVaSetValues(weekScale,
XmNorientation, XmHORIZONTAL,
XmNminimum, 1,
XmNmaximum, 52,
XmNvalue, 1,
XmNshowValue, TRUE,
NULL);
Widget monthScale = XtCreateManagedWidget("monthScale", xmScaleWidgetClass,
scales, NULL, 0);
XtVaSetValues(monthScale,
XmNorientation, XmHORIZONTAL,
XmNminimum, 1,
XmNmaximum, 12,
XmNvalue, 1,
XmNshowValue, TRUE,
NULL);
addView(scales);
}
ScaleWindow::~ScaleWindow()
{
// Empty
}
const char* ScaleWindow::className()
{
return "ScaleWindow";
}
///////////////////////////
// scaleApp.C
///////////////////////////
#include "ScaleWindow.h"
#include <Vk/VkApp.h>
void main ( int argc, char **argv )
{
VkApp *scaleApp = new VkApp("ScaleApp", &argc, argv);
ScaleWindow *scaleWin = new ScaleWindow("scaleWin");
scaleWin->show();
scaleApp->run();
}

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.

Example 18. Using a Component as a Window's View

Code

///////////////////////////
// RadioWindow.h
///////////////////////////
#include <Vk/VkSimpleWindow.h>
class RadioWindow: public VkSimpleWindow {
public:
RadioWindow (const char *);
~RadioWindow();
virtual const char* className();
private:
static String _defaultResources[];
};
///////////////////////////
// RadioWindow.C
///////////////////////////
#include "RadioWindow.h"
#include <Vk/VkRadioBox.h>
String RadioWindow::_defaultResources[] = {
"*color*label*labelString: Color",
"*red.labelString: Red",
"*green.labelString: Green",
"*blue.labelString: Blue",
NULL };
RadioWindow::RadioWindow (const char *name) : VkSimpleWindow (name)
{
setDefaultResources(mainWindowWidget(), _defaultResources);
VkRadioBox *rb = new VkRadioBox( "color", mainWindowWidget() );
rb->addItem("red");
rb->addItem("green");
rb->addItem("blue");
addView(rb);
}
RadioWindow::~RadioWindow()
{
// Empty
}
const char* RadioWindow::className()
{
return "RadioWindow";
}
///////////////////////////
// radioApp.C
///////////////////////////
#include <Vk/VkApp.h>
#include "RadioWindow.h"
void main ( int argc, char **argv )
{
VkApp *radioApp = new VkApp("RadioApp", &argc, argv);
RadioWindow *radioWin = new RadioWindow("radioWin");
radioWin->show();
radioApp->run();
}

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

Creating a Window Interface in the setUpInterface() Function

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.

Example 19. Creating a Window's Interface in setUpInterface() Function

Code

///////////////////////////
// RadioWindow2.h
///////////////////////////
#include <Vk/VkSimpleWindow.h>
class RadioWindow: public VkSimpleWindow {
public:
RadioWindow (const char *);
~RadioWindow();
virtual const char* className();
protected:
Widget setUpInterface(Widget);
private:
static String _defaultResources[];
};
///////////////////////////
// RadioWindow2.C
///////////////////////////
#include "RadioWindow2.h"
#include <Vk/VkRadioBox.h>
String RadioWindow::_defaultResources[] = {
"*color*label*labelString: Color",
"*red.labelString: Red",
"*green.labelString: Green",
"*blue.labelString: Blue",
NULL };
RadioWindow::RadioWindow (const char *name) : VkSimpleWindow (name)
{
// Empty
}
RadioWindow::~RadioWindow()
{
// Empty
}
const char* RadioWindow::className()
{
return "RadioWindow";
}
Widget RadioWindow::setUpInterface (Widget parent)
{
setDefaultResources(mainWindowWidget(), _defaultResources);
VkRadioBox *rb = new VkRadioBox( "color", parent );
rb->addItem("red");
rb->addItem("green");
rb->addItem("blue");
return(*rb);
}

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() );

Adding a Window Interface to a Direct Instantiation of a ViewKit ObjectPak Window Class

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.

Example 20. Adding a View to a Direct Instantiation of a Window Class

Code

VkSimpleWindow *roloWindow = VkSimpleWindow("roloWindow");
Rolodex *rolodex = Rolodex( "rolodex", roloWindow->mainWindowWidget() );
roloWindow->addView(rolodex);

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.

Replacing a Window's View

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().

Documentation Type: 

D

data members, initializing with X resources
deactivate() (in VkMenu)
deactivate() (in VkMenuItem)
deactivate() (in VkPrefItem)
deactivating
menu items [2]
preference items
defining ViewKit callbacks
deleteCallback (in VkComponent) [2]
deleteChildren() (in VkPrefGroup)
demonstration programs
dependencies
classes
VkApp [2]
deselecting
nodes in graphs
detach() (in VkModifiedAttachment)
detach() (in VkResizer)
dialogs
Apply button
busy [2]
installing
button labels, setting
Cancel button
centering algorithm
custom
error
event handling
during postAndWait()
during sendSyncRequest()
during wasInterrupted()
fatal error
file selection
caution
generic
Help button [2] [3]
information
interruptible busy
checking for interruptions
installing [2]
message
OK button
overview
parent widget
pointers
posting
examples
methods
preference
Product Information [2]
prompt
question
VkMenuConfirmFirstAction use
title, setting
unposting
warning
disabling multi-level undo support
Display structure
display() (in VkApp)
display() (in VkGraph)
displayAll() (in VkGraph)
displayButterfly() (in VkGraph)
displayIf() (in VkGraph)
displaying
components
graph overview window [2]
menu items
modified text attachment dogear
nodes in graphs [2] [3] [4]
resizer geometry controls
windows [2]
displayParentsAndChildren() (in VkGraph)
displayValue() (in VkModifiedAttachment)
displayWithAllChildren() (in VkGraph)
displayWithAllParents() (in VkGraph)
displayWithChildren() (in VkGraph)
displayWithParents() (in VkGraph)
distributeHorizontal() (in VkAlignmentGroup)
distributeVertical() (in VkAlignmentGroup)
doit() (in VkAction)
doit() (in VkMenuActionObject)
doLayout() (in VkGraph)
doSparseLayout() (in VkGraph)
doSubtreeLayout() (in VkGraph)
double-buffer component
drawing
resizing
switching buffers
draw() (in VkDoubleBuffer)
drawing, double-buffered

Data Input Classes: Overview

Overview

This chapter includes the following sections:

Inheritance graph

Figure 51. shows the inheritance graph for the ViewKit ObjectPak classes used for data input.

Figure 51. Inheritance Graph for Miscellaneous ViewKit Input Classes

Definition of a Component

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.

Deriving Classes from VkApp

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.

VkApp Protected Functions and Data Members

You can use VkApp::parseCommandLine() to parse command line options:

int parseCommandLine(XrmOptionDescRec *options,
Cardinal numOptions)

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.

Example of Subclassing VkApp

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.


Example 16. Deriving a Subclass from VkApp

Code

#include <Vk/VkApp.h>
#include <Vk/VkResource.h>
class MyApp : public VkApp {
public:
MyApp(char *appClassName,
int *arg_c,
char **arg_v,
XrmOptionDescRec *optionList = NULL,
int sizeOfOptionList = 0);
Boolean verbose() { return _verbose; } // Access function
protected:
Boolean _verbose; // Data member to initialize
private:
static XrmOptionDescRec _cmdLineOptions[]; // Command-line options
static XtResource _resources[]; // Resource descriptions
};
// Describe the command line options
XrmOptionDescRec MyApp::_cmdLineOptions[] =
{
{
"-verbose", "*verbose", XrmoptionNoArg, "TRUE",
},
};
// Describe the resources to retrieve and use to initialize the class
XtResource MyApp::_resources [] = {
{
"verbose",
"Verbose",
XmRBoolean,
sizeof ( Boolean ),
XtOffset ( MyApp *, _verbose ),
XmRString,
(XtPointer) "FALSE",
},
};
MyApp::MyApp(char *appClassName,
int *arg_c,
char **arg_v,
XrmOptionDescRec *optionList,
int sizeOfOptionList) : VkApp(appClassName,
arg_c,
arg_v,
optionList,
sizeOfOptionList)
{
// Parse the command line, loading options into the resource database
*arg_c = parseCommandLine(_cmdLineOptions,
XtNumber(_cmdLineOptions));
// Initialize this class from the resource data base
getResources (_resources, XtNumber(_resources));
}

Documentation Type: 

Deriving Subclasses to Create New Components

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.

Subclassing Summary

The following is a summary of guidelines for writing components based on the VkComponent class:

  • Encapsulate all of your component's widgets in a single-rooted subtree. While some extremely simple components might contain only a single widget, the majority of components must create some type of container widget as the root of the component's widget subtree; all other widgets are descendents of this one.
  • When you create your class's base widget, assign it to the _baseWidget data member inherited from the VkComponent class.
  • In most cases, create a component's base widget and all other widgets in the class constructor. The constructor should manage all widgets except the base widget, which should be left unmanaged. You can then manage or unmanage a component's entire widget subtree using the show() and hide() member functions.
  • Accept at least two arguments in your component's constructor: a string to be used as the name of the base widget, and a widget to be used as the parent of the component's base widget. Pass the name argument to the VkComponent constructor, which makes a copy of the string. Refer to a component's name using the _name member inherited from VkComponent or the name() access function. Refer to a component's base widget using the _baseWidget member inherited from VkComponent or the baseWidget() access function.
  • Override the virtual className() member function for your component classes to return a string consisting of the name of the component's C++ class.
  • Define all Xt callbacks required by a component class as private static member functions. In exceptional cases, you might want to declare them as protected so that derived classes can access them.
  • Pass the this pointer as client data to all Xt callback functions. Callback functions should retrieve this pointer, cast it to the expected component type and call a corresponding member function. For clarity, use the convention of giving static member functions used as callbacks the same name as the member function they call, with the word "Callback" appended. For example, name a static member function startCallback() if it calls the member function start().
  • Call installDestroyHandler() immediately after creating a component's base widget.
  • If you need to specify default resources for a component class, call the function setDefaultResources() with an appropriate resource list before creating the component's base widget.
  • If you need to initialize data members from values in the resource database, define an appropriate resource specification and call the function getResources() immediately after creating the component's base widget.

Creating a New Component

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

Example 8. A Simple User-Defined Component

The following code section lists the full implementation of this class.

Code

//////////////////////////////////////////////////////////////
// StartStopPanel.h
//////////////////////////////////////////////////////////////
#ifndef _STARTSTOPPANEL_H
#define _STARTSTOPPANEL_H
#include <Vk/VkComponent.h>
enum PanelAction { START, STOP };
class StartStopPanel : public VkComponent {
public:
StartStopPanel (const char *, Widget);
~StartStopPanel();
virtual const char *className();
static const char *const actionCallback;
protected:
virtual void start(Widget, XtPointer);
virtual void stop(Widget, XtPointer);
Widget _startButton;
Widget _stopButton;
private:
static void startCallback(Widget, XtPointer, XtPointer);
static void stopCallback(Widget, XtPointer, XtPointer);
static String _defaultResources[];
};
#endif
/////////////////////////////////////////////////////////////
// StartStopPanel.C
/////////////////////////////////////////////////////////////
#include "StartStopPanel.h"
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
// These are default resources for widgets in objects of this class
// All resources will be prefixed by *<name> at instantiation,
// where <name> is the name of the specific instance, as well as the
// name of the baseWidget. These are only defaults, and may be
// overriden in a resource file by providing a more specific resource
// name
String StartStopPanel::_defaultResources[] = {
"*start.labelString: Start",
"*stop.labelString: Stop",
NULL
};
const char *const StartStopPanel::actionCallback = "actionCallback";
StartStopPanel::StartStopPanel(const char *name, Widget parent) : VkComponent(name)
{
// Load class-default resources for this object before creating base widget
setDefaultResources(parent, _defaultResources );
// Create an XmRowColumn widget as the component's base widget
// to contain the buttons. Assign the widget to the _baseWidget
// data member.
_baseWidget = XmCreateRowColumn ( parent, _name, NULL, 0 );
// Set up callback to handle widget destruction
installDestroyHandler();
XtVaSetValues(_baseWidget, XmNorientation, XmHORIZONTAL, NULL);
// Create all other widgets as children of the base widget.
// Manage all child widgets.
_startButton = XmCreatePushButton ( _baseWidget, "start", NULL, 0);
_stopButton = XmCreatePushButton ( _baseWidget, "stop", NULL, 0);
XtManageChild(_startButton);
XtManageChild(_stopButton);
// Install static member functions as callbacks for the buttons
XtAddCallback(_startButton, XmNactivateCallback,
&StartStopPanel::startCallback, (XtPointer) this );
XtAddCallback(_stopButton, XmNactivateCallback,
&StartStopPanel::stopCallback, (XtPointer) this );
}
StartStopPanel::~StartStopPanel()
{
// Empty
}
const char* StartStopPanel::className()
{
return "StartStopPanel";
}
void StartStopPanel::startCallback(Widget w, XtPointer clientData,
XtPointer callData)
{
StartStopPanel *obj = ( StartStopPanel * ) clientData;
obj->start(w, callData);
}
void StartStopPanel::stopCallback(Widget w, XtPointer clientData,
XtPointer callData)
{
StartStopPanel *obj = ( StartStopPanel * ) clientData;
obj->stop(w, callData);
}
void StartStopPanel::start(Widget, XtPointer)
{
callCallbacks(actionCallback, (void *) START);
}
void StartStopPanel::stop(Widget, XtPointer)
{
callCallbacks(actionCallback, (void *) STOP);
}

Examples of Using and Subclassing a Component 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.

Using a Component Class Directly

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:

  1. It instantiates a StartStopPanel object named "controlPanel" and assigns it to the _controlPanel variable.
  2. It specifies a vertical orientation for the StartStopPanel object.
  3. It installs PanelWindow::statusChanged() as an ObjectPak callback function to be called whenever StartStopPanel::actionCallback triggers. In this example, PanelWindow::statusChanged() simply prints a status message to standard output whenever it is called.
  4. It installs the _controlPanel object as the window's "view." Showing the PanelWindow object will now display the _controlPanel object. ("Creating the Window Interface" describes how to create window interfaces.)

Example 9. Using a Component Directly

Code

//////////////////////////////////////////////////////////////
// PanelWindow.h
//////////////////////////////////////////////////////////////
#ifndef _PANELWINDOW_H
#define _PANELWINDOW_H
#include "StartStopPanel.h"
#include <Vk/VkSimpleWindow.h>
// Define a top-level window class
class PanelWindow: public VkSimpleWindow {
public:
PanelWindow(const char *name);
~PanelWindow();
virtual const char* className();
protected:
void statusChanged(VkCallbackObject *, void *, void *);
StartStopPanel * _controlPanel;
};
#endif
//////////////////////////////////////////////////////////////
// PanelWindow.C
//////////////////////////////////////////////////////////////
#include "PanelWindow.h"
#include <iostream.h>
PanelWindow::PanelWindow(const char *name) : VkSimpleWindow (name)
{
_controlPanel = new StartStopPanel( "controlPanel",
mainWindowWidget() );
XtVaSetValues(_controlPanel->baseWidget(),
XmNorientation, XmVERTICAL, NULL);
_controlPanel->addCallback( StartStopPanel::actionCallback, this,
(VkCallbackMethod) &PanelWindow::statusChanged );
addView(_controlPanel);
}
const char * PanelWindow::className()
{
return "PanelWindow";
}
PanelWindow::~PanelWindow()
{
// Empty
}
void PanelWindow::statusChanged(VkCallbackObject *obj,
void *, void *callData)
{
StartStopPanel * panel = (StartStopPanel *) obj;
PanelAction action = (PanelAction) callData;
switch (action) {
case START:
cout << "Process started\n" << flush;
break;
case STOP:
cout << "Process stopped\n" << flush;
break;
default:
cout << "Undefined state\n" << flush;
}
}

The following simple program displays the resulting PanelWindow object (Refer to Chapter 3--Application Class for more information about the VkApp:

//////////////////////////////////////////////////////////////
// PanelTest.C
//////////////////////////////////////////////////////////////
#include <Vk/VkApp.h>
#include "PanelWindow.h"
// Main driver. Just instantiate a VkApp and the PanelWindow,
// "show" the window and then "run" the application.
void main ( int argc, char **argv )
{
VkApp *panelApp = new VkApp("panelApp", &argc, argv);
PanelWindow *panelWin = new PanelWindow("panelWin");
panelWin->show();
panelApp->run();
}

Figure 5. shows the resulting PanelWindow window displayed by this program.

Figure 5. Resulting PanelWindow Window

Using a Component Class by Subclassing

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.

Example 10. Subclassing a Component

Code

//////////////////////////////////////////////////////////////
// ControlPanel.h
//////////////////////////////////////////////////////////////
#ifndef _CONTROLPANEL_H
#define _CONTROLPANEL_H
#include "StartStopPanel.h"
class ControlPanel : public StartStopPanel {
public:
ControlPanel (const char *, Widget);
~ControlPanel();
virtual const char *className();
protected:
virtual void start(Widget, XtPointer);
virtual void stop(Widget, XtPointer);
};
#endif
//////////////////////////////////////////////////////////////
// ControlPanel.C
//////////////////////////////////////////////////////////////
#include "ControlPanel.h"
#include <iostream.h>
ControlPanel::ControlPanel (const char *name , Widget parent) :
StartStopPanel (name, parent)
{
XtVaSetValues(_baseWidget, XmNorientation, XmVERTICAL, NULL);
}
ControlPanel::~ControlPanel()
{
// Empty
}
const char* ControlPanel::className()
{
return "ControlPanel";
}
void ControlPanel::start(Widget w, XtPointer callData)
{
cout << "Process started\n" << flush;
StartStopPanel::start(w, callData);
}
void ControlPanel::stop(Widget w, XtPointer callData)
{
cout << "Process stopped\n" << flush;
StartStopPanel::stop(w, callData);
}

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:

Code

//////////////////////////////////////////////////////////////
// PanelTest2.C
//////////////////////////////////////////////////////////////
#include <Vk/VkApp.h>
#include <Vk/VkSimpleWindow.h>
#include "ControlPanel.h"
// Main driver. Instantiate a VkApp, a VkSimpleWindow, and a
// ControlPanel, add the ControlPanel as the SimpleWindow's view,
// "show" the window and then "run" the application.
void main ( int argc, char **argv )
{
VkApp *panelApp = new VkApp("panel2App", &argc, argv);
VkSimpleWindow *panelWin = new VkSimpleWindow("panelWin");
ControlPanel *control = new ControlPanel("control",
panelWin->mainWindowWidget() );
panelWin->addView(control);
panelWin->show();
panelApp->run();
}

Deriving Window Subclasses

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.

Additional Virtual Functions and Data Members

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:

  • Provide a "safe quit" mechanism for your window
  • Determine your window's state and perform actions on state changes
  • Perform actions after realizing a window
  • Handle raw events not normally handled by the Xt dispatch mechanism

Providing a "safe quit" mechanism

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.)

Determining window states

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.

Performing actions after realizing a window

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.


Handling raw events

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.

Additional Data Members

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.

Window Creation Summary

The following lists a summary of guidelines for creating subclasses of the ObjectPak window classes:

  • Decide whether this window requires a menu bar. If it does, derive your subclass from VkWindow; otherwise, derive it from VkSimpleWindow.
  • In most cases where you provide a menu bar for your window, you should create it in the window class when you create the rest of your window's interface.
  • Determine whether users will often use your application without displaying this window even after the object is instantiated. If so, and the window interface is large or complex, you might consider creating the window interface using setUpInterface() to reduce the time it takes to start your application; otherwise, create the interface in the window's constructor.
  • Implement the window interface as a single-rooted widget subtree whose parent is the window's XmMainWindow widget (obtained by the mainWindowWidget() function). While some windows might contain only a single complex component, the majority of windows must create some type of container widget as the root of the window's interface; all other widgets and components are descendents of this widget.
  • Do not assign any widget to the _baseWidget data member. The ViewKit ObjectPak window classes assign the window's popup shell widget to _baseWidget.
  • Wherever appropriate, use resource values to set labels, other interface characteristics, and user-configurable component behavior. Define a default resource list as a static member variable of your window class, and call setDefaultResources() to set your window's default resources before creating the window interface.
  • Override the className() function to return the name of your window's class.
  • In addition to the widgets and components composing the window's interface, encapsulate any other required data and support functions as members of your window class.
  • If you explicitly allocate any memory in your derived window class, remember to free it in the window's destructor.
  • To explicitly set your window's title or its icon's title, call setTitle() or setIconName() respectively. You can also set these characteristics using the normal resource mechanisms.
  • To provide a "safe quit" mechanism for your window, override okToQuit() to perform any checking you want to perform before deleting the window.
  • To change how your window handles a WM_DELETE_MESSAGE from the window manager, override handleWmDeleteMessage().
  • To change how your window handles a WM_QUIT_APP from the window manager, override handleWmQuitMessage().
  • To set any additional properties on your window, override setUpWindowProperties().
  • To change the value of the window manager class hint stored on a window, call setClassHint().
  • To perform certain actions only after the window exists, override afterRealizeHook().
  • To handle events not normally handled by the Xt dispatch mechanism, call XSelectInput(3) to select the events that you want to receive, and override handleRawEvent() in your window subclass to implement your event processing.

Window Subclassing Example

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:

  • A Label gadget to serve as a title
  • A label gadget to report the resulting color

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).


Example 22. Creating a Window Subclass

Code

///////////////////////////
// ColorWindow.h
///////////////////////////
#include <Vk/VkSimpleWindow.h>
#include <Vk/VkCheckBox.h>
class ColorWindow: public VkSimpleWindow {
public:
ColorWindow (const char *);
~ColorWindow();
virtual const char* className();
private:
void displayColor(char *);
void colorChanged(VkCallbackObject *, void *, void *);
static String _defaultResources[]; // Default resource values
static String _colors[]; // Array of possible resulting colors
Widget _resultColor; // Label to display resulting color
VkCheckBox *_primaries; // Checkbox for setting colors
int _colorStatus; // Bit-wise color status variable
// Bit 0: Cyan
// Bit 1: Magenta
// Bit 2: Yellow
// Also used as index into _colors[]
};
///////////////////////////
// ColorWindow.C
///////////////////////////
#include "ColorWindow.h"
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/LabelG.h>
#include <Vk/VkCheckBox.h>
#include <Vk/VkResource.h>
// Default ColorWindow class resource values.
String ColorWindow::_defaultResources[] = {
"*windowTitle: Color Mixer",
"*iconTitle: Color Mixer",
"*primaries*label*labelString: Primary Colors",
"*cyan.labelString: Cyan",
"*magenta.labelString: Magenta",
"*yellow.labelString: Yellow",
"*resultLabel.labelString: Resulting Color",
"*cyan: Cyan",
"*magenta: Magenta",
"*yellow: Yellow",
"*blue: Blue",
"*red: Red",
"*green: Green",
"*white: White",
"*black: Black",
NULL };
// Set _colors array to correspond to color values indicated by the
// bits in the _colorStatus variable.
String ColorWindow::_colors[] = {
"white",
"cyan",
"magenta",
"blue",
"yellow",
"green",
"red",
"black" };
ColorWindow::ColorWindow (const char *name) : VkSimpleWindow (name)
{
Arg args[5];
int n;
// Set default resources for the window.
setDefaultResources(mainWindowWidget(), _defaultResources);
// Create a Form widget to use as the window's view.
Widget _form = XmCreateForm(mainWindowWidget(), "form", NULL, 0);
// Create a VkCheckBox object to allow users to select primary colors.
// Add toggle buttons and set their intial values to FALSE (unselected).
// The labels for the checkbox frame and the toggle buttons are set
// by the resouce database.
_primaries = new VkCheckBox( "primaries", _form );
_primaries->addItem("cyan", FALSE);
_primaries->addItem("magenta", FALSE);
_primaries->addItem("yellow", FALSE);
_primaries->addCallback(VkCheckBox::itemChangedCallback, this,
(VkCallbackMethod) &ColorWindow::colorChanged);
_primaries->show();
// Set constraint resources on checkbox's base widget.
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetValues(_primaries->baseWidget(), args, n);
// Create a frame to display the name of the resulting blended color.
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, _primaries->baseWidget()); n++;
Widget _result = XmCreateFrame(_form, "result", args, n);
XtManageChild(_result);
// Create a frame title label. The label text is set by the resource
// database.
n = 0;
XtSetArg(args[n], XmNchildType, XmFRAME_TITLE_CHILD); n++;
Widget _resultLabel = XmCreateLabelGadget( _result, "resultLabel", args, n);
// Create the label to display the blended color name.
_resultColor = XmCreateLabelGadget( _result, "resultColor", NULL, 0);
// Set intial value of _colorStatus and label string to white (all off).
_colorStatus = 0;
displayColor(_colors[_colorStatus]);
XtManageChild(_resultLabel);
XtManageChild(_resultColor);
// Add the top-level Form widget as the window's view.
addView(_form);
// Set the window title and the icon title.
setTitle("windowTitle");
setIconName("iconTitle");
}
ColorWindow::~ColorWindow()
{
// Empty
}
const char* ColorWindow::className()
{
return "ColorWindow";
}
// Given a color name, update the label to display the color
void ColorWindow::displayColor(char *newColor)
{
Arg args[2];
int n;
// Common resource trick in ObjectPak applications.
// Given a string, check the resource database for a corresponding
// value. If none exists, use the string as the value.
char *_colorName = (char *) VkGetResource(_baseWidget, newColor, "Color",
XmRString, newColor);
// Update the label
XmString _label = XmStringCreateSimple(_colorName);
n = 0;
XtSetArg(args[n], XmNlabelString, _label); n++;
XtSetValues(_resultColor, args, n);
XmStringFree(_label);
}
// When the user changes the value of one of the toggles, update the
// display to show the new blended color.
void ColorWindow::colorChanged(VkCallbackObject *obj, void *, void *callData)
{
ColorWindow *win = (ColorWindow *) obj;
int index = (int) callData;
// Update color status based on toggle value. Set or rest the
// status bit corresponding to the respective toggle.
if (_primaries->getValue(index))
_colorStatus |= 1<<index;
else
_colorStatus &= ~(1<<index);
// Update the display to show the new blended color, using
// _colorStatus as an index.
displayColor(_colors[_colorStatus]);
}
///////////////////////////
// colors.C
///////////////////////////
#include <Vk/VkApp.h>
#include "ColorWindow.h"
void main ( int argc, char **argv )
{
VkApp *colorApp = new VkApp("ColorApp", &argc, argv);
ColorWindow *colorWin = new ColorWindow("colorWin");
colorWin->show();
colorApp->run();
}

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.

Additional Virtual Functions and Data Members

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:

  • Provide a "safe quit" mechanism for your window
  • Determine your window's state and perform actions on state changes
  • Perform actions after realizing a window
  • Handle raw events not normally handled by the Xt dispatch mechanism

Providing a "safe quit" mechanism

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.)

Determining window states

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.

Performing actions after realizing a window

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.


Handling raw events

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.

Additional Data Members

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.

Window Creation Summary

The following lists a summary of guidelines for creating subclasses of the ObjectPak window classes:

  • Decide whether this window requires a menu bar. If it does, derive your subclass from VkWindow; otherwise, derive it from VkSimpleWindow.
  • In most cases where you provide a menu bar for your window, you should create it in the window class when you create the rest of your window's interface.
  • Determine whether users will often use your application without displaying this window even after the object is instantiated. If so, and the window interface is large or complex, you might consider creating the window interface using setUpInterface() to reduce the time it takes to start your application; otherwise, create the interface in the window's constructor.
  • Implement the window interface as a single-rooted widget subtree whose parent is the window's XmMainWindow widget (obtained by the mainWindowWidget() function). While some windows might contain only a single complex component, the majority of windows must create some type of container widget as the root of the window's interface; all other widgets and components are descendents of this widget.
  • Do not assign any widget to the _baseWidget data member. The ViewKit ObjectPak window classes assign the window's popup shell widget to _baseWidget.
  • Wherever appropriate, use resource values to set labels, other interface characteristics, and user-configurable component behavior. Define a default resource list as a static member variable of your window class, and call setDefaultResources() to set your window's default resources before creating the window interface.
  • Override the className() function to return the name of your window's class.
  • In addition to the widgets and components composing the window's interface, encapsulate any other required data and support functions as members of your window class.
  • If you explicitly allocate any memory in your derived window class, remember to free it in the window's destructor.
  • To explicitly set your window's title or its icon's title, call setTitle() or setIconName() respectively. You can also set these characteristics using the normal resource mechanisms.
  • To provide a "safe quit" mechanism for your window, override okToQuit() to perform any checking you want to perform before deleting the window.
  • To change how your window handles a WM_DELETE_MESSAGE from the window manager, override handleWmDeleteMessage().
  • To change how your window handles a WM_QUIT_APP from the window manager, override handleWmQuitMessage().
  • To set any additional properties on your window, override setUpWindowProperties().
  • To change the value of the window manager class hint stored on a window, call setClassHint().
  • To perform certain actions only after the window exists, override afterRealizeHook().
  • To handle events not normally handled by the Xt dispatch mechanism, call XSelectInput(3) to select the events that you want to receive, and override handleRawEvent() in your window subclass to implement your event processing.

Window Subclassing Example

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:

  • A Label gadget to serve as a title
  • A label gadget to report the resulting color

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).


Example 22. Creating a Window Subclass

Code

///////////////////////////
// ColorWindow.h
///////////////////////////
#include <Vk/VkSimpleWindow.h>
#include <Vk/VkCheckBox.h>
class ColorWindow: public VkSimpleWindow {
public:
ColorWindow (const char *);
~ColorWindow();
virtual const char* className();
private:
void displayColor(char *);
void colorChanged(VkCallbackObject *, void *, void *);
static String _defaultResources[]; // Default resource values
static String _colors[]; // Array of possible resulting colors
Widget _resultColor; // Label to display resulting color
VkCheckBox *_primaries; // Checkbox for setting colors
int _colorStatus; // Bit-wise color status variable
// Bit 0: Cyan
// Bit 1: Magenta
// Bit 2: Yellow
// Also used as index into _colors[]
};
///////////////////////////
// ColorWindow.C
///////////////////////////
#include "ColorWindow.h"
#include <Xm/RowColumn.h>
#include <Xm/Form.h>
#include <Xm/Frame.h>
#include <Xm/LabelG.h>
#include <Vk/VkCheckBox.h>
#include <Vk/VkResource.h>
// Default ColorWindow class resource values.
String ColorWindow::_defaultResources[] = {
"*windowTitle: Color Mixer",
"*iconTitle: Color Mixer",
"*primaries*label*labelString: Primary Colors",
"*cyan.labelString: Cyan",
"*magenta.labelString: Magenta",
"*yellow.labelString: Yellow",
"*resultLabel.labelString: Resulting Color",
"*cyan: Cyan",
"*magenta: Magenta",
"*yellow: Yellow",
"*blue: Blue",
"*red: Red",
"*green: Green",
"*white: White",
"*black: Black",
NULL };
// Set _colors array to correspond to color values indicated by the
// bits in the _colorStatus variable.
String ColorWindow::_colors[] = {
"white",
"cyan",
"magenta",
"blue",
"yellow",
"green",
"red",
"black" };
ColorWindow::ColorWindow (const char *name) : VkSimpleWindow (name)
{
Arg args[5];
int n;
// Set default resources for the window.
setDefaultResources(mainWindowWidget(), _defaultResources);
// Create a Form widget to use as the window's view.
Widget _form = XmCreateForm(mainWindowWidget(), "form", NULL, 0);
// Create a VkCheckBox object to allow users to select primary colors.
// Add toggle buttons and set their intial values to FALSE (unselected).
// The labels for the checkbox frame and the toggle buttons are set
// by the resouce database.
_primaries = new VkCheckBox( "primaries", _form );
_primaries->addItem("cyan", FALSE);
_primaries->addItem("magenta", FALSE);
_primaries->addItem("yellow", FALSE);
_primaries->addCallback(VkCheckBox::itemChangedCallback, this,
(VkCallbackMethod) &ColorWindow::colorChanged);
_primaries->show();
// Set constraint resources on checkbox's base widget.
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
XtSetValues(_primaries->baseWidget(), args, n);
// Create a frame to display the name of the resulting blended color.
n = 0;
XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n++;
XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
XtSetArg(args[n], XmNleftWidget, _primaries->baseWidget()); n++;
Widget _result = XmCreateFrame(_form, "result", args, n);
XtManageChild(_result);
// Create a frame title label. The label text is set by the resource
// database.
n = 0;
XtSetArg(args[n], XmNchildType, XmFRAME_TITLE_CHILD); n++;
Widget _resultLabel = XmCreateLabelGadget( _result, "resultLabel", args, n);
// Create the label to display the blended color name.
_resultColor = XmCreateLabelGadget( _result, "resultColor", NULL, 0);
// Set intial value of _colorStatus and label string to white (all off).
_colorStatus = 0;
displayColor(_colors[_colorStatus]);
XtManageChild(_resultLabel);
XtManageChild(_resultColor);
// Add the top-level Form widget as the window's view.
addView(_form);
// Set the window title and the icon title.
setTitle("windowTitle");
setIconName("iconTitle");
}
ColorWindow::~ColorWindow()
{
// Empty
}
const char* ColorWindow::className()
{
return "ColorWindow";
}
// Given a color name, update the label to display the color
void ColorWindow::displayColor(char *newColor)
{
Arg args[2];
int n;
// Common resource trick in ObjectPak applications.
// Given a string, check the resource database for a corresponding
// value. If none exists, use the string as the value.
char *_colorName = (char *) VkGetResource(_baseWidget, newColor, "Color",
XmRString, newColor);
// Update the label
XmString _label = XmStringCreateSimple(_colorName);
n = 0;
XtSetArg(args[n], XmNlabelString, _label); n++;
XtSetValues(_resultColor, args, n);
XmStringFree(_label);
}
// When the user changes the value of one of the toggles, update the
// display to show the new blended color.
void ColorWindow::colorChanged(VkCallbackObject *obj, void *, void *callData)
{
ColorWindow *win = (ColorWindow *) obj;
int index = (int) callData;
// Update color status based on toggle value. Set or rest the
// status bit corresponding to the respective toggle.
if (_primaries->getValue(index))
_colorStatus |= 1<<index;
else
_colorStatus &= ~(1<<index);
// Update the display to show the new blended color, using
// _colorStatus as an index.
displayColor(_colors[_colorStatus]);
}
///////////////////////////
// colors.C
///////////////////////////
#include <Vk/VkApp.h>
#include "ColorWindow.h"
void main ( int argc, char **argv )
{
VkApp *colorApp = new VkApp("ColorApp", &argc, argv);
ColorWindow *colorWin = new ColorWindow("colorWin");
colorWin->show();
colorApp->run();
}

The colors program displays the ColorWindow shown in Figure 15.

Figure 15. Example of the ColorWindow Window Subclass

E

Establishing a Connection to the ToolTalk Service

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:

VkMsgApp(char* appClassName, int* argc, char** argv,
XrmOptionDescRec* optionList = NULL,
int sizeOfOptionList = 0,
const char* ptid = NULL,
const char* sessid = NULL
Boolean noProtocol = FALSE)

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.

Command line arguments

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()

Exiting ViewKit ObjectPak Applications

Exiting with the option to abort

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.)

Default behavior

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.

Checking components before exiting

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.

Exiting Automatically

A ViewKit ObjectPak application automatically exits when all of its windows are deleted, under any of the following circumstances:

  • Application calls quitYourself()
  • Application deletes all of its windows individually
  • User deletes all application windows through window manager interaction (for example, selecting the "Close" option in the window menu provided by the window manager)

VkApp::terminate():

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.

Calling terminate()

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.

Overriding terminate()

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().


F

G

Getting Started

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.

Simple ViewKit Program

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:

Example 1. hello.C Example

Code

#include <Vk/VkApp.h>
#include <Vk/VkSimpleWindow.h>
#include <Xm/Label.h>
// Define a top-level window class
class HelloWindow: public VkSimpleWindow {
public:
HelloWindow (const char *name);
~HelloWindow();
virtual const char* className();
};
// Construct a single rooted widget tree, and designate the
// root of the tree as the window's view. This example is very
// simple, just creating a single XmLabel widget to display the
// string "hello".
HelloWindow::HelloWindow (const char *name) : VkSimpleWindow (name)
{
Widget label = XmCreateLabel (mainWindowWidget(), "hello",
NULL, 0);
addView(label);
}
const char* HelloWindow::className()
{
return "HelloWindow"; // Identify this class
}
HelloWindow::~HelloWindow()
{
// Empty
}
// Main driver. Just instantiate a VkApp and a top-level window,
// "show" the window and then "run" the application.
void main ( int argc, char **argv )
{
VkApp *app = new VkApp("Hello", &argc, argv);
HelloWindow *win = new HelloWindow("hello");
win->show();
app->run();
}

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:

CC -o hello hello.C -lvk -lvkhelp -lXm -lXt -lX11

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

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:

  • ${ObjectPak}/examples/ProgrammersGuide contains several of the example programs from this guide.
  • ${ObjectPak}/examples/Components/CBrowser contains the source for a component browser, which shows examples of many ObjectPak components. You might find this particularly useful to run when you read the later chapters in this guide that describe the prebuilt components shipped with ObjectPak.
  • ${ObjectPak}/examples/Applications/PhoneBook creates PhoneBook, a full-fledged application that keeps track of names, phone numbers, and addresses.PhoneBook uses a variety of ObjectPak classes.

Graph Component

Overview

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

H

I

Index

Symbols A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

Interprocess Message: Overview

Overview


Note: To develop applications that use ToolTalk, you must have the ToolTalk development software from your vendor.


This appendix includes the following sections:

Inheritance graph

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

L

M

main window
determining
during quitting
specifying
main()
mainWindow() (in VkApp)
mainWindowWidget() (in VkSimpleWindow) [2] 
makeNodeVisible() (in VkGraph)
makeNormal() (in VkAlignmentGroup)
man pages
management classes [2]
alignment groups
ganged scrollbars
modified text
radio-style toggles
resizers
menu bars
VkWindow destructor, and
VkWindow support
menu items
"Undo" selection
adding
setting label
actions
activating [2]
adding to menus
command classes
confirmable actions
deactivating [2]
determining position in menu
displaying
finding
hiding
labels [2]
overview
position
removing [2]
replacing
separators
toggles
type
undo support [2] [3]
menu() (in VkWindow)
menus
"Undo" selection
adding
setting label
activating items [2]
adding items
constructing dynamically
example
constructing from static description
example
VkMenuDesc structure
Xt callback client data
deactivating items [2]
determining item position
displaying items
finding menu items
Help menu [2] [3] [4]
resources
hiding items
menu bars
VkWindow destructor, and
VkWindow support
option menus
example
item width, setting
menu label, setting
selected item, setting
overview
popup menus
attaching to widget
example
popping up
radio submenus
removing items [2]
replacing items
setting item labels
setting item positions
submenus
tear-off behavior
VkMenuDesc structure
VkMenuItemType type
XtDisplay() caution
XtScreen() caution
XtWindow() caution
menuType() (in VkMenuItem)
message actions
message patterns [2]
message, dialogs
messageClient() (in VkMsgApp)
messageClient() (in VkMsgComponent)
messageClient() (in VkMsgWindow)
messages
receiving
sending
meter component
adding items
desired dimensions
resetting
resize policy
updating display
X resource
modified text attachment
adjusting geometry
attaching widgets
controlling contents [2]
detaching widgets
detecting changes
displaying dogear
hiding dogear
overview
retrieving values
modified() (in VkModifiedAttachment)
modifiedCallback (in VkModifiedAttachment)
Motif
suggested
reading
moving
nodes
in graphs
widgets
multi-level undo support
disabling
undo stack
clearing
examining
multiLevel() (in VkMenuUndoManager)
Multiple Arcs button (in VkGraph control panel)
multiple pointers to a component

Maintaining Product and Version Information

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.

Static integer constant

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

Static character array constant

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.

Setting version information

You can use VkApp::setVersionString() to set version information for an application based on ViewKit ObjectPak:

void setVersionString(const char *versionInfo)

Retrieving version information

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

Using custom information dialogs

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()

Management Classes

Controlling display of components and widgets

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.

ViewKit Support for Aligning Widgets

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.

Alignment group constructor and destructor

The VkAlignmentGroup constructor does not take any arguments:

VkAlignmentGroup()

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.

Adding widgets and components to a group

Use the add() function to add widgets or components to a VkAlignmentGroup object:

virtual void add(Widget w)
virtual void add(VkComponent *obj)
virtual void add(VkOptionMenu *menu)

Add() behavior

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.

Removing widgets and components

Remove widgets or components from a VkAlignmentGroup object with the remove() function inherited from VkWidgetList:

virtual void remove(Widget w)
virtual void remove(VkComponent *obj)

Provide the widget ID or component pointer that you used to add the widget or component to the alignment group.

Aligning and distributing

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):

 

 

Function

Description

alignLeft()

Aligns left edges of all widgets by repositioning all widgets so that the left side of each widget is moved to right-most left edge of any widget in the group.

alignRight()

Aligns right edges of all widgets by repositioning all widgets so that the right side of each widget is moved to the right-most position occupied by any widget in group.

alignTop()

Aligns top edges of all widgets by repositioning all widgets so that the top of each widget is moved to the bottom-most top edge of any widget in group.

alignBottom()

Aligns bottom edges of all widgets by repositioning all widgets so that the bottom of each widget is moved to the bottom-most position occupied by any widget in group.

alignWidth()

Resizes all widgets to the width of the largest widget in the group.

alignHeight()

Resizes all widgets to the height of the largest widget in the group.

makeNormal()

Returns all widgets to their desired widths and heights.

distributeVertical()

Repositions all widgets evenly in vertical direction (according to spacing between widgets) between position of the first and the last widgets in group.

distributeHorizontal()

Repositions all widgets evenly in horizontal direction (according to spacing between widgets) between position of the first and the last widgets in group.

 

 

Alignment group access functions

VkAlignmentGroup provides the following access functions:

  • VkAlignmentGroup::width() returns maximum width of all widgets in group. Value set after you call alignWidth().
    Dimension width()

  • VkAlignmentGroup::height() returns maximum height of all widgets in group. Value set after you call alignHeight().
    Dimension height()

  • VkAlignmentGroup::x() returns minimum x position of all widgets in group. Value set after you call either alignLeft() or alignRight().
    Position x()

  • VkAlignmentGroup::y() returns minimum y position of all widgets in group. Value set after you call either alignTop() or alignBottom().
    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.

ViewKit Support for Resizing and Moving Widgets

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.

Resizer constructor and destructor

The VkResizer constructor accepts two Boolean arguments:

VkResizer(Boolean autoAdjust = FALSE, Boolean liveResize = FALSE)

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.

Attaching and detaching a resizer object to and from a widget

Once you have created a VkResizer object, use the VkResizer::attach() function to attach it to an existing widget:

void attach(Widget w)

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:

resizer->attach( obj->baseWidget() );

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:

void detach()

Displaying the resizer object's geometry controls

After attaching a VkResizer object to a widget, you must call the VkResizer object's VkResizer::show() function to display its geometry controls:

void show()

You can hide the geometry controls by calling the VkResizer object's VkResizer::hide() function:

void hide()

The VkResizer::shown() function returns a Boolean value indicating whether the VkResizer object is visible and displaying its geometry controls:

Boolean shown()

Resizer utility functions

You can configure the VkResizer object's geometry manipulations with the VkResizer::setIncrements() function:

void setIncrements(int resizeWidth, int resizeHeight,
int moveX, int moveY)

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.

ObjectPak callbacks associated with the resizer

The VkResizer class also provides a ObjectPak member function callback named VkResizer::stateChangedCallback:

static const char *const 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.

Management Classes for Controlling Component and Widget Operation

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.

Supporting "Ganged" Scrollbar Operation

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.

Ganged scrollbar group constructor and destructor

The VkGangedGroup constructor does not take any arguments:

VkGangedGroup()

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.

Adding scales and scrollbars to a ganged group

Use the VkGangedGroup::add() function to add widgets or components to a VkGangedGroup object:

virtual void add(Widget w)
virtual void add(VkComponent *obj)

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.


Removing scales and scrollbars from a ganged group

You can remove widgets or components from a VkGangedGroup object with the remove() function inherited from VkWidgetList:

virtual void remove(Widget w)
virtual void remove(VkComponent *obj)

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:

virtual void removeFirst()
virtual void removeLast()

Enforcing Radio-Style Behavior on Toggle Buttons

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.


Radio group constructor and destructor

The VkRadioGroup constructor does not take any arguments:

VkGangedGroup()

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.

Adding Toggles and buttons to a radio group

Use the VkRadioGroup::add() function to add widgets or components to a VkRadioGroup object:

virtual void add(Widget w)
virtual void add(VkComponent *obj)

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.


Removing toggles and buttons from a radio group

You can remove widgets or components from a VkRadioGroup object with the remove() function inherited from VkWidgetList:

virtual void remove(Widget w)
virtual void remove(VkComponent *obj)

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:

virtual void removeFirst()
virtual void removeLast()

Deriving radio group subclasses

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:

virtual void valueChanged (Widget w, XtPointer callData)

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.

Modified Text Attachment

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.


Modified text attachment constructor and destructor

The VkModifiedAttachment constructor accepts three Boolean values:

VkModifiedAttachment(Boolean blankIsValue = FALSE,
Boolean autoAdjust = TRUE,
Boolean incrementalChange = FALSE)

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.

Attaching and detaching modified text attachment to and from a widget

Once you have created a VkModifiedAttachment object, use the VkModifiedAttachment::attach() function to attach it to an existing widget:

void attach(Widget w)

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:

void detach()

Displaying and hiding modified text attachment

Once you have attached a VkModifiedAttachment object to a text widget, you must call VkModifiedAttachment::show() to display the attachment:

void show()

You can hide a VkModifiedAttachment object by calling VkModifiedAttachment::hide():

void 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:

void expose()

expose() is called whenever the dogear widget receives an Expose event. Normally, you should not need to call this function.

Retrieving current and previous values of text widget

You can retrieve the current and previous values of the text widget with value() and previousValue() respectively:

char *value()
char *previousValue()

Note: Do not change or delete the character strings returned by value() and previousValue().


Detecting changes in text widget

The VkModifiedAttachment class provides an ObjectPak member function callback named VkModifiedAttachment::modifiedCallback:

static const char *const 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:

typedef struct {
VkModifiedReason reason;
class VkModified *obj;
XEvent *event;
} VkModifiedCallback

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.


Controlling the contents of the text widget

You can programmatically set the new current value of a VkModifiedAttachment object with VkModifiedAttachment::setValue():

virtual void setValue(const char *value)

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:

virtual void toggleDisplay()

To determine which value the text widget is displaying, call VkModifiedAttachment::latestDisplay():

Boolean 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()

void 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).

Adjusting the modified text attachment's geometry

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:

void adjustGeometry()

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:

virtual void setParameters(Dimension width, Dimension height)

To retrieve the current width and height of the dogear, call VkModifiedAttachment::getParameters():

void getParameters(Dimension *width, Dimension *height)

Other modified text attachment utility and access functions

The VkModifiedAttachment class provides several additional utility and access functions:

  • VkModifiedAttachment::fixPreviousValue() allows you to specify a fixed value to use as the attachment's previous value. After setting a fixed previous value, the attachment does not update the previous value; this provides a "default" value that the user can always toggle to and use.

    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)

  • VkModifiedAttachment::widget() returns the text widget to which the VkModifiedAttachment object is currently attached.
    Widget widget()

  • VkModifiedAttachment::modified() returns TRUE if the current value and the previous value are equal and FALSE if they are not equal.
    Boolean modified()

  • VkModifiedAttachment::setModified() forces the value of the object's modified flag. If you set the value to TRUE, the VkModifiedAttachment object displays its dogear; otherwise, it hides its dogear.
    virtual void setModified(Boolean value)

X Resources associated with modified text attachment

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.

Managing Top-Level Windows

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.

Main 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)

Access function

The access function VkApp::mainWindow() returns a pointer to the VkSimpleWindow (or subclass) object installed as the application's main window:

VkSimpleWindow *mainWindow() const

Functions supported by VkApp class

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.

Specifying iconified state upon startup

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.

Manipulating Windows

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.

Menu Bar Support

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.

Installing a menu bar

To install a menu bar, use setMenuBar():

void setMenuBar(VkMenuBar *menuObj)
void setMenuBar(VkMenuDesc *menudesc)

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

Adding a menu pane to the menu bar

To add a menu pane to the menu bar, use addMenuPane():

VkSubMenu *addMenuPane(const char *name)
VkSubMenu *addMenuPane(const char *name, VkMenuDesc *menudesc)

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.

Adding a menu pane to enforce radio behavior

You can add a menu pane that enforces radio behavior on the toggle items it contains using addRadioMenuPane():

VkRadioSubMenu *addRadioMenuPane(const char *name)
VkRadioSubMenu *addRadioMenuPane(const char *name,
VkMenuDesc *menudesc)

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.

Documentation Type: 

Miscellaneous Display

Overview

This chapter includes the following sections:

Inheritance graph

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

N

O

ObjectPak Support for Building Help

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.

Overview of ViewKit ObjectPak Preference Dialogs

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.

ViewKit ObjectPak Preference Dialog Class

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.

VkPrefDialog

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.

ViewKit ObjectPak Preference Item Classes

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.

Control

The following table describes the preference items that implement controls:

 

 

Preference Item

Description

VkPrefText

A text field.

VkPrefToggle

A single toggle button (you can group multiple toggle buttons into a VkPrefRadio item to enforce radio-style behavior of the buttons).

VkPrefOption

An option menu.

 

 

Ornamental

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.

 

 

Groups

The following table describes preference items that create groups of items:

 

 

Preference Item

Description

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.

VkPrefRadio

A subclass of VkPrefGroup for managing a group of toggle items in a radio box style. You can specify either vertical or horizontal layout; the default is vertical. Items are always padded so that they take equal space. You have the option of displaying a label for the group.

VkPrefList

Defines a group of related items. The VkPrefList class arranges its items vertically. Unlike VkPrefGroup, items are not padded so that they take equal space; instead, each item takes only as much space as it needs. Also in contrast to VkPrefGroup, VkPrefList does not display any label for the group.

 

 

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.

Example 37. Creating a ViewKit Preference Dialog

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

Code

/////////////////////
// DocPrefDialog.h
/////////////////////
#include <Vk/VkPrefDialog.h>
#include <Vk/VkPrefItem.h>
class DocPrefDialog: public VkPrefDialog {
protected:
VkPrefLabel *dialogName;
VkPrefSeparator *sep1;
VkPrefGroup *numberGroup;
VkPrefSeparator *sep2;
VkPrefRadio *paginationGroup;
VkPrefGroup *textGroup;
VkPrefGroup *horizGroup;
VkPrefList *docList;
static String _defaultResources[];
virtual Widget createDialog(Widget parent);
public:
VkPrefText *firstPageNumber;
VkPrefOption *firstPageSide;
VkPrefToggle *paginSingleSide;
VkPrefToggle *paginDoubleSide;
VkPrefToggle *textQuotes;
VkPrefToggle *textSpaces;
DocPrefDialog ( const char *name );
~DocPrefDialog();
virtual const char* className();
};
///////////////////////
// DocPrefDialog.C
///////////////////////
String DocPrefDialog::_defaultResources[] = {
"*dialogNameBase.labelString: Document Properties",
"*numberGroupLabel.labelString: Numbering:",
"*firstPageNumberLabel.labelString: 1st Page #:",
"*firstPageSideLabel.labelString: 1st Page:",
"*firstPageRight: Right",
"*firstPageLeft: Left",
"*paginationGroupLabel.labelString: Pagination:",
"*paginSingleSideBase.labelString: Single-sided",
"*paginDoubleSideBase.labelString: Double-sided",
"*textGroupLabel.labelString: Text:",
"*textQuotesBase.labelString: Smart Quotes",
"*textSpacesBase.labelString: Smart Spaces",
NULL
};
DocPrefDialog::DocPrefDialog (const char *name) : VkPrefDialog (name)
{
// Empty
}
Widget DocPrefDialog::createDialog(Widget parent) {
setDefaultResources(parent, _defaultResources);
dialogName = new VkPrefLabel("dialogName");
sep1 = new VkPrefSeparator("sep1");
firstPageNumber = new VkPrefText("firstPageNumber");
firstPageSide = new VkPrefOption("firstPageSide", 2);
firstPageSide->setLabel(0, "firstPageRight");
firstPageSide->setLabel(1, "firstPageLeft");
numberGroup = new VkPrefGroup("numberGroup");
numberGroup->addItem(firstPageNumber);
numberGroup->addItem(firstPageSide);
sep2 = new VkPrefSeparator("sep2");
paginSingleSide = new VkPrefToggle("paginSingleSide");
paginDoubleSide = new VkPrefToggle("paginDoubleSide");
paginationGroup = new VkPrefRadio("paginationGroup");
paginationGroup->addItem(paginSingleSide);
paginationGroup->addItem(paginDoubleSide);
textQuotes = new VkPrefToggle("textQuotes");
textSpaces = new VkPrefToggle("textSpaces");
textGroup = new VkPrefGroup("textGroup");
textGroup->addItem(textQuotes);
textGroup->addItem(textSpaces);
horizGroup = new VkPrefGroup("horizGroup", TRUE, TRUE);
horizGroup->addItem(paginationGroup);
horizGroup->addItem(textGroup);
docList = new VkPrefList("docList");
docList->addItem(dialogName);
docList->addItem(sep1);
docList->addItem(numberGroup);
docList->addItem(sep2);
docList->addItem(horizGroup);
setItem(docList);
Widget base = VkPrefDialog::createDialog(parent);
return(base);
}
DocPrefDialog::~DocPrefDialog()
{
delete firstPageNumber;
delete firstPageSide;
delete paginSingleSide;
delete paginDoubleSide;
delete textQuotes;
delete textSpaces;
delete dialogName;
delete sep1;
delete numberGroup;
delete sep2;
delete paginationGroup;
delete textGroup;
delete horizGroup;
delete docList;
}
const char* DocPrefDialog::className()
{
return "DocPrefDialog";
}

To post this dialog, create an instance of the DocPrefDialog class and use one of the post() functions described in "Posting Dialogs" . For example:

DocPrefDialog *docPref = new DocPrefDialog("docPref");
// ...
docPref->post();

Retrieve the value of a preference item with the getValue() function, as described in "Obtaining and Setting Preference Item Values" . For example:

Boolean smartSpaces;
// ...
smartSpaces = docPref->textSpaces->getValue();

Overview of ViewKit ObjectPak's ToolTalk Support

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.

ViewKit ObjectPak Classes Supporting ToolTalk

VkMsgClient class

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.

VkMsgApp class

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.

VkMsgWindow class

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.

VkMsgComponent
class

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.

ViewKit ObjectPak Message Facility Policies

The ObjectPak message facility provides mostly a mechanism for exchanging ToolTalk messages between applications, but it does impose some policies:

  • Messages are always sent to all members of the session.
  • Message actions receive all messages for a given operator, and cannot set patterns based on argument number or type.
  • By default, applications connect to the default session when started.

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.

Overview of the VkApp Class

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.

Initialization and event-
handling

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.

Support for application-
level tasks

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.

Information storage

The VkApp class also stores some essential information that can be accessed throughout an application. This information includes:

  • A pointer to the X Display structure associated with the application's connection to the server
  • The XtAppContext structure required by many Xt functions
  • The application's name
  • The application's class name

This information is maintained in the private portion of the class and is available through public access functions.

P

Preference Dialogs

Overview

This chapter includes the following sections:

Inheritance graph

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

Prerequisites

 

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:

  • Young, Douglas A. Object-Oriented Programming with C++ and OSF/Motif. Englewood Cliffs, New Jersey: Prentice Hall, Inc., 1992.

For information on OSF/Motif, see:

  • Open Software Foundation. OSF/Motif Programmer's Guide, Revision 1.2. Englewood Cliffs, New Jersey: Prentice Hall, Inc., 1992.
  • Open Software Foundation. OSF/Motif Programmer's Reference, Revision 1.2. Englewood Cliffs, New Jersey: Prentice Hall, Inc., 1992.
  • Open Software Foundation. OSF/Motif Style Guide, Revision 1.2. Englewood Cliffs, New Jersey: Prentice Hall, Inc., 1992.
  • Heller, Dan. Motif Programming Manual (X Window System Series: Volume Six). Sebastopol, California: O'Reilly & Associates, Inc., 1992.

For comprehensive information on the X Window SystemTM, Xlib, and Xt, see:

  • Nye, Adrian. Xlib Programming Manual (X Window System Series: Volume One). Sebastopol, California: O'Reilly & Associates, Inc., 1992.
  • O'Reilly & Associates, Inc. Xlib Reference Manual (X Window System Series: Volume Two). Sebastopol, California: O'Reilly & Associates, Inc., 1992.
  • Nye, Adrian, and Tim O'Reilly. X Toolkit Intrinsics Programming Manual (X Window System Series: Volume Four). Sebastopol, California: O'Reilly & Associates, Inc., 1992.
  • O'Reilly & Associates, Inc. X Toolkit Intrinsics Reference Manual (X Window System Series: Volume Five). Sebastopol, California: O'Reilly & Associates, Inc., 1992.
  •  

Documentation Type: 

Q

R

radio check box component
example
radio submenus
adding to menus
radio-style toggles
adding buttons
removing buttons
raise() (in VkApp)
raise() (in VkSimpleWindow)
raising windows [2]
raw events [2]
Realign button (in VkGraph control panel)
receiving messages
reference pages
conventions
registering functions, ViewKit callbacks
caution
example
function format [2]
registerPattern() (in VkMsgClient)
relayButton() (in VkGraph)
remove() (in VkAlignmentGroup)
remove() (in VkGangedGroup)
remove() (in VkGraph)
remove() (in VkMenuItem)
remove() (in VkRadioGroup)
removeAction() (in VkMsgClient)
removeAllCallbacks() (in VkCallbackObject)
removeCallback() (in VkCallbackObject)
removeDestroyHandler() (in VkComponent)
removeFirst() (in VkGangedGroup)
removeFirst() (in VkRadioGroup)
removeItem() (in VkMenu)
removeLast() (in VkGangedGroup)
removeLast() (in VkRadioGroup)
removeTab() (in VkTabPanel)
removing
buttons from radio group
functions, ViewKit callbacks
menu items [2]
nodes from graphs
pixmaps from tabs
scrollbars from a ganged group
tabs to tab panel
widgets from alignment group
reorientButton() (in VkGraph)
repeat buttons
activation, responding
repeating buttons
X resources
replace() (in VkMenu)
replacing
completion field expansion list
menu items
requirements
header files
libraries
packages
reset() (in VkMenuUndoManager)
reset() (in VkMeter)
resize() (in VkDoubleBuffer)
resizers
attaching widgets
detaching widgets
displaying geometry controls
geometry changes
detecting
restricting
hiding geometry controls
overview
resizing
double-buffer component
widgets
resource support
components
data members, initializing
default values, setting
global values, setting
requirements
resource values, setting
widget resources, note
retrieving values
example
note
Rotate Graph button (in VkGraph control panel)
run() (in VkApp)

Radio Check Box Component

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.

Example 42. Creating a Radio Box

Code

#include <Vk/VkApp.h>
#include <Vk/VkSimpleWindow.h>
#include <Vk/VkRadioBox.h>
class RadioBoxWindow: public VkSimpleWindow {
protected:
virtual Widget setUpInterface ( Widget parent );
static String _defaultResources[];
public:
RadioBoxWindow ( const char *name ) : VkSimpleWindow ( name ) { }
~RadioBoxWindow();
virtual const char* className();
};
RadioBoxWindow:: ~RadioBoxWindow()
{ }
const char* RadioBoxWindow::className() { return "RadioBoxWindow"; }
String RadioBoxWindow::_defaultResources[] = {
"*radio*label.labelString: Select one:",
"*radio*one*labelString: First choice",
"*radio*two*labelString: Second choice",
"*radio*three*labelString: Third choice",
"*radio*four*labelString: Fourth choice",
NULL
};
Widget RadioBoxWindow::setUpInterface ( Widget parent )
{
setDefaultResources(parent, _defaultResources);
VkRadioBox *rb = new VkRadioBox("radio", parent);
rb->addItem("one");
rb->addItem("two");
rb->addItem("three");
rb->addItem("four");
rb->show();
return rb->baseWidget();
}
void main ( int argc, char **argv )
{
VkApp *rbApp = new VkApp("radioBoxApp", &argc, argv);
RadioBoxWindow *rbWin = new RadioBoxWindow("radiobox");
rbWin->show();
rbApp->run();
}

Registering Services for Autostart

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:

ptype USR_MY_APP
{
start "/usr/bin/X11/myApp";
observe: session load_file() => start;
}

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:

VkMsgApp *myApp = new VkMsgApp("MyApp", &argc, argv, NULL, 0,
"USR_MY_APP");

Finally, when you install your application, register this information in the static ToolTalk config file by executing:

${ToolTalkBin}/tt_type_comp -dsystem myapp.ptype
kill -USR2 ${TTSessionPid}

This adds the contents of myapp.ptype to the system config file, and tells all existing ttsession processes to update their configurations.

Registering an action without the pattern

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().

Repeating Button Component

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.

Repeating Button Constructor

The VkRepeatButton constructor takes three arguments:

VkRepeatButton(char *name, Widget parent,
VkRepeatButtonType type)

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.

Responding to Repeat Button Activation

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.

Repeating Button Utility and Access Functions

The VkRepeatButton::setParameters() function changes the delay parameters for the button:

void setParameters(long initial, long repeat)

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:

VkRepeatButtonType type()

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.

X Resources Associated with Repeating Button

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)

Running ViewKit ObjectPak Applications

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.

Example 11. Using VkApp

Code

#include <Vk/VkApp.h>
// Application-specific setup
void main ( int argc, char **argv )
{
VkApp *myApp = new VkApp("MyApp", &argc, argv);
// Application-specific code
myApp->run(); // Run the application
}

S

saveToFile() (in VkGraph)
saving
graphs
ScrolledWindow widget and windows
secondary event loops
during handlePendingEvents()
during postAndWait()
during sendSyncRequest()
during wasInterrupted()
Selected Nodes menu (in VkGraph)
selectedTab() (in VkTabPanel)
selecting
nodes in graphs
selectTab() (in VkTabPanel) [2]
sendFileNotice() (in VkMsgClient)
sendFileRequest() (in VkMsgClient)
sending messages
sendIntFileNotice() (in VkMsgClient)
sendIntNotice() (in VkMsgClient)
sendNotice() (in VkMsgClient)
sendRequest() (in VkMsgClient)
sendStringFileNotice() (in VkMsgClient)
sendStringNotice() (in VkMsgClient)
sendSyncFileRequest() (in VkMsgClient)
sendSyncRequest() (in VkMsgClient)
set() (in VkOptionMenu)
setAboutDialog() (in VkApp)
setBaseHeight() (in VkPrefItem)
setBusyCursor() (in VkApp) [2]
setBusyDialog() (in VkApp)
setButtonLabels() (in VkDialogManager)
setClassHint() (in VkSimpleWindow)
setDefaultResources() (in VkComponent)
setDirectory() (in VkFileSelectionDialog)
setFilterPattern() (in VkFileSelectionDialog)
setIconName() (in VkSimpleWindow)
setIncrements() (in VkResizer)
setItem() (in VkPrefDialog)
setLabel() (in VkMenuItem)
setLabel() (in VkPrefOption)
setLabelHeight() (in VkPrefItem)
setLayoutStyle() (in VkGraph)
setMainWindow() (in VkApp)
setMargin() (in VkTickMarks)
setMenuBar() (in VkWindow)
setModified() (in VkModifiedAttachment)
setNormalCursor() (in VkApp)
setParameters() (in VkModifiedAttachment)
setParameters() (in VkRepeatButton)
setPosition() (in VkMenuItem)
setResizePolicy() (in VkMeter)
setScale() (in VkTickMarks)
setSelection() (in VkFileSelectionDialog)
setSize() (in VkGraph)
setSize() (in VkPrefOption)
setSortFunction() (in VkNode)
setStateAndNotify() (in VkMenuToggle)
setTabPixmap() (in VkTabPanel)
setting
check box labels
check box toggle values
command class labels
completion field expansion list
default resource values
example
note
dialog button labels
dialog titles
global resource values
preference items
labels
labels, group
labels, label items
labels, option menus [2]
labels, toggles
values
tick marks scale
VkAction class label for "Undo" selection
setTitle() (in VkDialogManager)
setTitle() (in VkSimpleWindow)
setUpInterface() (in VkSimpleWindow)
setUpWindowProperties() (in VkSimpleWindow)
setValue() (in VkCheckBox)
setValue() (in VkModifiedAttachment)
setValue() (in VkPrefItem)
setValue() (in VkPrefOption)
setValue() (in VkPrefText)
setValue() (in VkPrefToggle)
setValues() (in VkCheckBox)
setVersionString() (in VkApp)
setVisualState() (in VkMenuToggle)
setZoomOption() (in VkGraph)
SgGraph widget
SGIHelpIndexMsg()
SGIHelpInit()
SGIHelpMsg()
shell geometry
main window [2]
shell resources [2]
shell, application [2] [3]
geometry
shellGeometry() (in VkApp)
show() (in VkApp)
show() (in VkComponent)
show() (in VkMenuItem)
show() (in VkModifiedAttachment)
show() (in VkPopupMenu)
show() (in VkResizer)
show() (in VkSimpleWindow) [2]
showCursor() (in VkApp)
shown() (in VkResizer)
showOverview() (in VkGraph)
showTearOff() (in VkSubMenu)
size() (in VkPrefGroup)
size() (in VkPrefOption)
size() (in VkTabPanel)
sortAll() (in VkGraph)
sortChildren() (in VkNode)
startupIconified() (in VkApp) [2]
stateChanged() (in VkSimpleWindow)
stateChangedCallback (in VkResizer)
static member functions
Xt callbacks [2]
example
naming convention
static menu descriptions
this pointer
subclassing components
submenus
adding to menus
radio-style
tear-off behavior
[] (subscript) operator (in VkMenu)
suggested reading

Sending and Receiving ToolTalk Messages

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.

Sending Notices and Requests

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.

Sending simple notices

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:

void sendStringNotice(char *op ...)
void sendIntNotice(char *op ...)

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.


Composing and sending messages

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():

void composeAdd(char *val, VkMsgMode mode = VK_MSG_IN)
void composeAdd(int ival, VkMsgMode mode = VK_MSG_IN)
void composeAdd(unsigned char *val, int len,
VkMsgMode mode = VK_MSG_IN)

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.

Notice messages

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).

Sending a request

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.


Sending synchronous requests

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.

Specifying a filename message attribute

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:

void sendStringFileNotice(char *op, char *file ...)
void sendIntFileNotice(char *op, char *file ...)
void sendFileNotice(char *op, char *file)
void sendFileRequest(char *op, char *file)
void sendSyncFileRequest(char *op, char *file)

In these functions, file is the filename.

Receiving Notices and Handling Requests

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.

Overview of message dispatch

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.

Example

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.

Writing message action callbacks

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:

typedef Boolean (*VkMsgClientAction)(
void* /* clientData */,
VkMsgFacilityReason /* reason */,
VkMsgMessage /* msg_in */,
char* /* op */,
int /* argc */,
VkMsgArg* /* argv */
)

Note: The VkMsgClientAction function must be a regular function or a static member function; it cannot be a regular class member function.


Callback arguments

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:

typedef struct {
char *type;
VkMsgValue value;
VkMsgMode mode;
} VkMsgArg

Structure elements

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.


Callbacks that process notices

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.

Callbacks that process requests

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:

  1. Read any required data from the message's "in" or "in/out" arguments.
  2. Perform any appropriate actions.
  3. Modify any "out" or "in/out" arguments.
  4. Send the reply message using VkMsgReply():
    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.

Useful functions when handling messages

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:

Boolean VkMsgTypeIsInt(char *atype)
Boolean VkMsgTypeIsString(char *atype)
Boolean VkMsgTypeIsBString(char *atype)

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:

VkMsgSetIVal(VkMsgMessage msg, int index, int value)
VkMsgSetVal(VkMsgMessage msg, int index, char *value)
VkMsgSetBVal(VkMsgMessage msg, int index,
unsigned char *value, int len)

The header file <Vk/VkMsg.h> contains these declarations.

Parsing a messages arugments

To parse a message's arguments into a VkMsgArg structure (as described in "Writing message action callbacks" ), use the VkMsgParseArguments() function:

void VkMsgParseArguments(VkMsgMessage msg, int *argc_return, VkMsgArg **argv_return)

This function is declared in the header file <Vk/VkMsgUtils.h>. You must free the argv result when done.

Retrieving a file attribute

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.

Creating and registering simple message patterns

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:

VkMsgPattern addAction(char *op, VkMsgClientAction proc,
void *clientData, VkMsgActionType type,
Boolean deleteMessage = TRUE)

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.

VkMsgActionType
values

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.

Creating more detailed message patterns

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:

VkMsgPattern createAction(char *op, VkMsgClientAction proc,
void *clientData, VkMsgActionType type,
Boolean deleteMessage = TRUE)

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.

Example

As an example of creating a detailed message pattern, consider the following code sample:

pat = createAction("message_op", callback, this, VK_MSG_ACTION_HANDLE);
VkMsgPatternArg(pat, VK_MSG_IN, VK_MSG_INT_MSG_ARG_TYPE, NULL)
VkMsgPatternArg(pat, VK_MSG_OUT, VK_MSG_ALL_MSG_ARG_TYPE, NULL);
VkMsgPatternIArg(pat, VK_MSG_IN, VK_MSG_INT_MSG_ARG_TYPE, 5);
registerPattern(pat);
updatePatterns();

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.

Detecting and Handling Errors in Handling Requests

Error types

The two kinds of errors that can occur when a request is made and a reply is expected are as follows:

  • Either no one handles the request (and ToolTalk could not autostart an appropriate service)
  • Someone does handle the request and replies, but some error occurs while handling the request.

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.

Setting Application Cursors

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.

Setting and Retrieving the Normal Cursor

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()

Setting and Retrieving the Busy Cursor

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.

Setting and retrieving a fixed busy 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()

Creating, setting, and retrieving an animated busy cursor

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.

Example 12. Creating an Animated Busy Cursor

Code

#include <Vk/VkApp.h>
#include <Vk/VkResource.h>
#include <Vk/VkCursorList.h>
// Define an array of bit patterns that represent each frame of the cursor
// animation.
#define NUMCURSORS 8
static char time_bits[NUMCURSORS][32*32] = {
{
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x8c, 0x00, 0x00, 0x31, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x8c, 0xfe, 0x7f, 0x31, 0x0c, 0xfd, 0xbf, 0x30, 0x0c, 0xfa, 0x5f, 0x30,
0x0c, 0xe4, 0x27, 0x30, 0x0c, 0x98, 0x19, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x80, 0x01, 0x30, 0x0c, 0x80, 0x01, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x18, 0x18, 0x30, 0x0c, 0x04, 0x20, 0x30, 0x0c, 0x02, 0x40, 0x30,
0x0c, 0x01, 0x80, 0x30, 0x8c, 0x00, 0x00, 0x31, 0x4c, 0x00, 0x00, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x8c, 0x00, 0x00, 0x31, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00},
{
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x8c, 0x00, 0x00, 0x31, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x03, 0xc0, 0x32,
0x4c, 0x3f, 0xfc, 0x32, 0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x8c, 0xfe, 0x7f, 0x31, 0x0c, 0xfd, 0xbf, 0x30, 0x0c, 0xfa, 0x5f, 0x30,
0x0c, 0xe4, 0x27, 0x30, 0x0c, 0x98, 0x19, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x80, 0x01, 0x30, 0x0c, 0x80, 0x01, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x18, 0x19, 0x30, 0x0c, 0x84, 0x20, 0x30, 0x0c, 0x02, 0x41, 0x30,
0x0c, 0x81, 0x80, 0x30, 0x8c, 0x00, 0x01, 0x31, 0x4c, 0x80, 0x00, 0x32,
0x4c, 0x00, 0x01, 0x32, 0x4c, 0xfc, 0x3f, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x8c, 0x00, 0x00, 0x31, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00},
{
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x8c, 0x00, 0x00, 0x31, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32,
0x4c, 0x03, 0xc0, 0x32, 0x4c, 0x1f, 0xf8, 0x32, 0x4c, 0x7f, 0xfe, 0x32,
0x8c, 0xfe, 0x7f, 0x31, 0x0c, 0xfd, 0xbf, 0x30, 0x0c, 0xfa, 0x5f, 0x30,
0x0c, 0xe4, 0x27, 0x30, 0x0c, 0x98, 0x19, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x80, 0x01, 0x30, 0x0c, 0x80, 0x01, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x18, 0x19, 0x30, 0x0c, 0x84, 0x20, 0x30, 0x0c, 0x02, 0x41, 0x30,
0x0c, 0x81, 0x80, 0x30, 0x8c, 0x00, 0x01, 0x31, 0x4c, 0xc0, 0x07, 0x32,
0x4c, 0xfc, 0x3f, 0x32, 0x4c, 0xfe, 0x7f, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x8c, 0x00, 0x00, 0x31, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00},
{
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x8c, 0x00, 0x00, 0x31, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x4c, 0x03, 0xc0, 0x32, 0x4c, 0x0f, 0xf0, 0x32,
0x8c, 0x3e, 0x7c, 0x31, 0x0c, 0xfd, 0xbf, 0x30, 0x0c, 0xfa, 0x5f, 0x30,
0x0c, 0xe4, 0x27, 0x30, 0x0c, 0x98, 0x19, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x80, 0x01, 0x30, 0x0c, 0x80, 0x01, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x18, 0x19, 0x30, 0x0c, 0x84, 0x20, 0x30, 0x0c, 0x02, 0x41, 0x30,
0x0c, 0x81, 0x80, 0x30, 0x8c, 0xe0, 0x07, 0x31, 0x4c, 0xfc, 0x3f, 0x32,
0x4c, 0xfe, 0x7f, 0x32, 0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x8c, 0x00, 0x00, 0x31, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00},
{
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x8c, 0x00, 0x00, 0x31, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x03, 0xc0, 0x32,
0x8c, 0x06, 0x60, 0x31, 0x0c, 0x1d, 0xb8, 0x30, 0x0c, 0x7a, 0x5e, 0x30,
0x0c, 0xe4, 0x27, 0x30, 0x0c, 0x98, 0x19, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x80, 0x01, 0x30, 0x0c, 0x80, 0x01, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x18, 0x19, 0x30, 0x0c, 0x84, 0x20, 0x30, 0x0c, 0x82, 0x41, 0x30,
0x0c, 0xf1, 0x8f, 0x30, 0x8c, 0xfc, 0x3f, 0x31, 0x4c, 0xfe, 0x7f, 0x32,
0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x8c, 0x00, 0x00, 0x31, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00},
{
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x8c, 0x00, 0x00, 0x31, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32,
0x8c, 0x02, 0x40, 0x31, 0x0c, 0x05, 0xa0, 0x30, 0x0c, 0x1a, 0x58, 0x30,
0x0c, 0x64, 0x26, 0x30, 0x0c, 0x98, 0x19, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x80, 0x01, 0x30, 0x0c, 0x80, 0x01, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x18, 0x19, 0x30, 0x0c, 0x84, 0x20, 0x30, 0x0c, 0xe2, 0x47, 0x30,
0x0c, 0xf9, 0x9f, 0x30, 0x8c, 0xfe, 0x7f, 0x31, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x8c, 0x00, 0x00, 0x31, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00},
{
0x00, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x8c, 0xff, 0xff, 0x31, 0xcc, 0xff, 0xff, 0x33, 0x4c, 0x00, 0x00, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32, 0x4c, 0x00, 0x00, 0x32,
0x8c, 0x00, 0x00, 0x31, 0x0c, 0x01, 0x80, 0x30, 0x0c, 0x02, 0x40, 0x30,
0x0c, 0x04, 0x20, 0x30, 0x0c, 0x18, 0x18, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x80, 0x01, 0x30, 0x0c, 0x80, 0x01, 0x30, 0x0c, 0x60, 0x06, 0x30,
0x0c, 0x98, 0x19, 0x30, 0x0c, 0xe4, 0x27, 0x30, 0x0c, 0xfa, 0x5f, 0x30,
0x0c, 0xfd, 0xbf, 0x30, 0x8c, 0xfe, 0x7f, 0x31, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32, 0x4c, 0xff, 0xff, 0x32,
0x4c, 0x00, 0x00, 0x32, 0x8c, 0x00, 0x00, 0x31, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00},
{
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x60, 0xfe, 0xff, 0xff, 0x7f,
0xfe, 0xff, 0xff, 0x7f, 0x06, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x60,
0xf6, 0x01, 0x80, 0x6f, 0x0e, 0x02, 0x40, 0x78, 0xe6, 0x05, 0x20, 0x78,
0xe6, 0x0b, 0x10, 0x78, 0xe6, 0x17, 0x08, 0x78, 0xe6, 0x2f, 0x04, 0x78,
0xe6, 0x2f, 0x04, 0x78, 0xe6, 0x5f, 0x02, 0x78, 0xe6, 0x5f, 0x02, 0x78,
0xe6, 0xbf, 0x01, 0x78, 0xe6, 0xbf, 0x01, 0x78, 0xe6, 0x5f, 0x02, 0x78,
0xe6, 0x5f, 0x02, 0x78, 0xe6, 0x2f, 0x04, 0x78, 0xe6, 0x2f, 0x04, 0x78,
0xe6, 0x17, 0x08, 0x78, 0xe6, 0x0b, 0x10, 0x78, 0xe6, 0x05, 0x20, 0x78,
0x0e, 0x02, 0x40, 0x78, 0xf6, 0x01, 0x80, 0x6f, 0x06, 0x00, 0x00, 0x60,
0x06, 0x00, 0x00, 0x60, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f,
0x06, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00}
};
// Masks used for this cursor. The last frame requires a different
// mask, but all other frames can use the same mask.
static char time_mask_bits[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x8e, 0xff, 0xff, 0x71, 0xce, 0xff, 0xff, 0x73, 0xce, 0xff, 0xff, 0x73,
0xce, 0xff, 0xff, 0x73, 0xce, 0xff, 0xff, 0x73, 0xce, 0xff, 0xff, 0x73,
0x8e, 0xff, 0xff, 0x71, 0x0e, 0xff, 0xff, 0x70, 0x0e, 0xfe, 0x7f, 0x70,
0x0e, 0xfc, 0x3f, 0x70, 0x0e, 0xf8, 0x1f, 0x70, 0x0e, 0xe0, 0x07, 0x70,
0x0e, 0x80, 0x01, 0x70, 0x0e, 0x80, 0x01, 0x70, 0x0e, 0xe0, 0x07, 0x70,
0x0e, 0xf8, 0x1f, 0x70, 0x0e, 0xfc, 0x3f, 0x70, 0x0e, 0xfe, 0x7f, 0x70,
0x0e, 0xff, 0xff, 0x70, 0x8e, 0xff, 0xff, 0x71, 0xce, 0xff, 0xff, 0x73,
0xce, 0xff, 0xff, 0x73, 0xce, 0xff, 0xff, 0x73, 0xce, 0xff, 0xff, 0x73,
0xce, 0xff, 0xff, 0x73, 0x8e, 0xff, 0xff, 0xf1, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
#define time7_mask_width 32
#define time7_mask_height 32
#define time7_mask_x_hot 15
#define time7_mask_y_hot 15
static char time7_mask_bits[] = {
0x0f, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, 0xe0,
0xf7, 0x01, 0x80, 0xef, 0xff, 0x03, 0xc0, 0xff, 0xff, 0x07, 0xe0, 0xff,
0xff, 0x0f, 0xf0, 0xff, 0xff, 0x1f, 0xf8, 0xff, 0xff, 0x3f, 0xfc, 0xff,
0xff, 0x3f, 0xfc, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0xff,
0xff, 0x7f, 0xfe, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0x3f, 0xfc, 0xff,
0xff, 0x1f, 0xf8, 0xff, 0xff, 0x0f, 0xf0, 0xff, 0xff, 0x07, 0xe0, 0xff,
0xff, 0x03, 0xc0, 0xff, 0xf7, 0x01, 0x80, 0xef, 0x07, 0x00, 0x00, 0xe0,
0x07, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xf0};
/////////////////////////////////////////////////
// Class declaration. Subclass VkCursorList
///////////////////////////////////////////////////
class HourGlassCursors : public VkCursorList {
public:
HourGlassCursors( );
protected:
void createCursor(int index); // Overrides base class' pure virtual
private:
XColor xcolors[2];
};
// The constructor gets two colors to use for the cursor.
HourGlassCursors::HourGlassCursors ( ) : VkCursorList ( NUMCURSORS )
{
xcolors[0].pixel= (Pixel) VkGetResource(theApplication->baseWidget(),
"busyCursorForeground",
XmCForeground,
XmRPixel,
(char *) "Black");
xcolors[1].pixel= (Pixel) VkGetResource(theApplication->baseWidget(),
"busyCursorBackground",
XmCBackground,
XmRPixel,
char *) "White");
XQueryColors (theApplication->display(),
DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy)),
xcolors, 2);
}
// This function is called as needed, to create a new cursor frame.
// Just create the cursor corresponding to the requested index and
// install it in _cursorList.
void HourGlassCursors::createCursor(int index)
{
Pixmap pixmap = 0, maskPixmap = 0;
Display *dpy = theApplication->display();
pixmap = XCreateBitmapFromData (dpy,
DefaultRootWindow(dpy),
time_bits[index],
32, 32);
if(index == 7)
maskPixmap = XCreateBitmapFromData (dpy,
DefaultRootWindow(dpy),
time7_mask_bits,
32, 32);
else
maskPixmap = XCreateBitmapFromData (dpy,
DefaultRootWindow(dpy),
time_mask_bits,
32, 32);
_cursorList[index] = XCreatePixmapCursor ( dpy, pixmap, maskPixmap,
&(xcolors[0]), &(xcolors[1]),
0, 0);
if(pixmap)
XFreePixmap (dpy, pixmap);
if(maskPixmap)
XFreePixmap (dpy, maskPixmap);
}

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.

Setting and Retrieving a Temporary 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().

Supporting Busy States

This section describes ObjectPak's support for busy states. A busy state is when you lock out user input during an operation.

Entering and Exiting Busy States Using ViewKit ObjectPak

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:

virtual void busy(char *msg = NULL,
VkSimpleWindow window = NULL)

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.

Example 13. Using Busy States in a ViewKit ObjectPak Application

Code

class ReportWindow: public VkSimpleWindow {
public:
ReportWindow ( const char *name );
~ReportWindow();
virtual const char* className();
void report();
void sort();
private:
static String _defaultResources[];
};
String _defaultResources[] = {
"*sortDialogMsg: Sorting records...",
"*reportDialogMsg: Generating report...",
NULL
};
ReportWindow::ReportWindow(const char *name) : VkSimpleWindow ( name )
{
setDefaultResources(theApplication->baseWidget(), _defaultResources);
// Create window...
}
void ReportWindow::sort()
{
theApplication->busy("sortDialogMsg");
// Sort records...
theApplication->notBusy();
}
void ReportWindow::report()
{
theApplication->busy("reportDialogMsg");
// Report generation...
sort();
// Report generation continued...
theApplication->notBusy();
}

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().


Animating the Busy Cursor

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.

Example 14. Animating the Busy Cursor

Code

int i;
// Start being "busy"
theApplication->busy("Busy", (BusyWindow *) clientData);
for(i=0; i<100; i++)
{
// Every so often, update the busy cursor
theApplication->progressing();
sleep(1);
}
// Task done, so we"re not busy anymore
theApplication->notBusy();

Installing Different Busy Dialogs

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.

Example 15. Temporarily Installing an Interruptible Busy Dialog

#inlcude <Vk/VkApp.h>
#include <Vk/VkInterruptDialog.h>
// ...
// Install theInterruptDialog as the busy dialog
theApplication->setBusyDialog(theInterruptDialog);
theApplication->busy("Generating report"); // Enter busy state
// Perform task...
theApplication->notBusy(); // Exit busy state
theApplication->setBusyDialog(NULL); // Install default busy dialog

Documentation Type: 

Supporting Messaging in Application Windows

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.

Supporting Messaging in Components

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.

Symbols

<F1> key (Help) [2]
[] (subscript) operator (in VkMenu)
_allowMultipleDialogs (in VkGenericDialog)
_baseWidget (in VkComponent) [2] [3]
_baseWidget (in VkSimpleWindow)
_canvas (in VkDoubleBuffer)
_clientData() (in VkMenuActionObject)
_currentMatchList (in VkCompletionField)
_cursorList (in VkCursorList)
_height (in VkDoubleBuffer)
_iconState (in VkSimpleWindow)
_label (in VkCheckBox)
_label (in VkNode)
_mainWindowWidget (in VkSimpleWindow)
_minimizeMultipleDialogs (in VkGenericDialog)
_name (in VkComponent) [2] [3]
_nameList (in VkCompletionField)
_rc (in VkCheckBox)
_showApply (in VkGenericDialog)
_showCancel (in VkGenericDialog)
_showOK (in VkGenericDialog)
_stackingState (in VkSimpleWindow)
_visibleState (in VkSimpleWindow)
_widgetList (in VkCheckBox)
_width (in VkDoubleBuffer)
_winList (in VkApp)
"Click for Help" selection (in Help menu)
"Close" selection (in Admin menu)
"Collapse Selected Nodes" (in Selected Nodes menu)
"Collapse Subgraph" selection (in Node menu)
"Expand Selected Nodes" (in Selected Nodes menu)
"helpAuthorMode" resource
"Hide Node" selection (in Node menu)
"Hide Selected Nodes" (in Selected Nodes menu)
"Index" selection (in Help menu)
"Keys & Shortcuts" selection (in Help menu)
"labelString" resource (in VkAction)
"noUndoQuestion" resource (in VkMenuConfirmFirstAction)
"Overview" selection (in Help menu)
"Product Info" selection (in Help menu) [2]
"safe quit" mechanism [2] [3]
"Scale to Fit" selection (in Admin menu)
"Show Arcs" selection (in Admin menu)
"Show Immediate Children" selection (in Node menu)
"Show Parents" selection (in Node menu)
"Undo" menu selection label

T

Tab Panel Component

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

Collapsed tabs

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

Selecting a tab with the popup menu

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.

Tab Panel Constructor

The VkTabPanel constructor initializes the tab panel and allocates all resources required by the component:

VkTabPanel(char* name, Widget parent,
Boolean horizOrientation = TRUE, int tabHeight = 0)

name and parent are the standard component name and parent widget arguments.

Optional 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:

*tabs*tabHeight: 30

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.


Adding Tabs to a Tab Panel

Once you have created a tab panel, you can add a tab to it using VkTabPanel::addTab():

int addTab(char *label, void *clientData, Boolean sorted = FALSE)

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.


Return value of addTab()

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

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.

Adding more than one tab

You can add more than one tab at a time using VkTabPanel::addTabs():

void addTabs(char **labels, void **clientDatas, int numTabs,
Boolean sorted = FALSE)

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.

Removing a Tab from a Tab Panel

To remove a tab from a tab panel, use VkTabPanel::removeTab():

Boolean removeTab(int index)
Boolean removeTab(char *label)

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.


Adding a Pixmap to a Tab

To set or change a pixmap associated with a tab, use VkTabPanel::setTabPixmap():

Boolean setTabPixmap(int index, Pixmap pixmap)
Boolean setTabPixmap(char *label, Pixmap pixmap)

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.


Removing and existing pixmap

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():

Boolean tabPixmap(int index, Pixmap *pixmap_return)
Boolean tabPixmap(char *label, Pixmap *pixmap_return)

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.

Responding to Tab Selection

Selecting a tab

The user can select a tab using either of the following methods:

  1. By clicking on a tab with the left mouse button
  2. By clicking on a group of collapsed tabs with either the left or right mouse button and choosing a menu item corresponding to a tab.

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:

typedef struct {
char *label;
void *clientData;
int tabIndex;
XEvent *event;
} VkTabCallbackStruct

Arguments

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():

Boolean selectTab(int index, XEvent *event = NULL);
Boolean selectTab(char *label, XEvent *event = NULL);

Specifying a tab to use its position index or label

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():

int 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.

Tab Panel Access Functions

VkTabPanel provides several functions for accessing information about a tab panel and its tabs:

  • VkTabPanel::getTab() retrieves information about a specific tab. Specify the position index of the tab with the index argument. getTab() sets the value of the label_return argument to point to the tab's label. Note that if you set the label by specifying a resource name when you added this tab, the value of label_return is the value of the resource you specified. getTab() sets the value of the clientData_return argument to point to the client data you provided when you added the tab.

    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)

  • VkTabPanel::horiz() returns TRUE if the tab component is horizontally oriented, and FALSE if it is vertically oriented.
    Boolean horiz()

  • VkTabPanel::size() returns the number of tabs in the tab panel.
    int size()

  • VkTabPanel::tabHeight() returns the height of the tab display area. This is the maximum display height for pixmaps. Larger pixmaps are truncated, and smaller pixmaps are centered. The height of the tab display area is determined by the following conditions:

Determining height of tab

    1. Value you specify in the VkTabPanel constructor.
    2. Value of the VkTabPanel component's "tabHeight" resource.
    3. Value of the "height" resource of the "tabLabel" widget created by VkTabPanel.
    4. Height of the tab label's font as specified by the "fontList" resource of the "tabLabel" widget created by VkTabPanel.

      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.

Calculating total height of a tab

The total height of a tab, including decoration, is the sum of the following conditions:

    1. The height of tab display area as returned by tabHeight() plus
    2. The tab's top and bottom margin (determined by value of marginHeight resource of the tabLabel widget created by VkTabPanel) plus
    3. The value of the additionalMarginHeight resource of the VkTabPanel component.

      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()

  • VkTabPanel::uniformTabs() returns TRUE if the tabs have a uniform width (or height, if the tab panel is vertical). By default, tabs take on the width necessary to display their label and pixmap. You can force all tabs to take the width of the largest tab in the group by setting the VkTabPanel component's "uniformTabs" resource to TRUE.

    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()

  • VkTabPanel::lineThickness() returns the line thickness used when drawing the tab edges. The line thickness defaults to 1. You can set this value through the "lineThickness" resource of the VkTabPanel component, but a line thickness other than 1 might not render properly.
    int lineThickness()

  • VkTabPanel::tabBg() returns the color used for the background area around the tabs. This color is set by the "background" resource of the VkTabPanel component.
    Pixel tabBg()

  • VkTabPanel::labelFg() returns the color used for tab foregrounds (that is, the tab lettering and the foreground bits if the pixmap you supply is a bitmap). This color is set by the "foreground" resource for the "tabLabel" widget created by VkTabPanel.
    Pixel labelFg()

  • VkTabPanel::labelBg() returns the color used for tab backgrounds. This color is set by the "background" resource for the "tabLabel" widget created by VkTabPanel. When a bitmap is supplied as the pixmap, this color is used for the background bits.
    Pixel labelBg()

  • VkTabPanel::gc() returns X graphics context used for drawing the tabs. This might be useful if you create pixmaps and want to use the same foreground and background colors as the tabs.
    GC gc()

  • VkTabPanel::area1() returns work area widget to the left of the tab display (or top if the tab panel is vertical), and VkTabPanel::area2() returns the work area widget to the right of the tab display (or bottom if the tab panel is vertical). Both work areas are implemented as OSF/Motif Form widgets. By default, work areas are empty. Access work area widgets and implement additional displays as desired.
    Widget area1()

    Widget area2()

X Resources Associated with the Tab Panel Component

Determining component display characteristics

The VkTabPanel class provides X resources that determine the display characteristics of the component The following table lists and describes the available resources:

 

 

X Resource

Description

additionalMarginHeight

Additional height, expressed in pixels, added to margin between top and bottom of tab border and tab display area (default value 2).

additionalMarginWidth

Additional width, expressed in pixels, added to margin between sides of tab border and the tab display area (default value 4).

background

Background color of VkTabPanel component, shown in space around tabs.

endMultiplier

Number of overlapped tab symbols displayed as an "end indicator" when there are more tabs in panel than can be displayed at one time (default value 3).

endSpacing

Space, expressed in pixels, between overlapped tab symbols in the "end indicator" (default value 9).

lineThickness

Line thickness used when drawing the tab edges. Default value is 1. You can provide another value, but line thickness other than 1 might not render properly.

margin

Margin, expressed in pixels, between tab edges and component edge (default value 5).

margin1

Margin, expressed in pixels, between left or top work area widget and tabs (default value 5).

margin2

Margin, expressed in pixels, between right or bottom work area widget and tabs (default value 5).

pixmapSpacing

If tab contains a pixmap, the space, expressed in pixels, between the tab label and the pixmap (default value 3).

selectedTabBackground

Background color of the selected tab.

sideOffset

Amount of tab overlap, expressed in pixels (default value 17).

tabHeight

Height of tab display area determined by the following methods:

1. Value you specify in the VkTabPanel constructor

2. Value of the VkTabPanel component's "tabHeight" resource

3. Value of height" resource of the "tabLabel" widget created by VkTabPanel

4. Height of tab label's font as specified by the "fontList" resource of the "tabLabel" widget created by VkTabPanel.

If you attempt to set tab height via multiple methods, method 1 has highest precedence and method 4 the lowest. Default value of "tabHeight" is 0.

uniformTabs

Determines whether all tabs have the same width. The default value, FALSE, allows tabs to be wide enough to display their label and pixmap. You can force all tabs to take the width of the largest tab in the group by setting this resource to TRUE.

 

 

Determining tabLabel display 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:

 

 

X Resource

Description

tabLabel.background

Color used for tab backgrounds. When a bitmap is supplied as the pixmap, this color is used for background bits.

tabLabel.fontList

Font used for tab labels. If the values of the "tabLabel.height" and "tabHeight" resources are 0, and you do not specify a tab height in the VkTabPanel constructor, the height of the font is also used as the height of the tab display area.

tabLabel.foreground

Color used for tab foregrounds (tab lettering and foreground bits if pixmap a bitmap).

tabLabel.height

Height of tab display area determined by the following methods:

1. Value you specify in the VkTabPanel constructor

2. Value of the VkTabPanel component's "tabHeight" resource

3. Value of the "height" resource of the "tabLabel" widget created by VkTabPanel

4. Height of the tab label's font as specified by fontList" resource of tabLabel" widget created by VkTabPanel.

If you attempt to set tab height via multiple methods, method 1 has the highest precedence and method 4 the lowest. Default value of "tabLabel.height" is 0.

tabLabel.marginHeight

Margin, in pixels, between top and bottom of tab border and tab display area.

tabLabel.marginWidth

Margin, in pixels, between sides of tab border and tab display area.

 

 

Text Completion Field Component

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.

Text Completion Field Constructor and Destructor

The VkCompletionField constructor accepts the standard ObjectPak component name and parent widget arguments:

VkCompletionField(const char *name, Widget parent)

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.

Setting/Clearing the Text Completion Field Expansion List

You can add individual strings to the completion list by passing them as arguments to the VkCompletionField::add() function:

void add(char *name)

You can clear the completion list by calling the VkCompletionField::clear() function:

void clear(VkNameList *nameList = NULL)

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.

Retrieving the Text Completion Field Contents

The VkCompletionField::getText() function duplicates the contents of the text field and then returns a pointer to the duplicate string:

char *getText()

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:

openFile = fileName->getText();

Responding to Text Completion Field Activation

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.

Deriving Text Completion Field Subclasses

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:

virtual void expand(struct XmTextVerifyCallbackStruct *cb)

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:

VkNameList *_currentMatchList

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:

VkNameList *_nameList

You can override the protected member function VkCompletionField::activate(), called whenever the user presses the <Enter> key while typing in the text field:

virtual void activate(struct XmTextVerifyCallbackStruct *cb)

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.

Tick Marks for Scales

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

Tick Marks Component Constructor

The VkTickMarks constructor accepts five arguments:

VkTickMarks(char* name, Widget parent, Boolean labelsToLeft = TRUE,
Boolean noLabels = FALSE, Boolean centerLabels = FALSE)

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.

Configuring the Tick Marks

Set the scale of the tick marks with the VkTickMarks::setScale() function:

void setScale(int min, int max,
int majorInterval, int minorInterval)

Arguments

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.:

ticks->setScale( 0, 1000, 100, 50 );

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():

void addLabel(int value)

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:

void setMargin(int marginTop, int marginBottom);

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.

X Resources Associated with the Tick Marks Component

The VkTickMarks class provides several X resources that determine display characteristics of the component:

 

 

X Resource

Description

minimum

Initial minimum value (default 0)

maximum

Initial maximum value (default 10)

majorInterval

Major tick interval (default 5)

minorInterval

Minor tick interval (default 1)

majorSize

Width in pixels of major tick marks (default 10)

minorSize

Width in pixels of minor tick mark width (default 6)

labelSpacing

Spacing in pixels between tick marks and labels (default 3)

marginTop

Margin in pixels between top of component and top tick mark (default 19)

marginBottom

Margin in pixels between bottom of component and bottom tick mark (default 19)

lineThickness

Thickness in pixels of tick marks thickness (default 1)

label.foreground

Foreground color used for labels and tick marks

label.background

Background color used for labels and tick marks

label.fontList

Font used for labels

 

 

ToolTalk Concepts

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.

Overview

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

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.

Registering a message pattern

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.

Types of messages

There are two types of messages:

  • Notices
  • Requests

Sending applications

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

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.

Troubleshooting Checklist

This section lists some common mistakes to watch for:

  • When using ptype-registered patterns for autostart, make sure your executable has registered its ptype name in VkMsgApp::VkMsgApp() and that it has a non-specific action with no pattern (use VkMsgClient::createAction() instead of addAction()) to handle the request. Otherwise, ToolTalk autostarts your executable indefinitely.
  • If you don't set the optional deleteMessage argument to TRUE in VkMsgClient::addAction(), you should never call VkMsgDestroy() or VkMsgDestroyRequest() in your message action.
  • If you set the optional deleteMessage argument to FALSE in VkMsgClient::addAction(), you're responsible for destroying the message when you're through with it. If the message is a request, be sure to use VkMsgDestroyRequest() instead of VkMsgDestroy(). Otherwise, you may have memory heap corruption caused by double frees, if the requestor and handler are in the same process.
  • You must call VkMsgClient::updatePatterns() after you use VkMsgClient::addAction(). Otherwise, your new actions have no effect.
  • Anytime you go into an X event loop, messages can be received. Therefore, be careful about registering your message actions before calling VkApp::run(), or using a blocking dialog with a secondary event loop. For instance, a NetLS dialog should be used after your actions are added. Otherwise, autostart messages may not be handled properly.
  • With VkMsgClient::composeAdd() routines, ensure that you call VkMsgClient::composeBegin() first to reset the state of your composed arguments. Otherwise, random arguments are added to your message.
  • If you return TRUE from a message action where the reason was VK_MSG_ACTION_HANDLE, ensure that you call VkMsgReply(). Otherwise, ToolTalk assumes that your process is handling the message when in fact you never reply. The requestor waits forever for a response.

ttsession

Also, it is useful to turn on ttsession (the ToolTalk server process) debugging output. You can toggle ttsession on/off with:

kill -USR1 ${TTSessionPid}

U

Undo Management

This section describes the ViewKit ObjectPak undo manager, which supports reversing, or "undoing," actions.

Overview

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.

Default behavior

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.

Using the Undo Manager

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.

Instantiating ViewKit's undo manager

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

Adding the undo manager to a menu

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);

Providing undo support for menu item actions

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.

Example

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:

class EditWindow: public VkWindow {
private:
static VkMenuDesc editPane[];
static void cutCallback(Widget, XtPointer, XtPointer);
static void undoCutCallback(Widget, XtPointer, XtPointer);
// ...
};
VkMenuDesc EditWindow::editPane[] = {
{ ACTION, "Cut", &EditWindow::cutCallback,
NULL, NULL, &EditWindow::undoCutCallback },
{ END }
};
You could do the same thing by adding the menu item dynamically:
class EditWindow: public VkWindow {
private:
static VkSubMenu *editMenu;
static void cutCallback(Widget, XtPointer, XtPointer);
static void undoCutCallback(Widget, XtPointer, XtPointer);
// ...
};
EditWindow::EditWindow(char *name) : VkWindow(name)
{
// ...
editMenu->addAction("Cut", &EditWindow::cutCallback,
&EditWindow::undoCutCallback, this);
}

Providing undo support for non-menu item actions

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():

void add(const char *name,
XtCallbackProc undoCallback,
XtPointer clientData)

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.

Example 30. Adding a Non-menu Item Directly to the Undo Stack

Code

MyComponent: public VkComponent {
public:
MyComponent(const char *, Widget);
void actionCallback(Widget, XtPointer, XtPointer);
void undoActionCallback(Widget, XtPointer, XtPointer);
// ...
};
MyComponent::MyComponent(const char *, Widget parent)
{
// ...
Widget button = XmCreatePushButton(viewWidget, "button", NULL, 0);
XtAddCallback(button, XmNactivateCallback,
&MyWindow::actionCallback, (XtPointer) this);
// ...
}
void MyComponent::actionCallback(Widget w, XtPointer clientData,
XtPointer callData)
{
// Perform action...
theUndoManager->add("Action", &MyComponent::undoActionCallback, this);
}

Providing undo support for command class objects

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.

Enabling and disabling multi-level undo support

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.

Clearing the undo stack

You can force the undo manager to clear its command stack with the VkMenuUndoManager::reset() function:

void reset()

Examining the undo stack

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.

Setting the undo manager menu item label

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

Determining last executed commands

Command_label is the label for the last executed command registered with the undo manager, determined as follows:

  • For commands executed by menu items--VkMenuAction, VkMenuToggle, or VkMenuActionObject (described in "Command Classes" ) objects--the label is the item's XmNlabelString resource.
  • For VkAction objects (described in "Command Classes" ), the undo manager uses the object's "labelString" resource if one is defined, otherwise it uses the VkAction object's name as the label.
  • For actions that you add directly to the undo stack (described in "Using the Undo Manager" ), the undo manager uses the action name that you provided when you added the action.

Example of Using ViewKit's Undo Manager

Example 31. shows an example of using the undo manager.

Example 31. Using the Undo Manager

Code

////////////////////////////////////////////////////////////////
// Simple example to exercise Vk undo facilities
///////////////////////////////////////////////////////////////
#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkMenu.h>
#include <Vk/VkMenuItem.h>
#include <Vk/VkSubMenu.h>
#include <stream.h>
#include <Xm/Label.h>
#include <Xm/RowColumn.h>
#include <Xm/PushB.h>
class MyWindow: public VkWindow {
private:
static void pushCallback( Widget, XtPointer, XtPointer);
static void undoPushCallback( Widget, XtPointer, XtPointer);
static void oneCallback( Widget, XtPointer , XtPointer);
static void twoCallback( Widget, XtPointer , XtPointer);
static void threeCallback( Widget, XtPointer , XtPointer);
static void undoOneCallback( Widget, XtPointer , XtPointer);
static void undoTwoCallback( Widget, XtPointer , XtPointer);
static void undoThreeCallback( Widget, XtPointer , XtPointer);
static void quitCallback( Widget, XtPointer , XtPointer);
void quit();
void one();
void two();
void three();
void undoOne();
void undoTwo();
void undoThree();
static VkMenuDesc appMenuPane[];
static VkMenuDesc mainMenuPane[];
public:
MyWindow( const char *name);
~MyWindow( );
virtual const char* className();
};
MyWindow::MyWindow( const char *name) : VkWindow( name)
{
Widget rc = XmCreateRowColumn(mainWindowWidget(), "rc", NULL, 0);
Widget label = XmCreateLabel(rc, "an undo test", NULL, 0);
Widget pb = XmCreatePushButton(rc, "push", NULL, 0);
XtAddCallback(pb, XmNactivateCallback, &MyWindow::pushCallback,
(XtPointer) this);
XtManageChild(label);
XtManageChild(pb);
setMenuBar(mainMenuPane);
VkSubMenu * editMenuPane = addMenuPane("Edit");
editMenuPane->add(theUndoManager);
addView(rc);
}
MyWindow::~MyWindow()
{
}
const char* MyWindow::className()
{
return "MyWindow";
}
// The menu bar is essentially a set of cascading menu panes, so the
// top level of the menu tree is always defined as a list of submenus
VkMenuDesc MyWindow::mainMenuPane[] = {
{ SUBMENU, "Application", NULL, MyWindow::appMenuPane},
{ END}
};
VkMenuDesc MyWindow::appMenuPane[] = {
{ ACTION, "Command One", &MyWindow::oneCallback, NULL, NULL,
&MyWindow::undoOneCallback },
{ ACTION, "Command Two", &MyWindow::twoCallback, NULL, NULL,
&MyWindow::undoTwoCallback },
{ ACTION, "Command Three", &MyWindow::threeCallback, NULL, NULL,
&MyWindow::undoThreeCallback },
{ SEPARATOR },
{ CONFIRMFIRSTACTION, "Quit", &MyWindow::quitCallback},
{ END},
};
void MyWindow::one()
{
cout << "Command One executed" << "\n" << flush;
}
void MyWindow::two()
{
cout << "Command Two executed" << "\n" << flush;
}
void MyWindow::three()
{
cout << "Command Three executed" << "\n" << flush;
}
void MyWindow::undoOne()
{
cout << "Undoing Command One" << "\n" << flush;
}
void MyWindow::undoTwo()
{
cout << "UNdoing Command Two" << "\n" << flush;
}
void MyWindow::undoThree()
{
cout << "Undoing Command Three" << "\n" << flush;
}
void MyWindow::oneCallback( Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->one();
}
void MyWindow::twoCallback( Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->two();
}
void MyWindow::threeCallback( Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->three();
}
void MyWindow::undoOneCallback( Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->undoOne();
}
void MyWindow::undoTwoCallback( Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->undoTwo();
}
void MyWindow::undoThreeCallback( Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->undoThree();
}
void MyWindow::quitCallback ( Widget, XtPointer clientData, XtPointer )
{
MyWindow *obj = (MyWindow*) clientData;
delete obj;
}
void MyWindow::pushCallback( Widget, XtPointer clientData, XtPointer)
{
cout << "doing a push command\n" << flush;
theUndoManager->add("Push", &MyWindow::undoPushCallback, (XtPointer) clientData);
}

void MyWindow::undoPushCallback( Widget, XtPointer clientData, XtPointer)
{
cout << "undoing the push command\n" << flush;
}
main(int argc, char **argv)
{
VkApp *app = new VkApp("Menudemo", &argc, argv);
MyWindow *win = new MyWindow("MenuWindow");
win->show();
app->run();
}

Undo Management and Command Classes: Overview

Overview

This chapter includes the following sections:

Inheritance graph

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

User Interfaces to the Help System

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.

Using Dialogs

Overview

This chapter includes the following sections:

Inheritance graph

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

Using ViewKit Menu Subclasses

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.

Menu Bar

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.).

Menu Bar Constructors

There are four different versions of the VkMenuBar constructor:

VkMenuBar(Boolean showHelpPane = TRUE)
VkMenuBar(const char *name,
Boolean showHelpPane = TRUE);
VkMenuBar(VkMenuDesc *menuDesc,
XtPointer defaultCientData= NULL,
Boolean showHelpPane = TRUE)
VkMenuBar(const char *name,
VkMenuDesc *menuDesc,
XtPointer defaultCientData = NULL,
Boolean showHelpPane = TRUE)

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" .

Menu Bar access functions

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.

Submenus

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.

Submenu constructor

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:

VkSubMenu(const char *name,
VkMenuDesc *menuDesc = NULL,
XtPointer defaultClientData = NULL)

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.)

Submenu utility and access functions

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.


Radio Submenus

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().

Radio submenu constructor

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:

VkRadioSubMenu(const char *name,
VkMenuDesc *menuDesc = NULL,
XtPointer defaultClientData = NULL)

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" .

Radio Submenu Utility and Access Functions

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.

Example 27. Using a VkRadioSubMenu Object

Code

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkSubMenu.h>
#include <Vk/VkRadioSubMenu.h>
#include <Vk/VkMenu.h>
#include <Xm/Label.h>
#include <stream.h>
#include <stdlib.h>
class MyWindow: public VkWindow {
private:
static void sampleCallback( Widget, XtPointer , XtPointer);
static void quitCallback( Widget, XtPointer , XtPointer);
protected:
void sample();
public:
MyWindow( const char *name);
~MyWindow();
virtual const char* className();
};
MyWindow::~MyWindow()
{
// Empty
}
void MyWindow::sampleCallback( Widget, XtPointer clientData , XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->sample();
}
const char* MyWindow::className() { return "MyWindow";}
void MyWindow::sample()
{
cout << "In Sample Callback" << "\n" << flush;
}
void MyWindow::quitCallback ( Widget, XtPointer, XtPointer )
{
exit(0);
}
MyWindow::MyWindow( const char *name) : VkWindow( name)
{
Widget label = XmCreateLabel(mainWindowWidget(), "a menu", NULL, 0);
// Add a menu pane
VkSubMenu *appMenuPane = addMenuPane("Application");
appMenuPane->addAction("One", &MyWindow::sampleCallback, (XtPointer) this);
appMenuPane->addAction("Two", &MyWindow::sampleCallback, (XtPointer) this);
appMenuPane->addSeparator();
appMenuPane->addAction("Quit", &MyWindow::quitCallback, (XtPointer) this);
// Add a menu second pane
VkSubMenu *sampleMenuPane = addMenuPane("Sample");
sampleMenuPane->addLabel("Test Label");
sampleMenuPane->addSeparator();
sampleMenuPane->addAction("An Action", &MyWindow::sampleCallback,
(XtPointer) this);
// Create a cascading submenu
VkRadioSubMenu *subMenu = sampleMenuPane->addRadioSubmenu("A Submenu");
subMenu->addToggle("foo", &MyWindow::sampleCallback, (XtPointer) this);
subMenu->addToggle("bar", &MyWindow::sampleCallback, (XtPointer) this);
subMenu->addToggle("baz", &MyWindow::sampleCallback, (XtPointer) this);
addView(label);
}
void main(int argc, char **argv)
{
VkApp *myApp = new VkApp("Menu", &argc, argv);
MyWindow *w1 = new MyWindow("menuwindow");
w1->show();
myApp->run();
}

Option Menus

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.


Option menu constructors

There are two different versions of the VkOptionMenu constructor that you can use:

VkOptionMenu(Widget parent,
VkMenuDesc *menuDesc,
XtPointer defaultClientData = NULL)
VkOptionMenu(Widget parent,
const char *name = "optionMenu",
VkMenuDesc *menuDesc = NULL,
XtPointer defaultClientData = NULL)

Note the following considerations:

  • You must provide a parent argument to specify the parent widget of the option menu.
  • The forms of the constructor that do not take a name argument automatically use the name "optionMenu".
  • 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" .

Setting the option menu label

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:

  • Use the VkComponent::setDefaultResources() function to provide default resource values.
  • Set resource values in an external app-defaults resource file. Any values you provide in an external file will override values that you set using the VkComponent::setDefaultResources() function. This is useful when your application must support multiple languages; you can provide a separate resource file for each language supported.
  • Set the resource value directly using the XtSetValues() function. Values you set using this method override any values set using either of the above two methods. You should generally avoid using this method as it "hard codes" the resource values into the code, making them more difficult to change.

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.

Selecting items in an option menu

You can programmatically set the selected item in an option menu using VkOptionMenu::set():

void set(char* name)
void set(int index)
void set(VkMenuItem *item)

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.

Determining selected items in an option menu

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()

Option menu utility functions

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.

Example 28. Using a VkOptionMenu Object

Code

////////////////////////////////////////////////////////////////////
// Demonstrate ObjectPak interface to option menus
///////////////////////////////////////////////////////////////////
#include <Vk/VkApp.h>
#include <Vk/VkSimpleWindow.h>
#include <Vk/VkOptionMenu.h>
#include <stream.h>
#include <Xm/RowColumn.h>
class MyWindow: public VkSimpleWindow {
private:
static void sampleCallback( Widget, XtPointer , XtPointer);
static VkMenuDesc MyWindow::optionPaneDesc[];
protected:
void sample(Widget, XtPointer);
VkOptionMenu *_optionMenu;
public:
MyWindow( const char *name);
~MyWindow( );
virtual const char* className();
};
VkMenuDesc MyWindow::optionPaneDesc[] = {
{ ACTION, "Red", &MyWindow::sampleCallback},
{ ACTION, "Green", &MyWindow::sampleCallback},
{ ACTION, "Blue", &MyWindow::sampleCallback},
{ END},
};
MyWindow::MyWindow( const char *name) : VkSimpleWindow( name)
{
Widget rc = XmCreateRowColumn(mainWindowWidget(), "rc", NULL, 0);
_optionMenu = new VkOptionMenu(rc, optionPaneDesc, (XtPointer) this);
_optionMenu->set("Green");
addView(rc);
}
MyWindow::~MyWindow( )
{
}
const char* MyWindow::className() { return "MyWindow";}
void MyWindow::sampleCallback( Widget w, XtPointer clientData, XtPointer callData)
{
MyWindow *obj = (MyWindow *) clientData;
obj->sample(w, callData);
}
void MyWindow::sample(Widget, XtPointer)
{
cout << "Selected item's index = "
<< _optionMenu->getIndex()
<< ", name = "
<< _optionMenu->getItem()->name()
<< "\n"
<< flush;
}
void main(int argc, char **argv)
{
VkApp *app = new VkApp("Option", &argc, argv);
MyWindow *win = new MyWindow("OptionMenu");
win->show();
app->run();
}

Popup Menus

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.

Popup menu constructors

There are four different versions of the VkPopupMenu constructor:

VkPopupMenu(VkMenuDesc *menuDesc,
XtPointer defaultClientData = NULL)
VkPopupMenu(const char *name = "popupMenu",
VkMenuDesc *menuDesc = NULL,
XtPointer defaultClientData = NULL)
VkPopupMenu(Widget parent,
VkMenuDesc *menuDesc = NULL,
XtPointer defaultClientData = NULL)
VkPopupMenu(Widget parent,
const char *name = "popupMenu",
VkMenuDesc *menuDesc = NULL,
XtPointer defaultClientData = NULL)

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" .

Attaching popup menus to widgets

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.

Popping up popup menus

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.

Example 29. Using a VkPopupMenu Object

Code

//////////////////////////////////////////////////////////////////
// Sample program that demonstrates how to create a popup menu
/////////////////////////////////////////////////////////////////
#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkPopupMenu.h>
#include <stream.h>
#include <Xm/Label.h>
class MyWindow: public VkWindow {
private:
VkPopupMenu *_popup;
static void sampleCallback( Widget, XtPointer , XtPointer);
void sample();
static VkMenuDesc subMenu[];
static VkMenuDesc sampleMenuPane[];
protected:
public:
MyWindow( const char *name);
~MyWindow();
virtual const char* className();
};
MyWindow::MyWindow( const char *name) : VkWindow( name)
{
Widget label = XmCreateLabel(mainWindowWidget(), "a menu", NULL, 0);
_popup = new VkPopupMenu(label, sampleMenuPane, (XtPointer) this);
addView(label);
}
MyWindow::~MyWindow( )
{
}
const char* MyWindow::className() { return "MyWindow";}
// The menu bar is essentially a set of cascading menu panes, so the
// top level of the menu tree is always defined as a list of submenus
VkMenuDesc MyWindow::sampleMenuPane[] = {
{ LABEL, "Test Label"},
{ SEPARATOR },
{ ACTION, "An Action", &MyWindow::sampleCallback},
{ ACTION, "Another Action", &MyWindow::sampleCallback},
{ SUBMENU, "A Submenu", NULL, MyWindow::subMenu},
{ END},
};
VkMenuDesc MyWindow::subMenu[] = {
{ ACTION, "foo", &MyWindow::sampleCallback},
{ ACTION, "bar", &MyWindow::sampleCallback},
{ ACTION, "baz", &MyWindow::sampleCallback},
{ END},
};
void MyWindow::sample()
{
cout << "sample callback" << "\n" << flush;
}
void MyWindow::sampleCallback( Widget, XtPointer clientData , XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->sample();
}
void main(int argc, char **argv)
{
VkApp *myApp = new VkApp("Menudemo", &argc, argv);
MyWindow *menuWin = new MyWindow("MenuWindow");
menuWin->show();
myApp->run();
}

ViewKit Help Menu

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.

Implementation
of the help menu

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.

Adding the help pane to a menu

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.

X Resources for help pane

The following table describes the X resources that affect the appearance and behavior of the VkHelpPane class:

 

 

X Resource

Description

*helpMenu.labelString

Label for Help menu (default value "Help")

*helpMenu.mnemonic

Help menu mnemonic (default
value "H")

*helpMenu.helpOnContextMenuItem.labelString

Label for context-sensitive help item (default value "Click for Help")

*helpMenu.helpOnContextMenuItem.mnemonic

Context-sensitive help item
mnemonic (default value "C")

*helpMenu.helpOnContextMenuItem.accelerator

Context-sensitive help item accelerator (default value "Shift<Key>F1")

*helpMenu.helpOnContextMenuItem.acceleratorText

Context-sensitive help item accelerator label (default value "Shift+F1")

*helpMenu.helpOverviewMenuItem.labelString

Label for help overview item (default value "Overview")

*helpMenu.helpOverviewMenuItem.mnemonic

Help overview item mnemonic (default value "O")

*helpMenu.helpIndexMenuItem.labelString

Label for help index item (default value "Index")

*helpMenu.helpIndexMenuItem.mnemonic

Help index item mnemonic (default value "I")

*helpMenu.helpKeysMenuItem.labelString

Label for keys and shortcuts item (default value "Keys & Shortcuts")

*helpMenu.helpKeysMenuItem.mnemonic

Keys and shortcuts item mnemonic (default value "K")

*helpMenu.helpVersionMenuItem*labelString

Label for the product information item (default value "Product Info")

*helpMenu.helpVersionMenuItem*mnemonic

Product information item mnemonic (default value "P")

 

 

Using ViewKit and X and OSF/Motif Functions

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.

Using a Help System: Overview

Using the ViewKit ObjectPak Dialog Subclasses

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.

Information Dialogs

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.

Example 33. Posting an Information Dialog

Code

#include <Vk/VkWindow.h>
#include <Vk/VkInfoDialog.h>
class MailWindow: public VkWindow {
public:
MailWindow(const char*);
void newMail();
// ...
private:
static String _defaultResources[];
// ...
};
String MailWindow::_defaultResource[] = {
"*newMailMsg: You have new mail in your system mailbox.",
"*newMailTitle: New Mail",
NULL
};
MailWindow::MailWindow(const char *name) : VkSimpleWindow (name)
{
setDefaultResources( mainWindowWidget(), _defaultResources );
// ...
}
void MailWindow::newMail()
{
// ...
theInfoDialog->setTitle("newMailTitle");
theInfoDialog->post("newMailMsg");
// ...
}

Figure 27. shows the appearance of the resulting dialog.

Figure 27. Example of an Information Dialog

Warning Dialogs

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.


Error Dialogs

The VkErrorDialog class supports standard OSF/Motif error dialogs. The global pointer to the error dialog manager, declared in <Vk/VkErrorDialog.h>, is theErrorDialog.

Invalid actions

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.

Fatal Error Dialogs

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.

Busy Dialog

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.

Interruptible Busy Dialog

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.

Example 34. Using the Interruptible Busy Dialog

Code

int i;
// Install the interruptible dialog as the dialog
// to post when busy
theApplication->setBusyDialog(theInterruptDialog);
// Start being "busy"
theApplication->busy("Very Busy", (BusyWindow *) clientData);
for(i=0; i<10000; i++)
{
// Every so often, see if the task was interrupted
if( theInterruptDialog->wasInterupted() )
{
break; // kick out of current task if user interrupts
}
sleep(1);
}
// Task done, so we"re not busy anymore
theApplication->notBusy();
// Restore the application's busy dialog as the default
theApplication->setBusyDialog(NULL);

Question 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.

Prompt Dialog

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.

Example 35. Extracting the Text String from a Prompt Dialog

Code

void MailWindow::okCallback(Widget w, XtPointer, clientData, XtPointer callData)
{
MailWindow *obj = (MailWindow *) clientData;
obj->ok(w, callData);
}
void MailWindow::ok(Widget dialog, XtPointer callData);
{
char *_text;
XmSelectionBoxCallbackStruct *cbs =
(XmSelectionBoxCallbackStruct *) callData;
XmStringGetLtoR(cbs->value,
XmFONTLIST_DEFAULT_TAG,
&_text );
// ...
}

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).


File Selection Dialog

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

Setting the initial directory

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.

Setting the inital filter pattern

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.

Setting the initial dialog selection

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.

Example 36. Extracting the Text String from a File Selection Dialog

Code

void MailWindow::okCallback(Widget w, XtPointer, clientData, XtPointer callData)
{
MailWindow *obj = (MailWindow *) clientData;
obj->ok(w, callData);
}
void MailWindow::ok(Widget dialog, XtPointer callData);
{
char *_text;
XmFileSelectionBoxCallbackStruct *cbs =
(XmFileSelectionBoxCallbackStruct *) callData;
XmStringGetLtoR(cbs->value,
XmFONTLIST_DEFAULT_TAG,
&_text );
// ...
}

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:

#include <iostream.h>
#include <Vk/VkFileSelectionDialog.h>
// ...
theFileSelectionDialog->setDirectory("/usr/tmp");
if(theFileSelectionDialog->postAndWait( ) == VkDialogManager::OK)
cout << "File name: " << theFileSelectionDialog->fileName()
<< '\n' << flush;

Deriving New Dialog Classes Using the Generic Dialog

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.

Changing default displays

To change the default display buttons and other characteristics for your custom dialog, set the protected data members listed in the following table:

 

 

Data Member

Description

Boolean _showOK

If TRUE (default), forces OK button to always appear in your custom dialog. If set FALSE, OK button appears only if you provide an OK callback function when posting the dialog.

Boolean _showCancel

If TRUE (default), forces Cancel button to always appear in your custom dialog. If set FALSE, Cancel button appears only if you provide a cancel callback function when posting the dialog.

Boolean _showApply

If TRUE, forces Apply button to always appear in your custom dialog. If set FALSE (default), Apply button appears only if you provide an apply callback function when posting the dialog.

Boolean _allowMultipleDialogs

If FALSE, does not create additional dialogs, but reuses an existing dialog in all cases.

Note: Default behavior of VkDialogManager class is to allow multiple dialogs of any given type to be posted at once. VkDialogManager class calls the createDialog() member fuction of the derived classes as needed to create additional widgets. For some types of dialogs, you will want to allow only one instance of a particular dialog type to exist at any one time. For example, multiple nested calls to VkApp::busy() should not normally produce multiple dialogs.

Boolean_minimizeMultipleDialogs

If TRUE, VkDialogManager reuses any existing dialog that is not currently displayed. VkDialogManager creates a new dialog only if all existing instances of the dialog type are currently displayed.

Note: VkDialogManager caches dialogs on a per-top-level window basis. If there are many top-level windows, this could result in having many dialogs of the same type. This may be undesirable for some types of dialogs, particularly if they are expensive to create.

 

 

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:

virtual void ok(Widget dialog, XtPointer callData)
virtual void cancel(Widget dialog, XtPointer callData)
virtual void apply(Widget dialog, XtPointer callData)

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.

V

value() (in VkModifiedAttachment)
valueChanged() (in VkCheckBox)
valueChanged() (in VkRadioGroup)
version information
versionString() (in VkApp)
ViewKit
benefits
 
compiling programs
example
header files
libraries
major elements
overview
X and IRIS IM, and
ViewKit callbacks
callback functions
format
[2]
registering
removing
unregistering
creating
defining
invoking
overview
predefined
arcCreatedCallback
(in VkGraph)
arcDestroyedCallback (in VkGraph)
buttonCallback (in VkRepeatButton)
deleteCallback (in VkComponent) [2]
enterCallback (in VkCompletionField)
interruptedCallback (in VkInterruptDialog)
itemChanged (in VkCheckBox)
modifiedCallback (in VkModifiedAttachment)
prefCallback (in VkPrefDialog)
stateChangedCallback (in VkResizer)
tabPopupCallback (in VkTabPanel)
tabSelectCallback (in VkTabPanel)
triggering
ViewKit help library [2]
determining help tokens
ViewKit message facility
establishing ToolTalk connection
message actions
message patterns [2]
overview
policies
receiving messages
registering services for autostart
sending messages
ToolTalk concepts
troubleshooting
ViewKitMajorRelease (in VkApp)
ViewKitMinorRelease (in VkApp)
ViewKitReleaseString (in VkApp)
views, windows [2] [3]
direct instantiation, adding to
replacing
setUpInterface(), creating in
window constructor, creating in
viewWidget() (in VkSimpleWindow)
visible() (in VkSimpleWindow)
VkAction class
activating
 
executing
inheritance graph
member functions
constructor
 
doit()
undoit()
VkAction()
overview
setting label for "Undo" selection
VkAlignmentGroup class
adding widgets
aligning widgets
inheritance graph
member functions
~VkAlignmentGroup()
add()
alignBottom()
alignHeight()
alignLeft()
alignRight()
alignTop()
alignWidth()
constructor
destructor
distributeHorizontal()
distributeVertical()
height()
makeNormal()
remove()
VkAlignmentGroup()
width()
x()
y()
removing widgets
VkApp class
application name [2]
application pointer
busy states [2]
busy dialog [2]
entering
example
exiting
nested
class name [2]
command-line options, parsing [2]
example
component name [2]
cursors
busy, animated [2]
busy, fixed
default [2]
normal
temporary
data members
_winList
 
theApplication
ViewKitMajorRelease
ViewKitMinorRelease
ViewKitReleaseString
Display structure
event handling
during postAndWait()
during sendSyncRequest()
during wasInterrupted()
pending events
raw events [2]
inheritance graph
member functions
aboutDialog
()
afterRealizeHook()
appContext()
applicationClassName()
argc()
argv()
baseWidget()
busy()
busyCursor() [2]
className()
constructor
display()
handlePendingEvents()
handleRawEvent()
hide()
iconify()
lower()
mainWindow()
name()
normalCursor()
notBusy()
open()
parseCommandLine()
progressing()
quitYourself() [2] [3]
raise()
run()
setAboutDialog()
setBusyCursor() [2]
setBusyDialog()
setMainWindow()
setNormalCursor()
setVersionString()
shellGeometry()
show()
showCursor()
startupIconified() [2]
terminate() [2] [3]
versionString()
VkApp()
overview
product information
quitting applications [2] [3] [4] [5] [6]
running applications
shell, application [2] [3]
geometry
subclassing
example
typical use
version information
ViewKit callbacks
windows, managing [2]
XtAppContext structure
VkBusyDialog class
inheritance graph
installing as busy dialog
theBusyDialog
VkCallbackFunction type
VkCallbackMethod type
VkCallbackObject class
inheritance graph
member functions
addCallback
()
callCallbacks()
removeAllCallbacks()
removeCallback()
VkCheckBox class
data members
_label
_rc
_widgetList
example
inheritance graph
member functions
addItem
()
constructor
getValue()
setValue()
setValues()
valueChanged()
VkCheckBox()
setting labels
subclassing
toggles
adding
 
detecting value changes
getting values
setting values
ViewKit callbacks
itemChanged
 
VkCompletionField class
activation, responding
clearing expansion list
data members
_currentMatchList
 
_nameList
inheritance graph
member functions
~VkCompletionField()
activate()
add()
clear()
constructor
destructor
expand()
getText()
VkCompletionField()
replacing expansion list
retrieving contents
setting expansion list
subclassing
ViewKit callbacks
enterCallback
 
VkComponent class
base
widget [2] [3] [4]
deletion, handling
realization, detecting
class name [2]
data members
_baseWidget
[2] [3]
_name [2] [3]
displaying
hiding
inheritance graph
managing widgets [2]
member functions
~VkComponent()
afterRealizeHook()
baseWidget()
className() [2]
constructor
destructor
getResources()
hide()
installDestroyHandler() [2]
isComponent()
name()
okToQuit()
removeDestroyHandler()
setDefaultResources()
show()
VkComponent()
widgetDestroyedCallback()
multiple pointers to component
name [2] [3] [4]
operators
Widget
 
overview
parent widget [2]
resource support
data members, initializing
default values, setting
global values, setting
requirements
resource values, setting
values, retrieving
widget resources, note
static member functions and Xt callbacks [2]
naming convention
this pointer
subclassing
constructor
examples
summary
VkComponent()
testing for valid component
ViewKit callbacks
deleteCallback
 
widget destruction [2] [3] [4]
widgets [2]
Xt callbacks [2]
example
naming convention
this pointer
VkComponent> class
static
member functions and Xt callbacks
example
 
VkComponentclass
ViewKit
callbacks
deleteCallback
VkCursorList class
data
members
_cursorList
 
inheritance graph
member functions
constructor
 
createCursor()
VkCursorList()
VkDialogManager class
Apply button
button labels, setting
Cancel button
centering algorithm
Help button [2] [3]
inheritance graph
member functions
apply
()
cancel()
centerOnScreen()
lastPosted()
ok()
post()
postAndWait() [2]
postBlocked()
postModal()
setButtonLabels()
setTitle()
unpost()
unpostAll()
message
OK button
parent widget
posting
examples
methods
title, setting
unposting
VkDoubleBuffer class
data members
_canvas
_height
_width
drawing
inheritance graph
member functions
~VkDoubleBuffer()
constructor
destructor
draw()
resize()
update()
VkDoubleBuffer()
resizing
switching buffers
VkErrorDialog class
inheritance graph
theErrorDialog
VkFatalErrorDialog class
inheritance graph
theFatalErrorDialog
VkFileSelectionDialog class
caution
inheritance graph
member functions
fileName
()
setDirectory()
setFilterPattern()
setSelection()
theFileSelectionDialog
VkGangedGroup class
adding scrollbars
inheritance graph
member functions
~VkGangedGroup()
add()
constructor
destructor
remove()
removeFirst()
removeLast()
VkGangedGroup()
removing scrollbars
VkGenericDialog class
data members
_allowMultipleDialogs
 
_minimizeMultipleDialogs
_showApply
_showCancel
_showOK
inheritance graph
member functions
createDialog
()
VkGetResource()
example
note
VkGraph class [2]
arc attributes
butterfly graphs
control panel
edit mode [2]
example
finding
graph widget
inheritance graph
member functions
~VkGraph()
add()
addDesktopMenuItems()
addMenuItems()
buildCmdPanel()
buildZoomMenu()
clearAll()
constructor
destructor
display()
displayAll()
displayButterfly()
displayIf()
displayParentsAndChildren()
displayWithAllChildren()
displayWithAllParents()
displayWithChildren()
displayWithParents()
doLayout()
doSparseLayout()
doSubtreeLayout()
expandNode()
expandSubgraph()
find()
forAllNodesDo()
graphWidget()
hideAllChildren()
hideNode()
hideOverview()
hideParents()
hideParentsAndChildren()
hideWithAllChildren()
makeNodeVisible()
numNodes()
overviewWindow()
popupMenu()
relayButton()
remove()
reorientButton()
saveToFile()
setLayoutStyle()
setSize()
setZoomOption()
showOverview()
sortAll()
tearDownGraph()
twinsButton()
twinsVisibleHook()
undisplay()
VkGraph()
workArea()
multiple arcs
Node menu
nodes
adding
 
aligning [2]
deselecting
displaying [2] [3] [4]
establishing connections [2]
hiding [2] [3]
laying out [2]
moving
performing action
removing
selecting
sorting
orientation
overview
overview window [2]
Admin menu
read-only mode
reusing
saving
Selected Nodes menu
subclassing
ViewKit callbacks
arcCreatedCallback
 
arcDestroyedCallback
widget
X resource
zooming [2]
VkGraphFilterProc type
VkGraphNodeProc type
VkHelpPane class [2]
inheritance graph
resources
VkInfoDialog class
inheritance graph
theInfoDialog
VkInterruptDialog class
checking for interruptions
inheritance graph
installing as busy dialog [2]
member functions
wasInterrupted
()
theInterruptDialog
ViewKit callbacks
interruptedCallback
 
VkMenu class
activating menu items
constructing dynamically
example
constructing from static description
example
VkMenuDesc structure
Xt callback client data
deactivating menu items
determining menu item position
finding menu items
inheritance graph
member functions
activateItem
()
add()
addAction()
addConfirmFirstAction()
addLabel()
addRadioSubmenu()
addSeparator()
addSubmenu()
addToggle()
deactivate()
findNamedItem()
getItemPosition()
numItems()
removeItem()
replace()
operators
[] (subscript)
overview
removing menu items
replacing menu items
VkMenuItemType type
XtDisplay() caution
XtScreen() caution
XtWindow() caution
VkMenuAction class
adding to menus
inheritance graph
member functions
hasUndo
()
undo()
VkMenuActionObject class
activating
 
data members
_clientData
()
executing
inheritance graph
member functions
constructor
 
doit()
undoit()
VkMenuActionObject()
overview
VkMenuBar class
inheritance graph
member functions
constructor
 
helpPane()
VkMenuBar()
VkWindow destructor, and
VkWindow support
VkMenuConfirmFirstAction class
adding to menus
inheritance graph
VkMenuDesc structure
VkMenuItem class
activating menu items [2]
deactivating menu items [2]
determining position in menu
displaying menu items
finding menu items
hiding menu items
inheritance graph
labels
member functions
activate
()
deactivate()
hide()
isContainer()
menuType()
remove()
setLabel()
setPosition()
show()
overview
position
removing menu items [2]
replacing menu items
type
XtDisplay() caution
XtScreen() caution
XtWindow() caution
VkMenuItemType type [2]
VkMenuLabel class
adding to menus
inheritance graph
VkMenuSeparator class
adding to menus
inheritance graph
VkMenuToggle class
adding to menus
inheritance graph
member functions
getState
()
setStateAndNotify()
setVisualState()
VkMenuUndoManager class
adding "Undo" selection to menu
example
inheritance graph
instantiating
member functions
add
()
historyList()
multiLevel()
reset()
multi-level undo support
setting "Undo" selection label
theUndoManager
undo stack
clearing
 
examining
undoing
command
class objects
menu item actions
non-menu item actions
VkAction class
VkMenuActionObject class
VkMeter class
adding items
desired dimensions
member functions
~VkMeter()
add()
constructor
destructor
neededHeight()
neededWidth()
reset()
setResizePolicy()
update()
VkMeter()
resetting
resize policy
updating display
X resources
VkModified class
inheritance
graph
VkModifiedAttachment class
adjusting geometry
attaching widgets
controlling contents [2]
detaching widgets
detecting changes
displaying dogear
hiding dogear
inheritance graph
member functions
~VkModifiedAttachment()
adjustGeometry()
attach()
constructor
destructor
detach()
displayValue()
expose()
fixPreviousValue()
getParameters()
hide()
latestDisplay()
modified()
previousValue()
setModified()
setParameters()
setValue()
show()
toggleDisplay()
value()
VkModifiedAttachment()
widget()
overview
retrieving values
ViewKit callbacks
modifiedCallback
 
X resource
VkModifiedCallback structure
VkModifiedReason type
VkMsgApp class [2]
inheritance graph
member functions
constructor
 
messageClient()
VkMsgApp()

ViewKit Components

Overview

This chapter includes the following sections:

Inheritance graph

Figure 3 depicts the inheritance graph for the ViewKit ObjectPak classes VkCallbackObject and VkCallbackObject.

Figure 3. Inheritance Graph for VkCallbackObject and VkComponent

ViewKit Menu Base Class

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.

Constructing Menus

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.

Constructing menus from a static description

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.

VkMenuDesc structure

The definition for the VkMenuDesc structure is:

struct VkMenuDesc {
VkMenuItemType menuType;
char *name;
XtCallbackProc callback;
VkMenuDesc *submenu;
XtPointer clientData;
XtCallbackProc undoCallback;
};

VkMenuDesc fields

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" .

 

 

Menu type parameter values

The menu type parameter is an enumerated value of type VkMenuItemType. The following table describes the VkMenuType parameter values:

 

 

VkMenuItemType Value

Description

ACTION

A selectable menu item, implemented as a VkMenuAction object.

CONFIRMFIRSTACTION

A selectable menu item, implemented as a VkMenuConfirmFirstAction object, which performs an action that the user must confirm before it is executed.

TOGGLE

A two-state toggle button gadget, implemented as a VkMenuToggle object.

LABEL

Label implemented as a VkMenuLabel object.

SEPARATOR

A separator, implemented as a VkMenuSeparator object.

SUBMENU

A cascading submenu, implemented as a VkSubMenu object.

RADIOSUBMENU

A cascading submenu that acts as a radio-style pane, implemented as a VkRadioSubMenu object.

END

All menu descriptions must be terminated by this constant.

 

 

Menu item type fields

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.

 

Array definition example

For example, consider the following array definition:

class EditWindow: public VkWindow {
private:
static VkMenuDesc editMenu[];
// ...
};
VkMenuDesc EditWindow::editMenu[] = {
{ ACTION, "Cut", &EditWindow::cutCallback,
NULL, NULL, &EditWindow::undoCutCallback },
{ ACTION, "Copy", &EditWindow::copyCallback,
NULL, NULL, &EditWindow::undoCopyCallback },
{ ACTION, "Paste", &EditWindow::pasteCallback,
NULL, NULL, &EditWindow::undoPasteCallback },
{ ACTION, "Search" &EditWindow::searchCallback }
{ SEPARATOR },
{ CONFIRMFIRSTACTION, "Revert", &EditWindow::revertCallback },
{ END }
};

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.

Search action

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.

Revert item

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.

Example

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:

class TextWindow: public VkWindow {
private:
static VkMenuDesc menu[];
static VkMenuDesc applicationPane[];
static VkMenuDesc editPane[];
// ...
};
VkMenuDesc TextWindow::applicationPane[] = {
{ ACTION, "Open", &TextWindow::openCallback },
{ ACTION, "Save", &TextWindow::saveCallback },
{ END }
};
VkMenuDesc TextWindow::editPane[] = {
{ ACTION, "Cut", &TextWindow::cutCallback },
{ ACTION, "Paste", &TextWindow::pasteCallback },
{ END }
};
VkMenuDesc TextWindow::menu[] = {
{ SUBMENU, "Application", NULL, applicationPane },
{ SUBMENU, "Edit", NULL, editPane },
{ END }
};

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);

Considerations
for Xt Callback client data

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.

Example 23. Providing Default Client Data Static Menu Descriptions

Code

class SampleWindow: public VkWindow {
private:
static void oneCallback(Widget, XtPointer, XtPointer);
static void twoCallback(Widget, XtPointer, XtPointer);
static void cutCallback(Widget, XtPointer, XtPointer);
static void pasteCallback(Widget, XtPointer, XtPointer);
static VkMenuDesc applicationPane[];
static VkMenuDesc editPane[];
static VkMenuDesc menu[];
public:
SampleWindow(const char *name);
// Other members
};
SampleWindow::SampleWindow(char *name) : VkWindow(name)
{
setMenuBar(new VkMenuBar(menu, (XtPointer) this));
// Other actions
}

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.


Creating a Menubar Using a Static Description

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.

Example 24. Creating a Menu Bar Using a Static Description

Code

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkMenu.h>
#include <iostream.h>
#include <Xm/Label.h>
class MyWindow: public VkWindow {
private:
static void sampleCallback( Widget, XtPointer , XtPointer);
static void quitCallback( Widget, XtPointer , XtPointer);
void quit();
void sample();
static VkMenuDesc subMenu[];
static VkMenuDesc sampleMenuPane[];
static VkMenuDesc appMenuPane[];
static VkMenuDesc mainMenuPane[];
public:
MyWindow( const char *name);
~MyWindow();
virtual const char* className();
};
MyWindow::MyWindow( const char *name) : VkWindow( name)
{
Widget label = XmCreateLabel(mainWindowWidget(), "a menu",
NULL, 0);
setMenuBar(mainMenuPane);
addView(label);
}
MyWindow::~MyWindow()
{
// Empty
}
const char* MyWindow::className()
{
return "MyWindow";
}
// The menu bar is essentially a set of cascading menu panes, so the
// top level of the menu tree is always defined as a list of submenus
VkMenuDesc MyWindow::mainMenuPane[] = {
{ SUBMENU, "Application", NULL, MyWindow::appMenuPane },
{ SUBMENU, "Sample", NULL, MyWindow::sampleMenuPane },
{ END }
};
VkMenuDesc MyWindow::appMenuPane[] = {
{ ACTION, "One", &MyWindow::sampleCallback },
{ ACTION, "Two", &MyWindow::sampleCallback },
{ ACTION, "Three", &MyWindow::sampleCallback },
{ SEPARATOR },
{ ACTION, "Quit", &MyWindow::quitCallback },
{ END },
};
VkMenuDesc MyWindow::sampleMenuPane[] = {
{ LABEL, "Test Label" },
{ SEPARATOR },
{ ACTION, "An Action", &MyWindow::sampleCallback },
{ ACTION, "Another Action", &MyWindow::sampleCallback },
{ SUBMENU, "A Submenu", NULL, MyWindow::subMenu },
{ END },
};
VkMenuDesc MyWindow::subMenu[] = {
{ ACTION, "foo", &MyWindow::sampleCallback },
{ ACTION, "bar", &MyWindow::sampleCallback },
{ ACTION, "baz", &MyWindow::sampleCallback },
{ END },
};
void MyWindow::sample()
{
cout << "sample callback" << "\n" << flush;
}
void MyWindow::sampleCallback(Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->sample();
}
void MyWindow::quitCallback ( Widget, XtPointer, XtPointer )
{
theApplication->quitYourself();
}
void main(int argc, char **argv)
{
VkApp *myApp = new VkApp("Menudemo", &argc, argv);
MyWindow *menuWin = new MyWindow("MenuWindow");
menuWin->show();
myApp->run();
}

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.

Constructing menus dynamically

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.

Functions for dynamically creating menus

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:

VkMenuAction *addAction(const char *name,
XtCallbackProc actionCallback = NULL,
XtPointer clientData = NULL,
int position = -1)
VkMenuAction *addAction(const char *name,
XtCallbackProc actionCallback,
XtCallbackProc undoCallback,
XtPointer clientData,
int position = -1)

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:

VkMenuConfirmFirstAction *
addConfirmFirstAction(const char *name,
XtCallbackProc actionCallback = NULL,
XtPointer clientData = NULL,
int position = -1)

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:

VkMenuToggle *addToggle(const char *name,
XtCallbackProc actionCallback = NULL,
XtPointer clientData = NULL,
int state = -1)
int position = -1)

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:

VkMenuLabel *addLabel(const char *name,
int position = -1)

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:

VkMenuSeparator *addSeparator(const char *name,
int position = -1)

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:

VkSubMenu *addSubmenu(VkSubMenu *submenu,
int position = -1)
VkSubMenu *addSubmenu(const char *name,
int position = -1)
VkSubMenu *addSubmenu(const char *name,
VkMenuDesc *menuDesc)
XtPointer *defaultClientData = NULL)
int position = -1)

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:

VkRadioSubMenu *addRadioSubmenu(VkRadioSubMenu *submenu,
int position = -1)
VkRadioSubMenu *addRadioSubmenu(const char *name,
int position = -1)
VkRadioSubMenu *addRadioSubmenu(const char *name,
VkMenuDesc *menuDesc)
XtPointer *defaultClientData = NULL)
int position = -1)

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.

Example 25. Creating a Menu Bar Dynamically

Code

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkSubMenu.h>
#include <Vk/VkMenu.h>
#include <Xm/Label.h>
#include <iostream.h>
class MyWindow: public VkWindow {
private:
static void sampleCallback( Widget, XtPointer, XtPointer);
static void quitCallback( Widget, XtPointer, XtPointer);
protected:
void sample();
public:
MyWindow( const char *name);
~MyWindow();
virtual const char* className();
};
MyWindow::MyWindow( const char *name) : VkWindow( name)
{
Widget label = XmCreateLabel(mainWindowWidget(), "a menu", NULL, 0);
// Add a menu pane
VkSubMenu *appMenuPane = addMenuPane("Application");
appMenuPane->addAction("One", &MyWindow::sampleCallback,
(XtPointer) this);
appMenuPane->addAction("Two", &MyWindow::sampleCallback,
(XtPointer) this);
appMenuPane->addAction("Three", &MyWindow::sampleCallback,
(XtPointer) this);
appMenuPane->addSeparator();
appMenuPane->addAction("Quit", &MyWindow::quitCallback,
(XtPointer) this);
// Add a menu second pane
VkSubMenu *sampleMenuPane = addMenuPane("Sample");
sampleMenuPane->addLabel("Test Label");
sampleMenuPane->addSeparator();
sampleMenuPane->addAction("An Action",
&MyWindow::sampleCallback,
(XtPointer) this);
sampleMenuPane->addAction("Another Action",
&MyWindow::sampleCallback,
(XtPointer) this);
// Create a cascading submenu
VkSubMenu *subMenu = sampleMenuPane->addSubmenu("A Submenu");
subMenu->addAction("foo", &MyWindow::sampleCallback,
(XtPointer) this);
subMenu->addAction("bar", &MyWindow::sampleCallback,
(XtPointer) this);
subMenu->addAction("baz", &MyWindow::sampleCallback,
(XtPointer) this);
addView(label);
}
MyWindow::~MyWindow()
{
// Empty
}
const char* MyWindow::className() { return "MyWindow";}
void MyWindow::sampleCallback(Widget, XtPointer clientData, XtPointer)
{
MyWindow *obj = (MyWindow *) clientData;
obj->sample();
}
void MyWindow::sample()
{
cout << "sample callback" << "\n" << flush;
}
void MyWindow::quitCallback ( Widget, XtPointer, XtPointer )
{
theApplication->quitYourself();
}
void main(int argc, char **argv)
{
VkApp *myApp = new VkApp("Menu", &argc, argv);
MyWindow *w1 = new MyWindow("menuWindow");
w1->show();
myApp->run();
}

Manipulating Items in Menu

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.

Finding items in a menu

The VkMenu::findNamedItem() function allows you to find an item in a menu given its component name:

VkMenuItem *findNamedItem(const char *name,
Boolean caseless = FALSE)

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().


Activating and deactivating items in a menu

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().

Removing items from a menu

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().


Replacing Items in a Menu

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.

Example of Manipulating Menu Items

The program in Example 26. allows users to dynamically add and remove items from a menu, and also to activate and deactivate items.

Example 26. Manipulating Menu Items

Code

#include <Vk/VkApp.h>
#include <Vk/VkWindow.h>
#include <Vk/VkMenu.h>
#include <Vk/VkSubMenu.h>
#include <Xm/Label.h>
#include <stream.h>
#include <stdlib.h>
class MyWindow: public VkWindow {
private:
static void addOneCallback (Widget, XtPointer, XtPointer);
static void removeOneCallback (Widget, XtPointer, XtPointer);
static void activateOneCallback (Widget, XtPointer, XtPointer);
static void deactivateOneCallback(Widget, XtPointer, XtPointer);
static void sampleCallback (Widget, XtPointer, XtPointer);
static void quitCallback (Widget, XtPointer, XtPointer);
protected:
VkSubMenu *_appMenuPane;
VkSubMenu *_menuPaneTwo;
void addOne();
void removeOne();