Chapter 7. Using Dialogs in ViewKit

This chapter introduces the basic ViewKit classes needed to create and manipulate the dialogs in a ViewKit application. Figure 7-1 shows the inheritance graph for these classes.

Figure 7-1. Inheritance Graph for the ViewKit Dialog Classes

Figure 7-1 Inheritance Graph for the ViewKit Dialog Classes

Overview of ViewKit Dialog Management

Creating all of the dialogs your application uses when you start the application is inefficient: the dialogs, which might or might not be displayed, take time to create, consume memory, and tie up server resources. If an application does not create a dialog until it is needed, the application is smaller and has faster initial startup time; however, depending on the performance of the system, there may be an unacceptable delay in posting each dialog because the application must create a new dialog for each message.

The compromise used by ViewKit is to cache dialogs when they are created. When a particular dialog is no longer needed, the application unmanages that dialog but retains it in the cache. Then, if the cache contains an unused dialog widget when the application needs to post a dialog, the application reuses the cached dialog widget; otherwise it creates a new dialog widget. ViewKit caches up to one dialog of each class for each window in the application (for example, information dialogs and question dialogs are cached separately).

The ViewKit dialog classes also offer the following features:

  • Single function mechanisms for posting dialogs.

  • Ability to post any dialog in non-blocking, non-modal mode; modal mode; and two blocking modes.

  • Positioning in multiwindow applications.

  • Posting of dialogs even when windows are iconified, if desired.

  • Correct handling of dialog references when widgets are destroyed.

ViewKit Dialog Class Overview

ViewKit encapsulates dialog management, including caching, in the abstract VkDialogManager class that serves as a base class for other, specific dialog classes. Each type of dialog in ViewKit has a separate class derived from VkDialogManager. Each class is responsible for managing its own type of dialog (for example, each class maintains its own dialog cache).

The dialog classes provided by ViewKit fall into three categories: information and error dialogs; busy dialogs; and data input dialogs.

The information and error dialogs provide feedback to the user about actions or conditions in the application. The dialog classes in this category are as follows:

VkInfoDialog 

Displays information.

VkWarningDialog 


Warns the user about the consequences of an action (for example, that an action will irretrievably delete items).

VkErrorDialog  


Informs the user of an invalid action (such as entering out-of-range data) or a potentially dangerous condition (for example, the inability to create a backup file).

VkFatalErrorDialog 


Informs the user of a fatal error; the application terminates when the user acknowledges the dialog.

The busy dialogs inform the user that an action is underway which might take considerable time. While a busy dialog is displayed, the user cannot interact with the application. The dialog classes in this category are as follows:

VkBusyDialog 


Dialog displayed while the application is busy.

VkInterruptDialog 


Dialog that allows the user to interrupt the action.

VkProgressDialog 


Dialog that displays a bar graph indicating the percentage of the task that has been completed.

The data input dialogs allow the application to request input from the user. The dialog classes in this category are as follows:

VkQuestionDialog 


Allows the user to choose among simple choices by clicking pushbuttons.

VkPromptDialog 


Prompts the user to enter a text string.

VkColorChooserDialog  


Displays an SgColorChooser dialog, using the caching facilities of the VkDialogManager class.

VkFileSelectionDialog 


Allows the user to interactively browse and select a file or directory.

VkPrefDialog 

Supports preference dialogs capable of displaying a wide variety of program-configurable controls that allow the user to observe and set values used by the program. Chapter 8, “Preference Dialogs,” discusses preference dialogs.

Additionally, ViewKit provides the VkGenericDialog class, an abstract class providing a convenient interface for creating custom dialogs that use the ViewKit interface.

Do not directly instantiate dialog manager objects in your program for the predefined dialog types. ViewKit automatically creates an instance of an appropriate dialog manager if you attempt to use a predefined dialog type in your program.

The header file for each dialog class provides a global pointer to the instance of that class's dialog manager. The name of the pointer consists of “the” followed by the dialog type. For example, the global pointer to the information dialog manager declared in <Vk/VkInfoDialog.h> is theInfoDialog, the global pointer to the error dialog manager declared in <Vk/VkErrorDialog.h> is theErrorDialog, and so forth. To access the dialog managers in your application, simply use these global pointers.[7]


Note: VkGenericDialog, being an abstract class designed for creating customized dialogs, does not automatically create a dialog manager or provide a global pointer.


ViewKit Dialog Base Class

This section describes the dialog management features provided by the abstract VkDialogManager base class. It describes how to post dialogs, unpost dialogs, set dialog titles, and set dialog button labels. Because all ViewKit dialog management classes are derived from VkDialogManager, the functions and techniques described in this section apply to all dialog management classes.

Posting Dialogs

This section describes the various methods of posting dialogs and provides some simple examples.

Methods of Posting Dialogs

ViewKit offers four different functions for posting dialogs:

post() 

Posts a non-blocking, non-modal dialog. The function immediately returns, and the application continues to process user input in all windows.

postModal() 

Posts a non-blocking, full-application-modal dialog. The function immediately returns, but the user cannot interact with any application windows until after dismissing the dialog.

postBlocked() 

Posts a blocking, full-application-modal dialog. The user cannot interact with any application windows until after dismissing the dialog. Furthermore, the function does not return until the user dismisses the dialog.

postAndWait() 

Posts a blocking, full-application-modal dialog. The user cannot interact with any application windows until after dismissing the dialog. Furthermore, the function does not return until the user dismisses the dialog. postAndWait() is simpler to use than postBlocked(), but it does not allow as much programming flexibility.

post(), postModal(), and postBlocked() accept the same arguments. They are also overloaded identically to allow for almost any combination of arguments without resorting to using NULLs as placeholders. Consult the VkDialogManager(3x) reference page for a complete listing of the overloaded versions of the post(), postModal(), and postBlocked() functions. The following is the most general form of the post() function:

virtual Widget post ( const char      *msg = NULL,
                      XtCallbackProc   okCB = NULL,
                      XtCallbackProc   cancelCB = NULL,
                      XtCallbackProc   applyCB = NULL,
                      XtPointer        clientData = NULL,
                      const char      *helpString = NULL,
                      Widget          *parent = NULL)

The following are the arguments for these methods:

msg 

The message to display in the dialog. This string is first treated as a resource name, which is looked up relative to the dialog widget. If it exists, the resource value is used as the message. If the resource does not exist, or if the string contains spaces or newline characters, the string itself is used as the message.

Most dialogs are not useful if you do not provide a message argument: they display no text. VkFileDialog and VkPreferenceDialog are exceptions in that they provide their own complex interfaces.

okCB 

An Xt-style callback function executed when the user clicks the OK button. (All dialogs except for the VkBusyDialog and VkInterruptDialog dialogs display an OK button by default.)

cancelCB 

An Xt-style callback function executed when the user clicks the Cancel button. For many of the dialog classes, ViewKit does not display a Cancel button unless you provide this callback.

applyCB 

An Xt-style callback function executed when the user clicks the Apply button. For many of the dialog classes, ViewKit does not display an Apply button unless you provide this callback.

clientData 

Client data to pass to the button callback functions. Following ViewKit conventions as described in “Using Xt Callbacks With Components”, you should normally pass the this pointer as client data so that the callback functions can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.

helpString 

A help string to pass to the help system. See , “Using a Help System With ViewKit,” for information on the help system. If you provide a string, the dialog displays a Help button.

parent 

The widget over which ViewKit should display the dialog. If you do not provide a widget, or if the given widget is hidden or iconified, ViewKit posts the dialog over the main window if it is managed and not iconified. (“Managing Top-Level Windows” describes how the main window is determined.) If both the widget you specify and the main window are hidden or iconified, ViewKit posts the dialog as a child of the hidden application shell created by the VkApp class. Also see the description of VkDialogManager::centerOnScreen() in “Dialog Access and Utility Functions”.

All versions of the post(), postModal(), and postBlocked() functions return the widget ID of the posted dialog. You should rarely need to use this value.


Note: The arguments that you provide apply only to the dialog posted by the current call to post(), postModal(), and postBlocked(); they have no effect on subsequent dialogs. For example, if you provide an apply callback function to a call to post(), it is used only for the dialog posted by that call. If you want to use that callback for subsequent dialogs, you must provide it as an argument every time you post a dialog.

postAndWait() provides a simpler method for posting blocking, application-modal dialogs than postBlocked(). The most general form of the postAndWait() function is as follows:

virtual VkDialogReason postAndWait ( const char      *msg = NULL, 
                                     Boolean          ok = TRUE, 
                                     Boolean          cancel = TRUE, 
                                     Boolean          apply = FALSE, 
                                     const char      *helpString = NULL,
                                     Widget          *parent = NULL)

msg is the message to display in the dialog. As with the other posting functions, postAndWait() first treats the string as a resource name, which it looks up relative to the dialog widget. If the resource exists, postAndWait() uses the resource value as the message. If postAndWait() finds no resource, or if the string contains spaces or newline characters, it uses the string itself as the message. The next three arguments determine which buttons the dialog should display. A TRUE value displays the button and a FALSE value hides the button. helpString and parent specify a help string and a parent window, just as with the other posting functions.


Note: The arguments that you provide apply only to the dialog posted by the current call to postAndWait(); they have no effect on subsequent dialogs.

When you call postAndWait(), ViewKit posts the dialog, enters a secondary event loop, and does not return until the user dismisses the dialog. Unlike postBlocked(), postAndWait() handles all callbacks internally and simply returns an enumerated value of type VkDialogReason, indicating which button the user chose. The possible return values are VkDialogManager::OK, VkDialogManager::CANCEL, or VkDialogManager::APPLY. postAndWait() is useful for cases in which it is necessary or convenient not to go on to the next line of code until the user dismisses the dialog. For example:

if ( theFileSectionDialog->postAndWait() == VkDialogManager::OK )
    int fd = open( theFileSelectionDialog->fileName(), O_RDONLY);


Note: postAndWait() posts dialogs as full-application modal dialogs to minimize potential problems that can be caused by the secondary event loop, but you should be aware that the second event loop is used and be sure that no non-re-entrant code can be called.

As with the other functions for posting a dialog, postAndWait() is overloaded to allow for almost any combination of arguments without resorting to using NULLs as placeholders. Consult the VkDialogManager reference page for a complete listing of the overloaded versions of postAndWait().


Note: Under certain circumstances, using postAndWait() can cause some unexpected consequences. If you have your own custom dialog, and you delete a widget within it from an event handler such as prePost(), the widget will not be destroyed until the event handler returns. Therefore, widgets that you destroyed will still appear in the dialog. This is because the phase 2 destroy does not happen until the return from the XtDispatch. There are several workarounds you can try if this proves to be a problem:


  • Do not use postAndWait(). Simply post the dialog, return from your event handler, then do whatever you need to do. This may result in flashing, since widgets may be momentarily posted before they are destroyed.

  • Unmanage any widget that should not appear. The object will still be there, but will not be visible.

  • Keep the dialog cleaned up as you go along. Set up the dialog initially with only permanent items. Then, whenever the dialog is posted, add whatever objects you need. Finally, whenever that dialog is taken down, return it to the original state. You can handle this by catching both OK and Cancel callbacks.

Posting Dialogs

The following line posts a simple non-modal, non-blocking information dialog over the application's main window:

theInfoDialog->post("You have new mail in your system mailbox");

Figure 7-2 shows the appearance of this dialog when posted. Because the call did not provide any callback for the OK button, when the user clicks the button, ViewKit simply dismisses the dialog.

Figure 7-2. Information Dialog

Figure 7-2 Information Dialog

You could also specify the message as an X resource. In the above example, you could name the resource something such as newMailMessage and set it in a resource file with the following line:

*newMailMessage:  You have new mail in your system mailbox

Then you could use this line to post the information dialog:

theInfoDialog->post("newMailMessage");

The following code displays a non-modal, non-blocking question dialog over the application's main window:

void MailWindow::newMail()
{
  // ...
    theQuestionDialog->post("Read new mail?",
                            &MailWindow::readMailCallback,
                            (XtPointer) this);
  // ...
}

Figure 7-3 shows the appearance of this dialog when posted. If the user clicks the OK button, the program dismisses the dialog and executes the MailWindow::readMailCallback() function. Following ViewKit conventions as described in “Using Xt Callbacks With Components”, the client data argument is set to the value of the this pointer so that MailWindow::readMailCallback() can retrieve the pointer, cast it to the expected component type, and call a corresponding member function.

Figure 7-3. Question Dialog

Figure 7-3 Question Dialog

Because the call to post() did not provide any callback for the Cancel button, when the user clicks the button, ViewKit simply dismisses the dialog. If instead you needed to perform some type of cleanup operation when the user clicks the Cancel button, you would need to provide a callback for the Cancel button:

void MailWindow::newMail()
{
  // ...
    theQuestionDialog->post("Read new mail?",
                            &MailWindow::readMailCallback,
                            &MailWindow::cleanupMailCallback,
                            (XtPointer) this);
  // ...
}

In general, you should try to encapsulate all dialog callbacks and related information in the subclass of the object with which they are associated. For example, for dialogs that are associated with a specific window, you include all the code related to those dialogs in the subclass definition for that window.

This technique is illustrated in Example 7-1, a simple program which uses the VkWarningDialog class to post a warning dialog.

Example 7-1. Posting a Dialog

#include <Vk/VkApp.h>
#include <Vk/VkSimpleWindow.h>
#include <Vk/VkWarningDialog.h>
#include <Xm/PushB.h>
 
class MyWindow: public VkSimpleWindow {
 
  protected:
    static void postCallback(Widget, XtPointer, XtPointer);
 
  public:
    MyWindow (const char *name);
    ~MyWindow ( );
    virtual const char* className();
};
 
MyWindow::MyWindow (const char *name) : VkSimpleWindow (name) 
{
    Widget button =  XmCreatePushButton (mainWindowWidget(), "Push Me",
                                         NULL, 0);
    XtAddCallback(button, XmNactivateCallback, 
                  &MyWindow::postCallback, 
                  (XtPointer) this);
    addView(button);
}
 
const char* MyWindow::className() { return "MyWindow"; }
 
MyWindow::~MyWindow()
{
    // Empty
}
 
void MyWindow::postCallback(Widget, XtPointer clientData, XtPointer)
{
    theWarningDialog->post("Watch Out!!!", NULL,
                            (MyWindow *) clientData);
}
 
void main ( int argc, char **argv )
{
    VkApp     *app  = new VkApp("Dialog", &argc, argv);
    MyWindow  *win  = new MyWindow("Dialog");
 
    win->show();
    app->run();
}


Manipulating Dialogs Prior to Posting

Using a prepostCallback

If you wish to make changes to a dialog before it is posted, but you do not wish to use subclasses, you can use VkDialogManager::prepostCallback. This callback is invoked just before a dialog is displayed. The callData parameter indicates the dialog widget about to be displayed.

Using prepost()

VkDialogManager provides an overloaded, protected function, prepost(), which allows a subclass to manipulate dialogs before they are posted. Called from VkDialogManager::post(), prepost() is responsible for finding or creating a dialog to be displayed by the post() functions. The two versions of prepost() are as follows:

Widget prepost (const char *message,
                const char *helpString,
                VkSimpleWindow *parent)
 
virtual Widget prepost (const char *message,
                        XtCallbackProc okCB = NULL,
                        XtCallbackProc cancelCB = NULL,
                        XtCallbackProc applyCB = NULL,
                        XtPointer clientData = NULL,
                        const char *helpString = NULL,
                        VkSimpleWindow *parent = NULL)
 

If you use derived classes that need to perform some operations on a dialog widget before displaying it, you should do the following:

  1. Override prepost().

  2. Call VkDialogManager::prepost() directly to obtain a widget.

  3. Do any additional operations you need to do.

  4. Return the Widget returned by VkDialogManager::prepost().

Unposting Dialogs

After posting a dialog, you might encounter situations in which you want to unpost it even though the user has not acknowledged and dismissed it. For example, your application might post an information dialog that the user doesn't bother to acknowledge. At some later point, the information presented in the dialog might no longer be valid, in which case the application should unpost the dialog. In situations such as these, you can use the VkDialogManager::unpost() function to remove the dialog:

void unpost()
void unpost(Widget w)

If you provide the widget ID of a specific dialog, unpost() dismisses that dialog. Otherwise, unpost() dismisses the most recent dialog of that class posted.

If you want to dismiss all dialogs of a given class, you can call the VkDialogManager::unpostAll() function:

void unpostAll()

For example, the following dismisses all information dialogs currently posted:

theInformationDialog->unpostAll();

Setting the Title of the Dialog

By default, ViewKit sets the title of a dialog (displayed in the window manager title bar for the dialog) to the name of the application; however, you have the ability to set dialog titles on both a per-class and per-dialog basis.

If you want all dialogs of a certain class to have a title other than the default, you can specify the title with an X resource. For example, you could set the title of all warning dialogs in an application to “Warning” by including the following line in a resource file:

*warningDialog.dialogTitle: Warning

You can use the VkDialogManager::setTitle() function to set the title for the next dialog of that class that you post:

void setTitle(const char *nextTitle = NULL)

setTitle() accepts as an argument a character string. setTitle() first treats the string as a resource name which it looks up relative to the dialog widget. If the resource exists, setTitle() uses the resource value as the dialog title. If setTitle() finds no resource, or if the string contains spaces or newline characters, it uses the string itself as the dialog title.

setTitle() affects only the next dialog posted; subsequent dialogs revert to the default title for that class.

For example, imagine an editor that uses the question dialog to post two dialogs, one that asks “Do you really want to replace the current buffer?” and one that asks “Do you really want to exit?” If you want different titles for each dialog, you could define resources for each:

*replaceTitle: Dangerous Replacement Dialog
*exitTitle: Last Chance Before Exit Dialog

Then to post the question dialog for replacing the buffer, call the following:

theQuestionDialog->setTitle("replaceTitle");
theQuestionDialog->post("Do you really want to replace the current buffer?",
                         &EditWindow::replaceBufferCallback, 
                         XtPointer) this);

Figure 7-4 shows the resulting dialog.

Figure 7-4. Setting the Dialog Title

Figure 7-4 Setting the Dialog Title

To post the exit question dialog as a modal dialog, call the following:

theQuestionDialog->setTitle("exitTitle");
theQuestionDialog->postModal("Do you really want to exit?",
                             &EditWindow::replaceBufferCallback,
                             (XtPointer) this);

Figure 7-5 shows the resulting dialog.

Figure 7-5. Another Example of Setting the Dialog Title

Figure 7-5 Another Example of Setting the Dialog Title

Setting the Button Labels

The button labels (the text that appears on the buttons) used for a dialog are controlled by the XmNokLabelString, XmNcancelLabelString, and XmNapplyLabelString resources. The default values of these resources are respectively “OK”, “Cancel”, and “Apply”.

You can use the VkDialogManager::setButtonLabels() function to set the button labels for the next dialog that you post:

void setButtonLabels(const char *ok = NULL,
                     const char *cancel = NULL,
                     const char *apply = NULL)

setButtonLabels() accepts as arguments up to three character strings: the first string controls the label for the OK button, the second the label for the Cancel button, and the third the label for the Apply button. If you pass NULL as an argument for any of these strings, the corresponding button uses the default label. setTitle() first treats each string as a resource name, which it looks up relative to the dialog widget. If the resource exists, setTitle() uses the resource value as the button label. If setTitle() finds no resource, or if the string contains spaces or newline characters, it uses the string itself as the button label.

setButtonLabels() affects only the next dialog posted; subsequent dialogs revert to the default button labels.

Dialog Access and Utility Functions

The VkDialogManager class also provides some access and utility functions to help manipulate dialogs.

VkDialogManager::centerOnScreen() controls the algorithm that ViewKit uses to determine where on the screen to post a dialog:

void centerOnScreen(Boolean flag)

If flag is TRUE, ViewKit uses the following algorithm:

  1. If you provide a parent window argument when you call one of the posting functions, and that window is visible and not iconified, ViewKit posts the dialog over that window.

  2. If a) you provide a parent window argument but the window is hidden or iconified, or b) you do not provide a parent window argument, ViewKit creates the dialog as a child of the hidden application shell created by the VkApp class and posts the dialog over that shell. Unless you or the user explicitly sets the geometry for the application, ViewKit centers the application shell on the screen, so the dialog appears centered on the screen.

If flag is FALSE, ViewKit uses the following algorithm, which is the default algorithm:

  1. If you provide a parent window argument when you call one of the posting functions, and that window is visible and not iconified, ViewKit posts the dialog over that window.

  2. If a) you provide a parent window argument but the window is hidden or iconified, or b) you do not provide a parent window argument, ViewKit attempts to create the dialog as a child of the application's main window and post the dialog over that window. (“Managing Top-Level Windows” describes how the main window is determined.)

  3. If the main window is hidden or iconified, ViewKit creates the dialog as a child of the hidden application shell created by the VkApp class and posts the dialog over that shell. Unless you or the user explicitly sets the geometry for the application, ViewKit centers the application shell on the screen, so the dialog appears centered on the screen.

VkDialogManager::enableCancelButton() sets whether or not the default will be to provide a Cancel button in future dialogs, and allows the application to determine when a dialog was closed without using the cancel button, such as by a window manager action:

VkDialogManager::enableCancelButton (Boolean flag)

VkDialogManager::lastPosted() returns the widget ID of the last dialog posted of that class:

Widget lastPosted()

VkDialogManager::setVisual() sets visual resources:

void setVisual (VkVisual *v)

setVisual() overrides any visual arguments that may have been passed in using setArgs().

VkDialogManager::setArgs() allows you to pass in resources to be used when creating the first dialog:

void setArgs (ArgList list, Cardinal argCnt)

Whichever way you set them, dialog arguments should be set just once, before any dialog is created. Due to the way ViewKit caches dialogs, resetting the dialog creation arguments after the first dialog is created results in an undefined action.

Using the ViewKit Dialog Subclasses

This section describes the features of each ViewKit 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 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 7-2 shows 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 7-2. Posting an Information Dialog

#include <Vk/VkWindow.h>
#include <Vk/VkInfoDialog.h>
 
class MailWindow: public VkWindow {
  public:
    MailWindow(const char*);
    void newMail();
    // ...
 
  private:
    static String _defaultResources[];
    // ...
};
 
String MailWindow::_defaultResources[] = {
    "-*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 7-6 shows the appearance of the resulting dialog.

Figure 7-6. Information Dialog

Figure 7-6 Information Dialog

Warning Dialogs

The VkWarningDialog class supports standard 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.


Tip: 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 the warning dialog's OK, button, the ok callback performs the action; if the user clicks the Cancel button, ViewKit dismisses the dialog without performing any action.

VkWarningDialog does not provide any additional functions beyond those offered by the VkDialogManager.

Error Dialogs

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

Use VkErrorDialog to inform the user of an invalid action (such as entering out-of-range data) or potentially dangerous condition (for example, the inability to create a backup file).

The messages contained in the error dialogs should not require any decision to be made by the user. Therefore, the error dialogs display only the OK button by default. If you need the user to make a selection, you should use another dialog class such as VkQuestionDialog.

VkErrorDialog does not provide any additional functions beyond those offered by the VkDialogManager.

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. “Quitting ViewKit 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 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 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 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 ViewKit callback VkInterruptDialog::interruptedCallback. This callback allows objects to register a member function to be called when the user clicks 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 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 7-3 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 7-3. Using the Interruptible Busy Dialog

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


Progress Dialog

The VkProgressDialog class supports applications that perform lengthy, interruptible tasks, and wish to display a progress report to the user. This class displays a bar graph showing what percentage of the job has been completed, and how much remains to be done. See Figure 7-7 for an example of a progress dialog.

The global pointer to the interruptible busy dialog manager, declared in <Vk/VkProgressDialog.h>, is theProgressDialog.

VkProgressDialog is used in nearly the same way as VkInterruptDialog. The only addition is the setPercentDone() method, which changes the dialog's graphical progress indicator.

The prototype for setPercentDone() is as follows:

void setPercentDone(int percentDone)

percentDone should be an integer between 0 and 100, where 100 represents completion.

By default, VkProgressDialog shows a Cancel button that permits the user to interrupt the current task. If you do not wish to allow users to interrupt your task, you can prevent the Cancel button from appearing by passing FALSE as the second parameter in the VKProgressDialog constructor.

Figure 7-7. Progress Dialog

Figure 7-7 Progress Dialog

Example 7-4 shows a code segment that installs the progress dialog and performs a simulated lengthy task, checking for interrupts periodically and updating the progress indicator.

Example 7-4. Using the Progress Dialog

int i;
 
// Install the progress dialog as the dialog to post when busy
 
theApplication->setBusyDialog(theProgressDialog);
 
// Start being “busy”
 
the application->busy(“Very Busy”, (BusyWindow *) clientData);
 
int percentDone = 0;
for (i = 0; i < 10000; i++)
{
    // Every so often see if the task was interrupted
 
    if (theProgressDialog->wasInterrupted())
    {
        break;   // kick out of current task if user interrupts
    }
 
    // Update the percent done indicator. Do this only if we've made
    // more than one percent increment in progress. This avoids 
    // updating the dialog more frequently than is really necessary.
 
    if ((i/100) > percentDone)
    {
        percentDone = i/100;
        theProgressDialog->setPercentDone(percentDone);
    }
 
    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 Motif question dialogs. These allow the user to select among simple choices by clicking 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 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 ViewKit 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.

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 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 as described in “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.

VkPromptDialog::setText() allows you to enter an initial text string in the prompt dialog's text field.

One method of obtaining the text string the user entered in the prompt dialog is to extract it and use it in the OK callback function (and the apply callback function if you provide one). Example 7-5 demonstrates this technique.

Example 7-5. Extracting the Text String From a Prompt Dialog

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 );
    // ...
}

Another method of obtaining the text string is to call VkPromptDialog::text() after the user has dismissed the dialog:

const char *text()

If the user clicks 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 have 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() and postAndWait() returns a value of VkDialogManager::OK.


Caution: The following are two points that you should keep in mind when using VkPromptDialog:


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

  • Be aware that 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, either retrieve the text string in the OK callback function as shown in Example 7-5, 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 Motif file selection dialogs (an example of which is shown in Figure 7-8). These allow the user to interactively browse and select a file or directory. The global pointer to the file selection dialog manager, declared in <Vk/VkFileSelectionDialog.h>, is theFileSelectionDialog.

Figure 7-8. File Selection Dialog

Figure 7-8 File Selection Dialog

You can set the initial directory displayed by the dialog using VkFileSelectionDialog::setDirectory():

void setDirectory(const char *directory)

If you do not explicitly set a directory, the dialog defaults to the current directory.

You can set the initial filter pattern used by the dialog, which determines the files displayed in the list box by using VkFileSelectionDialog::setFilterPattern():

void setFilterPattern(const char *pattern)

If you do not explicitly set a selection, the dialog displays all files in a directory.

You can set the initial selection used of the dialog using 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. Example 7-6 demonstrates this technique.

Example 7-6. Extracting the Text String From a File Selection Dialog

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 );
    // ...
}

Another method of obtaining the selection string is to call VkFileSelectionDialog::fileName() after the user has dismissed the dialog:

const char* fileName()

If the user clicks 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 postAndWait() returns a value of VkDialogManager::OK.


Caution: The following are two points that you should keep in mind when using VkFileSelectionDialog:


  • Do not use fileName() from within one of the VkFileSelectionDialog callback functions. VkFileSelectionDialog sets the value returned by fileName() using its own OK callback function. Because 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.

  • Be aware that 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;   

Color Chooser Dialog

The VkColorChooserDialog class displays an SgColorChooser dialog widget that provides a powerful user-friendly interface for selecting colors (see Figure 7-9). The color chooser provides a color hexagon, color sliders, and editable text fields. The color hexagon allows the user to pick a color by sight. The sliders and text fields let the user choose a color by hue, saturation, and value (HSV), or by the levels of red, green, and blue (RGB). The user has the option of displaying and manipulating different combinations of sliders: value only, value and RGB, and HSV and RGB. The color chooser dialog also allows the user to store one color for reference (the “stored color”) while selecting another one (the “current color”).

For more information about color chooser dialogs, see the VkColorChooserDialog(3x) and SgColorChooser(3X) reference pages. For a demonstration of the VkColorChooserDialog class, see the example program in /usr/share/src/ViewKit/Dialogs.

The global pointer to the color chooser dialog manager, declared in <Vk/VkColorChooserDialog.h>, is theColorChooserDialog.

Figure 7-9. Color Chooser Dialog

Figure 7-9 Color Chooser Dialog

VkColorChooserDialog Access Functions

The VkColorChooserDialog class provides access functions to set and obtain the current and stored color selections. Each of these functions has two variations. One set uses true XColors, with color component values in the range of 0 to 64K. The other set uses colors suitable for non-X graphics, with component values in the range of 0 to 255.

You can obtain the current color by using one of the following functions:

  • getColor()

    XColor* getColor (Void)
    

    Returns a pointer to the current color, whose values range from 0 to 255.

  • getXColor()

    XColor* getXColor (void)
    

    Returns a pointer to the current color, which is a true XColor.

You can set colors by using the following functions:

  • setColor()

    void setColor (short r, short g, short b)
    

    Sets both the current and stored colors; requires color values from 0 to 255.

  • setXColor()

    void setXColor (unsigned short r, unsigned short g, 
                    unsigned short b)
    

    Sets both the current and stored colors; requires standard XColor colors.

  • setCurrentColor()

    void setCurrentColor (short r, short g, short b)
    

    Sets the current color; requires color values from 0 to 255.

  • setCurrentXColor()

    void setCurrentXColor (unsigned short r, unsigned short g, 
                           unsigned short b)
    

    Sets the current color; requires standard XColor colors.

  • setStoredColor()

    void setStoredColor (short r, short g, short b)
    

    Sets the stored color; requires color values from 0 to 255.

  • setStoredXColor()

    void setStoredXColor (unsigned short r, unsigned short g, 
                          unsigned short b)
    

    Sets the stored color; requires standard XColor colors.

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

You can change the buttons displayed by default and other characteristics for your custom dialog by setting certain protected data members:

Boolean _showOK 


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

Boolean _showCancel 


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

Boolean _showApply 


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

Boolean _allowMultipleDialogs 


The default behavior of the VkDialogManager class is to allow multiple dialogs of any given type to be posted at once. The VkDialogManager class calls derived classes's createDialog() member function as needed to create additional widgets. For some types of dialogs, it makes more sense 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. If you set _allowMultipleDialogs to FALSE, the VkDialogManager class does not create additional dialogs, but reuses an existing dialog in all cases.

Boolean _minimizeMultipleDialogs 


Normally, 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, which may be undesirable for some types of dialogs, particularly if they are expensive to create. If you set _minimizeMultipleDialogs 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.

Also, by default ViewKit dismisses your dialog whenever the user clicks either the OK or Cancel button, and keeps the dialog posted whenever the user clicks 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)

ViewKit calls these functions whenever the user clicks 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.

Putting Dialogs in the Overlay Planes

By default, dialogs appear in the normal planes. ViewKit dialogs, however, may be explicitly placed in the overlay planes. Doing so prevents the dialogs from causing expose events that disturb such things as complex GL rendering in the normal planes.

There are three ways to enable dialogs in the overlay planes:

  • Call VkDialogManager::useOverlayDialogs(TRUE). This forces dialogs into the overlay planes, with no way to put them back in the normal planes without recompiling.

  • Put the resource string “*useOverlayDialogs:True” in your application's default file. This will put dialogs in the overlay planes by default, but allow users to use the normal planes by changing their .Xdefaults file.


    Note: This is an application-specific resource. There is no class resource, so “*UseOverlayDialogs” is not supported.


  • Have users add the -useOverlayDialogs command-line switch when they run your application if they wish to use the overlay planes for dialogs.

If you do decide to place dialogs in the overlay planes, here are some factors to consider:

  • Dialogs are placed in the deepest available overlay planes: generally 4- or 8-bit planes, occasionally 2-bit planes.

  • If the deepest available overlay is 2 bits, any dialogs placed in that visual may not look right. Because the colormap in the 2-bit overlay planes only has three color entries (the fourth being a transparent pixel), any items in the dialog other than labels (for example cascade or toggle buttons) may look odd.

  • Other applications using the overlay planes may display in the wrong colors when the application posting the dialog gets colormap focus. The colors in the other applications may flash because the dialog's colormap is installed and replaces any previous overlay colormap.



[7] These global pointers are actually implemented as compiler macros that invoke access functions to return pointers to the unique instantiation of the dialog managers. Although you should never need to use these access functions directly, you might encounter them while debugging a ViewKit application that uses dialogs.