This chapter describes the fundamental concepts that
underly all Motif dialogs. It provides a foundation for the more
advanced material in the following chapters. In the course of the
introduction, the chapter also provides information about Motif's
predefined MessageDialog classes.
In Chapter 4, The Main Window, we discussed
the top-level windows that are managed by the window manager and that
provide the overall framework for an application. Most applications are
too complex to do everything in one main top-level window. Situations
arise that call for secondary windows, or transient windows,
that serve specific purposes. These windows are commonly referred to as
dialog boxes, or more simply as dialogs.
Dialog boxes play an integral role in a GUI-based
interface such as Motif. The examples in this book use dialogs in many
ways, so just about every chapter can be used to learn more about
dialogs. We've already explored some of the basic concepts in
Chapter 2, The Motif Programming Model, and Chapter 3,
Overview of the Motif Toolkit. However, the use of dialogs in Motif
is quite complex, so we need more detail to proceed further.
The Motif Style Guide makes a set of generic
recommendations about how all dialogs should look. The Style Guide
also specifies precisely how certain dialogs should look, how they
should respond to user events, and under what circumstances the dialogs
should be used. We refer to these dialogs as predefined Motif dialogs,
since the Motif toolkit implements each of them for you. These dialogs
are completely self-sufficient, opaque objects that require very little
interaction from your application. In most situations, you can create
the necessary dialog using a single convenience routine and you're
done. If you need more functionality than what is provided by a
predefined Motif dialog, you may have to create your own customized
dialog. In this case, building and handling the dialog requires a
completely different approach.
There are three chapters on basic dialog usage in
this book--two on the predefined Motif dialogs and one on customized
dialogs. There is also an additional chapter later in the book that
deals with more advanced dialog topics. This first chapter discusses
the most common class of Motif dialogs, called MessageDialogs. These
are the simplest kinds of dialogs; they typically display a short
message and use a small set of standard responses, such as OK,
Yes, or No. These dialogs are transient, in that they are
intended to be used immediately and then dismissed. MessageDialogs
define resources and attributes that are shared by most of the other
dialogs in the Motif toolkit, so they provide a foundation for us to
build upon in the later dialog chapters. Although Motif dialogs are
meant to be opaque objects, we will examine their implementation and
behavior in order to understand how they really work. This information
can help you understand not only what is happening in your application,
but also how to create customized dialogs.
Chapter 6, Selection Dialogs, describes
another set of predefined Motif dialogs, called SelectionDialogs. Since
these dialogs are the next step in the evolution of dialogs, most of
the material in this chapter is applicable there as well.
SelectionDialogs typically provide the user with a list of choices.
These dialogs can remain displayed on the screen so that they can be
used repeatedly. Chapter 7, Custom Dialogs, addresses the issues
of creating customized dialogs, and Chapter 21, Advanced Dialog
Programming, discusses some advanced topics in X and Motif
programming using dialogs as a backdrop.
For most applications, it is impossible to develop
an interface that provides the full functionality of the application in
a single main window. As a result, the interface is typically broken up
into discrete functional modules, where the interface for each module
is provided in a separate dialog box.
As an example, consider an electronic mail
application. The broad range of different functions includes searching
for messages according to patterns, composing messages, editing an
address book, reporting error messages, and so on. Dialog boxes are
used to display simple messages, as shown in the figure. They are also
used to prompt the user to answer simple questions, as shown in the
figure. A dialog box can also present a more complicated interaction,
as shown in the figure.
In the figure, many different widget classes are
used to provide an interface that allows the user to save e-mail
messages in different folders. The purpose of a dialog is to focus on
one particular task in an application. Since the scope of these tasks
is usually quite limited, an application usually provides them in
dialog boxes, rather than in its main window.
There is actually no such thing as a dialog widget
class in the Motif toolkit. A dialog is actually made up of a
DialogShell widget and a manager widget child that implements the
visible part of the dialog. The DialogShell interacts with the window
manager to provide the transient window behavior required of dialogs.
When we refer to a dialog widget, we are really talking about the
manager widget and all of its children collectively.
When you write a custom dialog, you simply create
and manage the children of the DialogShell in the same way that you
create and manage the children of a top-level application shell. The
predefined Motif dialogs follow the same approach, except that the
toolkit creates the manager widget and all of its children internally.
Most of the standard Motif dialogs are composed of a DialogShell and
either a MessageBox or SelectionBox widget. Each of these widget
classes creates and manages a number of internal widgets without
application intervention. See Chapter 3, Overview of the Motif
Toolkit, to review the various types of predefined Motif dialogs.
All of the predefined Motif dialogs are subclassed
from the BulletinBoard widget class. As such, a BulletinBoard can be
thought of as the generic dialog widget class, although it can
certainly be used as generic manager widget (see Chapter 8, Manager
Widgets). Indeed, a dialog widget is a manager widget, but it is
usually not treated as such by the application. The BulletinBoard
widget provides the keyboard traversal mechanisms that support gadgets,
as well as a number of dialog-specific resources.
It is important to note that for the predefined
Motif dialogs, each dialog is implemented as a single widget class,
even though there are smaller, primitive widgets under the hood. When
you create a MessageBox widget, you automatically get a set of Labels
and PushButtons that are laid out as described in the Motif Style
Guide. What is not created automatically is the DialogShell widget
that manages the MessageBox widget. You can either create the shell
yourself and place the MessageBox in it or use a Motif convenience
routine that creates both the shell and its dialog widget child.
The Motif toolkit uses the DialogShell widget class
as the parent for all of the predefined Motif dialogs. In this context,
a MessageBox widget combined with a DialogShell widget creates what the
Motif toolkit calls a MessageDialog. A careful look at terminology can
help you to distinguish between actual widget class and Motif compound
objects. The name of the actual widget class ends in Box,
while the name of the compound object made up of the widget and a
DialogShell ends in Dialog. For example, the convenience
routine XmCreateMessageBox() creates a MessageBox widget,
which you need to place inside of a DialogShell yourself.
Alternatively, XmCreateMessageDialog() creates a MessageDialog
composed of a MessageBox and a DialogShell.
Another point about terminology involves the
commonly-used term dialog box. When we say dialog box, we are referring
to a compound object composed of a DialogShell and a dialog widget, not
the dialog widget alone. This terminology can be confusing, since the
Motif toolkit also provides widget classes that end in box.
One subtlety in the use of MessageBox and
SelectionBox widgets is that certain types of behavior depend on
whether or not the widget is a direct child of a DialogShell. For
example, the Motif Style Guide says that clicking on the OK
button in the action area of a MessageDialog invokes the action of the
dialog and then dismisses the dialog. Furthermore, pressing the RETURN
key anywhere in the dialog is equivalent to clicking on the OK
button. However, none of this takes place when the MessageBox widget is
not a direct child of a DialogShell.
Perhaps the most important thing to remember is how
the Motif toolkit treats dialogs. Once a dialog widget is placed in a
DialogShell, the toolkit tends to treat the entire combination as a
single entity. In fact, as we move on, you'll find that the toolkit's
use of convenience routines, callback functions, and popup widget
techniques all hide the fact that the dialog is composed of these
discrete elements. While the Motif dialogs are really composed of many
primitive widgets, such as PushButtons and TextFields, the
single-entity approach implies that you never access the subwidgets
directly. If you want to change the label for a button, you set a
resource specific to the dialog class, rather than getting a handle to
the button widget and changing its resource. Similarly, you always
install callbacks on the dialog widget itself, instead of installing
them directly on buttons in the control or action areas.
This approach may be confusing for those already
familiar with Xt programming, but not yet familiar with the Motif
toolkit. Similarly, those who learn Xt programming through experiences
with the Motif toolkit might get a misconception of what Xt programming
is all about. We try to point out the inconsistencies between the two
approaches so that you will understand the boundaries between the Motif
toolkit and its Xt foundations.
As described in Chapter 3, Overview of the Motif
Toolkit, dialogs are typically broken down into two regions known
as the control and action areas. The control area is also referred to
as the work area. The control area contains the widgets that provide
the functionality of the dialog, such as Labels, ToggleButtons, and
List widgets. The action area contains PushButtons whose callback
routines actually perform the action of the dialog box. While most
dialogs follow this pattern, it is important to realize that these two
regions represent user-interface concepts and do not necessarily
reflect how Motif dialogs are implemented.
the figure shows these areas in a sample dialog box.
The Motif Style Guide describes in a general
fashion how the control and action areas for all dialogs should be laid
out. For predefined Motif dialogs, the control area is rigidly
specified. For customized dialogs, there is only a general set of
guidelines to follow. The guidelines for the action area specify a
number of common actions that can be used in both predefined Motif
dialogs and customized dialogs. These actions have standard meanings
that help ensure consistency between different Motif applications.
By default, the predefined Motif MessageDialogs
provide three action buttons, which are normally labeled OK,
Cancel, and Help, respectively. SelectionDialogs provide a
fourth button, normally labeled Apply, which is placed between
the OK and Cancel buttons. This button is created but not
managed, so it is not visible unless the application explicitly manages
it. The Style Guide specifies that the OK button applies
the action of the dialog and dismisses it, while the Apply
button applies the action but does not dismiss the dialog. The
Cancel button dismisses the dialog without performing any action
and the Help button provides any help that is available for the
dialog. When you are creating custom dialogs, or even when you are
using the predefined Motif dialogs, you may need to provide actions
other than the default ones. If so, you should change the labels on the
buttons so that the actions are obvious. You should try to use the
common actions defined by the Motif Style Guide if they are
appropriate, since these actions have standard meanings. We will
address this issue further as it comes up in discussion; it is not
usually a problem until you create your own customized dialogs, as
described in Chapter 7, Custom Dialogs.
Under most circumstances, creating a predefined
Motif dialog box is very simple. All Motif dialog types have
corresponding convenience routines that simplify the task of creating
and managing them. For example, a standard MessageDialog can be created
as shown in the following code fragment:
#include <Xm/MessageB.h> extern Widget parent; Widget dialog; Arg arg[5]; XmString t; int n = 0; t = XmStringCreateLocalized ("Hello World"); XtSetArg (arg[n], XmNmessageString, t); n++; dialog = XmCreateMessageDialog (parent, "message", arg, n); XmStringFree (t);The convenience routine does almost everything automatically. The only thing that we have to do is specify the message that we want to display.
As we mentioned earlier, there are two basic types
of predefined Motif dialog boxes: MessageDialogs and SelectionDialogs.
MessageDialogs present a simple message, to which a yes (OK) or
no (Cancel) response usually suffices. There are six types of
MessageDialogs: ErrorDialog, InformationDialog, QuestionDialog,
TemplateDialog, WarningDialog, and WorkingDialog. These types are not
actually separate widget classes, but rather instances of the generic
MessageDialog that are configured to display different graphic symbols.
All of the MessageDialogs are compound objects that are composed of a
MessageBox widget and a DialogShell. When using MessageDialogs, you
must include the file <Xm/MessageB.h>.
SelectionDialogs allow for more complicated
interactions. The user can select an item from a list or type an entry
into a TextField widget before acting on the dialog. There are
essentially four types of SelectionDialogs, although the situation is a
bit more complex than for MessageDialogs. The PromptDialog is a
specially configured SelectionDialog; both of these dialogs are
compound objects that are composed of a SelectionBox widget and a
DialogShell. The Command widget and the FileSelectionDialog are based
on separate widget classes. However, they are both subclassed from the
SelectionBox and share many of its features. When we use the general
term "selection dialogs," we are referring to these three widget
classes plus their associated dialog shells. To use a SelectionDialog,
you must include the file <Xm/SelectioB.h>. Yes, you read that
right. It does, in fact, read SelectioB.h. The reason for the
missing n is there is a fourteen-character filename limit on
UNIX System V machines. For FileSelectionDialogs, the appropriate
include file is <Xm/FileSB.h>, and for the Command widget it is
<Xm/Command.h>.
You can use any of the following convenience
routines to create a dialog box. They are listed according to the
header file in which they are declared:
<Xm/MessageB.h>:
XmCreateMessageBox() XmCreateMessageDialog() XmCreateErrorDialog() XmCreateInformationDialog() XmCreateQuestionDialog() XmCreateTemplateDialog() XmCreateWarningDialog() XmCreateWorkingDialog()<Xm/SelectioB.h>:
XmCreateSelectionBox() XmCreateSelectionDialog() XmCreatePromptDialog()<Xm/FileSB.h>:
XmCreateFileSelectionBox() XmCreateFileSelectionDialog()<Xm/Command.h>:
XmCreateCommand()Each of these routines creates a dialog widget. In addition, the routines that end in Dialog automatically create a DialogShell as the parent of the dialog widget. All of the convenience functions for creating dialogs use the standard Motif creation routine format. For example, XmCreateMessageDialog() takes the following form:
Widget XmCreateMessageDialog(parent, name, arglist, argcount) Widget parent; String *name; ArgList arglist; Cardinal argcount;In this case, we are creating a common MessageDialog, which is a MessageBox with a DialogShell parent. The parent parameter specifies the widget that acts as the owner or parent of the DialogShell. Note that the parent must not be a gadget, since the parent must have a window associated with it. The dialog widget itself is a child of the DialogShell. You are returned a handle to the newly created dialog widget, not the DialogShell parent. For the routines that just create a dialog widget, the parent parameter is simply a manager widget that contains the dialog.
The arglist and argcount
parameters for the convenience routines specify resources using the
old-style ArgList format, just like the rest of the Motif
convenience routines. A varargs-style interface is not available for
creating dialogs. However, you can use the varargs-style interface for
setting resources on a dialog after is has been created by using
XtVaSetValues().
There are a number of resources and callback
functions that apply to almost all of the Motif dialogs. These
resources deal with the action area buttons in the dialogs. Other
resources only apply to specific types of dialogs; they deal with the
different control area components such as Labels, TextFields, and List
widgets. The different resources are listed below, grouped according to
the type of dialogs that they affect:
General dialog resources:
XmNokLabelString XmNokCallback XmNcancelLabelString XmNcancelCallback XmNhelpLabelString XmNhelpCallbackMessageDialog resources:
XmNmessageString XmNsymbolPixmapSelectionDialog resources:
XmNapplyLabelString XmNapplyCallback XmNselectionLabelString XmNlistLabelStringFileSelectionDialog resources:
XmNfilterLabelString XmNdirListLabelString XmNfileListLabelStringCommand resources:
XmNpromptStringThe labels and callbacks of the various buttons in the action area are specified by resources based on the standard Motif dialog button names. For example, the XmNokLabelString resource is used to set the label for the OK button. XmNokCallback is used to specify the callback routine that the dialog should call when that button is activated. As discussed earlier, it may be appropriate to change the labels of these buttons, but the resource and callback names will always have names that correspond to their default labels.
The XmNmessageString resource specifies the
message that is displayed by the MessageDialog. The XmNsymbolPixmap
resource specifies the iconic symbol that is associated with each of
the MessageDialog types. This resource is rarely changed, so discussion
of it is deferred until Chapter 21, Advanced Dialog Programming.
The other resources apply to the different types of
selection dialogs. For example, XmNselectionLabelString sets
the label that is placed above the list area in SelectionDialog. These
resources are discussed in Chapter 6, Selection Dialogs.
All of these resources apply to the Labels and
PushButtons in the different dialogs. It is important to note that they
are different from the usual resources for Labels and PushButtons. For
example, the Label resource XmNlabelString would normally be
used to specify the label for both Label and PushButton widgets.
Dialogs use their own resources to maintain the abstraction of the
dialog widget as a discrete user-interface object.
Another important thing to remember about the
resources that refer to widget labels is that their values must be
specified as compound strings. Compound strings allow labels to be
rendered in arbitrary fonts and to span multiple lines. See Chapter 19,
Compound Strings, for more information.
The following code fragment demonstrates how to
specify dialog resources and callback routines:
Widget dialog; XmString msg, yes, no; extern void my_callback(); dialog = XmCreateQuestionDialog (parent, "dialog", NULL, 0); yes = XmStringCreateLocalized ("Yes"); no = XmStringCreateLocalized ("No"); msg = XmStringCreateLocalized ("Do you want to quit?");
XtVaSetValues (dialog, XmNmessageString, msg, XmNokLabelString, yes, XmNcancelLabelString, no, NULL); XtAddCallback (dialog, XmNokCallback, my_callback, NULL); XtAddCallback (dialog, XmNcancelCallback, my_callback, NULL); XmStringFree (yes); XmStringFree (no); XmStringFree (msg);
None of the Motif toolkit convenience functions
manage the widgets that they create, so the application must call
XtManageChild() explicitly. It just so happens that managing a
dialog widget that is the immediate child of a DialogShell causes the
entire dialog to pop up. Similarly, unmanaging the same dialog widget
causes it and its DialogShell parent to pop down. This behavior is
consistent with the Motif toolkit's treatment of the dialog/shell
combination as a single object abstraction. The toolkit is treating its
own dialog widgets as opaque objects and trying to hide the fact that
there are DialogShells associated with them. The toolkit is also making
the assumption that when the programmer manages a dialog, she wants it
to pop up immediately.
This practice is somewhat presumptuous and it
conflicts directly with the specifications for the X Toolkit
Intrinsics. These specifications say that when the programmer wants to
display a popup shell on the screen, she should use XtPopup().
Similarly, when the dialog is to be dismissed, the programmer should
call XtPopdown(). The fact that XtManageChild()
happens to pop up the shell and XtUnmanageChild() causes it to
pop down is misleading to the new Motif programmer and confusing to the
experienced Xt programmer.
You should understand that this discussion of
managing dialogs does not apply to customized dialogs that you create
yourself. It only applies to the predefined Motif dialog widgets that
are created as immediate children of DialogShells. The Motif toolkit
uses this method because it has been around for a long time and it must
be supported for backwards compatibility with older versions.
Furthermore, using XtPopup() requires access to the
DialogShell parent of a dialog widget, which breaks the single-object
abstraction.
There are two ways to manage Motif dialogs. You can
follow the Motif toolkit conventions of using XtManageChild()
and XtUnmanageChild() to pop up and pop down dialog widgets or
you can use XtPopup() and XtPopdown() on the dialog's
parent to do the same job. Whatever you do, it is good practice to pick
one method and be consistent throughout an application. It is possible
to mix and match the methods, but there may be some undesirable side
effects, which we will address in the next few sections.
In an effort to make our applications easier to port
to other Xt-based toolkits, we follow the established convention of
using XtPopup(). This technique can coexist easily with
XtManageChild(), since popping up an already popped-up shell has no
effect. XtPopup() takes the following form:
void XtPopup(shell, grab_kind) Widget shell; XtGrabKind grab_kind;The shell parameter to the function must be a shell widget; in this case it happens to be a DialogShell. If you created the dialog using one of the Motif convenience routines, you can get a handle to the DialogShell by calling XtParent() on the dialog widget.
The grab_kind parameter can be one
of XtGrabNone, XtGrabNonexclusive, or
XtGrabExclusive. We almost always use XtGrabNone, since
the other values imply a server grab, which means that other
windows on the desktop are locked out. Grabbing the server results in
what is called modality; it implies that the user cannot
interact with anything but the dialog. While a grab may be desirable in
some cases, the Motif toolkit provides some predefined resources that
handle the grab for you automatically. The advantage of using this
alternate method is that it allows the client to communicate more
closely with the Motif Window Manager (mwm) and it provides for
different kinds of modality. These methods are discussed in Section
#smodaldlg. For detailed information on XtPopup() and the
different uses of grab_kind, see Volume Four, X
Toolkit Intrinsics Programming Manual.
If you call XtPopup() on a dialog widget
that has already been popped up using XtManageChild(), the
routine has no effect. As a result, if you attempt to specify
grab_kind as something other than XtGrabNone, it also
has no effect.
The counterpart to XtPopup() is
XtPopdown(). Any time you want to pop down a shell, you can use
this function, which has the following form:
void XtPopdown(shell) Widget shell;Again, the shell parameter should be the XtParent() of the dialog widget. If you use XtUnmanageChild() to pop down a dialog, it is not necessary to call XtPopdown(), although we advise it for correctness and good form. However, it is important to note that if you use XtUnmanageChild() to pop down a dialog, you must use XtManageChild() to redisplay it again. Don't forget that the dialog widget itself is not a shell, so managing or unmanaging it still takes place when you use the manage and unmanage functions.
Let's take a closer look at how dialogs are really
used in an application. Examining the overall design and the mechanics
that are involved will help to clarify a number of issues about
managing and unmanaging dialogs and DialogShells. The program listed in
the source code displays an InformationDialog when the user presses a
PushButton in the application's main window. XtSetLanguageProc()
is only available in X11R5; there is no corresponding function in
X11R4. XmStringCreateLocalized() is only available in Motif
1.2; XmStringCreateSimple() is the corresponding function in
Motif 1.1.
/* hello_dialog.c -- your typical Hello World program using * an InformationDialog. */ #include <Xm/RowColumn.h> #include <Xm/MessageB.h> #include <Xm/PushB.h> main(argc, argv) int argc; char *argv[]; { XtAppContext app; Widget toplevel, rc, pb; extern void popup(); /* callback for the pushbuttons -- pops up dialog */ extern void exit(); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); rc = XtVaCreateWidget ("rowcol", xmRowColumnWidgetClass, toplevel, NULL); pb = XtVaCreateManagedWidget ("Hello", xmPushButtonWidgetClass, rc, NULL); XtAddCallback (pb, XmNactivateCallback, popup, "Hello World"); pb = XtVaCreateManagedWidget ("Goodbye", xmPushButtonWidgetClass, rc, NULL); XtAddCallback (pb, XmNactivateCallback, exit, NULL); XtManageChild (rc); XtRealizeWidget (toplevel); XtAppMainLoop (app); } /* callback for the PushButtons. Popup an InformationDialog displaying * the text passed as the client data parameter. */ void popup(button, client_data, call_data) Widget button; XtPointer client_data; XtPointer call_data; { Widget dialog; XmString xm_string; extern void activate(); Arg args[5]; int n = 0; char *text = (char *) client_data; /* set the label for the dialog */ xm_string = XmStringCreateLocalized (text); XtSetArg (args[n], XmNmessageString, xm_string); n++; /* Create the InformationDialog as child of button */ dialog = XmCreateInformationDialog (button, "info", args, n); /* no longer need the compound string, free it */ XmStringFree (xm_string); /* add the callback routine */ XtAddCallback (dialog, XmNokCallback, activate, NULL); /* manage the dialog */ XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); } /* callback routine for when the user presses the OK button. * Yes, despite the fact that the OK button was pressed, the * widget passed to this callback routine is the dialog! */ void activate(dialog, client_data, call_data) Widget dialog; XtPointer client_data; XtPointer call_data; { puts ("OK was pressed."); }
The output of this program is shown in the figure.
Dialogs are often invoked from callback routines
attached to PushButtons or other interactive widgets. Once the dialog
is created and popped up, control of the program is returned to the
main event-handling loop (XtAppMainLoop()), where normal event
processing resumes. At this point, if the user interacts with the
dialog by selecting a control or activating one of the action buttons,
a callback routine for the dialog is invoked. In the source code we
happen to use an InformationDialog, but the type of dialog used is
irrelevant to the model.
When the PushButton in the main window is pressed,
popup() is called. A text string that is used as the message to
display in the InformationDialog is passed as client data. The dialog
uses a single callback routine, activate(), for the
XmNokCallback resource. This function is invoked when the user
presses the OK button. The callback simply prints a message to
standard output that the button has been pressed. Similar callback
routines could be installed for the Cancel and Help
buttons through the XmNcancelCallback and XmNhelpCallback
resources.
You might notice that activating either the OK
or the Cancel button in the previous example causes the dialog
to be automatically popped down. The Motif Style Guide says that
when any button in the action area of a predefined Motif dialog is
pressed, except for the Help button, the dialog should be
dismissed. The Motif toolkit takes this specification at face value and
enforces the behavior, which is consistent with the idea that Motif
dialogs are self-contained, self-sufficient objects. They manage
everything about themselves from their displays to their interactions
with the user. And when it's time to go away, they unmanage themselves.
Your application does not have to do anything to cause any of the
behavior to occur.
Unfortunately, this behavior does not take into
account error conditions or other exceptional events that may not
necessarily justify the dialog's dismissal. For example, if pressing
OK causes a file to be updated, but the operation fails, you may
not want the dialog to be dismissed. If the dialog is still displayed,
the user can try again without having to repeat the actions that led to
popping up the dialog.
The XmNautoUnmanage resource provides a way
around the situation. This resource controls whether the dialog box is
automatically unmanaged when the user selects an action area button
other than the Help button. If XmNautoUnmanage is
True, after the callback routine for the button is invoked, the
DialogShell is popped down and the dialog widget is unmanaged
automatically. However, if the resource is set to False, the
dialog is not automatically unmanaged. The value of this resource
defaults to True for MessageDialogs and SelectionDialogs; it
defaults to False for FileSelectionDialogs.
Since it is not always appropriate for a dialog box
to unmanage itself automatically, it turns out to be easier to set
XmNautoUnmanage to False in most circumstances. This
technique makes dialog management easier, since it keeps the toolkit
from indiscriminately dismissing a dialog simply because an action
button has been activated. While it is true that we could program
around this situation by calling XtPopup() or
XtManageChild() from a callback routine in error conditions, this
type of activity is confusing because of the double-negative action it
implies. In other words, programming around the situation is just
undoing something that should not have been done in the first place.
This discussion brings up some issues about when a
dialog should be unmanaged and when it should be destroyed. If you
expect the user to have an abundant supply of computer memory, you may
reuse a dialog by retaining a handle to the dialog, as shown in Example
5-4 later in this chapter. There are also performance considerations
that may affect whether you choose to destroy or reuse dialogs. It
takes less time to reuse a dialog than it does to create a new one,
provided that your application is not so large that it is consuming all
of the system's resources. If you do not retain a handle to a dialog,
and if you need to conserve memory and other resources, you should
destroy the dialog whenever you pop it down.
Another method the user might use to close a dialog is to select the Close item from the window menu. This menu can be pulled down from the title bar of a window. Since the menu belongs to the window manager, rather than the shell widget or the application, you cannot install any callback routines for its menu items. However, you can use the XmNdeleteResponse resource to control how the DialogShell responds to a Close action. The Motif VendorShell, from which the DialogShell is subclassed, is responsible for trapping the notification and determining what to do next, based on the value of the resource. It can have one of the following values:
It may be convenient for your application to know
when a dialog has been popped up or down. If so, you can install
callbacks that are invoked whenever either of these events take place.
The actions of popping up and down dialogs can be monitored through the
XmNpopupCallback and XmNpopdownCallback callback
routines. For example, when the function associated with a
XmNpopupCallback is invoked, you could position the dialog
automatically, rather than allowing the window manager to control the
placement. See Chapter 7, Custom Dialogs, for more information
on these callbacks.
Posting dialogs that display informative messages is
something just about every application is going to do frequently.
Rather than write a separate routine for each case where a message
needs to be displayed, we can generalize the process by writing a
single routine that handles most, if not all, cases. the source code
shows the PostDialog() routine. This routine creates a
MessageDialog of a given type and displays an arbitrary message. Rather
than use the convenience functions provided by Motif for each of the
MessageDialog types, the routine uses the generic function
XmCreateMessageDialog() and configures the symbol to be displayed
by setting the XmNdialogType resource.
XmStringCreateLocalized() is only available in Motif 1.2;
XmStringCreateSimple() is the corresponding function in Motif 1.1.
/* * PostDialog() -- a generalized routine that allows the programmer * to specify a dialog type (message, information, error, help, etc..), * and the message to display. */ Widget PostDialog(parent, dialog_type, msg) Widget parent; int dialog_type; char *msg; { Widget dialog; XmString text; dialog = XmCreateMessageDialog (parent, "dialog", NULL, 0); text = XmStringCreateLocalized (msg); XtVaSetValues (dialog, XmNdialogType, dialog_type, XmNmessageString, text, NULL); XmStringFree (text); XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); return dialog; }This routine allows the programmer to specify several parameters: the parent widget, the type of dialog that is to be used, and the message that is to be displayed. The function returns the new dialog widget, so that the calling routine can modify it, unmanage it, or keep a handle to it. You may have additional requirements that this simplified example does not satisfy. For instance, the routine does not allow you to specify callback functions for the buttons in the action area and it does not handle the destruction of the widget when it is no longer needed. You could extend the routine to handle these issues, or you could control them outside the context of the function. You may also want to extend the routine so that it reuses the same dialog each time it is called and so that it allows you to disable the different action area buttons. All of these issues are discussed again in Chapter 6, Selection Dialogs, and in Chapter 21, Advanced Dialog Programming.
The following sections discuss resources that are
specific to Motif dialogs. In most cases, these resources are
BulletinBoard widget resources, since all Motif dialogs are subclassed
from this class. However, they are not intended to be used by generic
BulletinBoard widgets. The resources only apply when the widget is an
immediate child of a DialogShell widget; they are really intended to be
used exclusively by the predefined Motif dialog classes. Remember that
the resources must be set on the dialog widget, not the DialogShell.
See Chapter 8, Manager Widgets, for details on the generic
BulletinBoard resources.
All predefined Motif dialogs have a default
button in their action area. The default button is activated when
the user presses the RETURN key in the dialog. The OK button is
normally the default button, but once the dialog is displayed, the user
can change the default button by using the arrow keys to traverse the
action buttons. The action button with the keyboard focus is always the
default button. Since the default button can be changed by the user,
the button that is the default is only important when the dialog is
initially popped up. The importance of the default button lies in its
ability to influence the user's default response to the dialog.
You can change the default button for a MessageDialog by setting the XmNdefaultButtonType resource on the dialog widget. This resource is specific to MessageDialogs; it cannot be set for the various types of selection dialogs. The resource can have one of the following values:
The values for XmNdefaultButtonType come up
again later, when we discuss XmMessageBoxGetChild() and again
in Chapter 6, Selection Dialogs, for
XmSelectionBoxGetChild(). An example of how the default button type
can be used is shown in the source code XmStringCreateLocalized()
is only available in Motif 1.2; XmStringCreateSimple() is the
corresponding function in Motif 1.1.
/* * WarningMsg() -- Inform the user that she is about to embark on a * dangerous mission and give her the opportunity to back out. */ void WarningMsg(parent, client_data, call_data) Widget parent; XtPointer client_data; XtPointer call_data; { static Widget dialog; XmString text, ok_str, cancel_str; char *msg = (char *) client_data; if (!dialog) dialog = XmCreateWarningDialog (parent, "warning", NULL, 0); text = XmStringCreateLtoR (msg, XmFONTLIST_DEFAULT_TAG); ok_str = XmStringCreateLocalized ("Yes"); cancel_str = XmStringCreateLocalized ("No"); XtVaSetValues (dialog, XmNmessageString, text, XmNokLabelString, ok_str, XmNcancelLabelString, cancel_str, XmNdefaultButtonType, XmDIALOG_CANCEL_BUTTON, NULL); XmStringFree (text); XmStringFree (ok_str); XmStringFree (cancel_str); XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); }The intent of this function is to create a dialog that tries to discourage the user from performing a destructive action. By using a WarningDialog and by making the Cancel button the default choice, we have given the user adequate warning that the action may have dangerous consequences. The output of a program running this code fragment is shown in the figure.
You can also set the default button for a dialog by
the setting the BulletinBoard resource XmNdefaultButton. This
technique works for both MessageDialogs and SelectionDialogs. The
resource value must be a widget ID, which means that you have to get a
handle to a subwidget in the dialog to set the resource. You can get
the handle to subwidgets using XmMessageBoxGetChild() or
XmSelectionBoxGetChild(). Since this method breaks the Motif dialog
abstraction, we describe it later in Section #sinternwid.
When a dialog widget is popped up, one of the
internal widgets in the dialog has the keyboard focus. This widget is
typically the default button for the dialog, which makes sense in most
cases. However, there are situations where it is appropriate for
another widget to have the initial keyboard focus. For example, when a
PromptDialog is popped up, it makes sense for the TextField to have the
keyboard focus so that the user can immediately start typing a
response.
In Motif 1.1, it is not easy to set the initial
keyboard focus in a dialog widget to anything other than a button in
the action area. Motif 1.2 has introduced the XmNinitialFocus
resource to deal with this situation. Since this resource is a Manager
widget resource, it can be used for both MessageDialogs and
SelectionDialogs, although it is normally only used for
SelectionDialogs. The resource specifies the subwidget that has the
keyboard focus the first time that the dialog is popped up. If the
dialog is popped down and popped up again later, it remembers the
widget that had the keyboard focus when it was popped down and that
widget is given the keyboard focus again. The resource value must again
be a widget ID. The default value of XmNinitialFocus for
MessageDialogs is the subwidget that is also the XmNdefaultButton
for the dialog. For SelectionDialogs, the text entry area is the
default value for the resource.
The XmNminimizeButtons resource controls
how the dialog sets the widths of the action area buttons. If the
resource is set to True, the width of each button is set so
that it is as small as possible while still enclosing the entire label,
which means that each button will have a different width. The default
value of False specifies that the width of each button is set
to the width of the widest button, so that all buttons have the same
width.
When a new shell widget is mapped to the screen, the
window manager creates its own window that contains the title bar,
resize handles, and other window decorations and makes the window of
the DialogShell the child of this new window. This technique is called
reparenting a window; it is only done by the window manager in order to
add window decorations to a shell window. The window manager reparents
instances of all of the shell widget classes except OverrideShell.
These shells are used for menus and thus should not have window manager
decorations.
Most window managers that reparent shell windows
display titles in the title bars of their windows. For predefined Motif
dialogs, the Motif toolkit sets the default title to the name of the
dialog widget with the string _popup appended. Since this
string is almost certainly not an appropriate title for the window, you
can change the title explicitly using the XmNdialogTitle
BulletinBoard resource. (Do not confuse this title with the message
displayed in MessageDialog, which is set by XmNmessageString.)
The value for XmNdialogTitle must be a compound string. The
BulletinBoard in turn sets the XmNtitle resource of the
DialogShell; the value of this resource is a regular C string.
So, you can set the title for a dialog window in one
of two ways. The following code fragment shows how to set the title
using the XmNdialogTitle resource:
XmString title_string; title_string = XmStringCreateLocalized ("Dialog Box"); dialog = XmCreateMessageDialog (parent, "dialog_name", NULL, 0); XtVaSetValues (dialog, XmNdialogTitle, title_string, NULL); XmStringFree (title_string);This technique requires creating a compound string. If you set the XmNtitle resource directly on the DialogShell, you can use a regular C string, as in the following code fragment:
dialog = XmCreateMessageDialog (parent, "dialog_name", NULL, 0); XtVaSetValues (XtParent (dialog), XmNtitle, "Dialog Box", NULL);While the latter method is easier and does not require creating and freeing a compound string, it does break the abstraction of treating the dialog as a single entity.
The XmNnoResize resource controls whether
or not the window manager allows the dialog to be resized. If the
resource is set to True, the window manager does not display
resize handles in the window manager frame for the dialog. The default
value of False specifies that the window manager should
provide resize handles. Since some dialogs cannot handle resize events
very well, you may find it better aesthetically to prevent the user
from resizing them.
This resource is an attribute of the BulletinBoard
widget, even though it only affects the shell widget parent of a dialog
widget. The resource is provided as a convenience to the programmer, so
that she is not required to get a handle to the DialogShell. The
resource only affects the presence of resize handles in the window
manager frame; it does not deal with other window manager controls. See
Chapter 16, Interacting With the Window Manager, for details on
how to specify the window manager controls for a DialogShell, or any
shell widget, directly.
The BulletinBoard widget provides resources that
enable you to specify the fonts that are used for all of the button,
Label, and Text widget descendants of the BulletinBoard. Since Motif
dialog widgets are subclassed from the BulletinBoard, you can use these
resources to make sure that the fonts that are used within a dialog are
consistent. The XmNbuttonFontList resource specifies the font
list that is used for all of the button descendants of the dialog. The
resource is set on the dialog widget itself, not on its individual
children. Similarly, the XmNlabelFontList resource is used to
set the font list for all of the Label descendants of the dialog and
XmNtextFontList is used for all of the Text and TextField
descendants.
If one of these resources is not set, the toolkit
determines the font list by searching up the widget hierarchy for an
ancestor that is a subclass of BulletinBoard, VendorShell, or
MenuShell. If an ancestor is found, the font list resource is set to
the value of that font list resource in the ancestor widget. See
Chapter 19, Compound Strings, for more information on font
lists.
You can override the XmNbuttonFontList,
XmNlabelFontList, and XmNtextFontList resources on a
per-widget basis by setting the XmNfontList resource directly
on individual widgets. Of course, you must break the dialog abstraction
and retrieve the widgets internal to the dialog itself to set this
resource. While we describe how to do this in the following section, we
do not recommend configuring dialogs down to this level of detail.
As mentioned earlier, the predefined Motif dialogs
have their own resources to reference the labels and callback routines
for the action area PushButtons. Instead of accessing the PushButton
widgets in the action area to install callbacks, you use the resources
XmNokCallback, XmNcancelCallback, and XmNhelpCallback
on the dialog widget itself. These callbacks correspond to each of the
three buttons, OK, Cancel, and Help.
Installing callbacks for a dialog is no different
than installing them for any other type of Motif widget; it may just
seem different because the dialog widgets contain so many subwidgets.
The following code fragment demonstrates the installation of simple
callback for all of the buttons in a MessageDialog:
... dialog = XmCreateMessageDialog (w, "notice", NULL, 0); ... XtAddCallback (dialog, XmNokCallback, ok_pushed, "Hi"); XtAddCallback (dialog, XmNcancelCallback, cancel_pushed, "Foo"); XtAddCallback (dialog, XmNhelpCallback, help_pushed, NULL); XtManageChild (dialog); ... /* ok_pushed() --the OK button was selected. */ void ok_pushed(widget, client_data, call_data) Widget widget; XtPointer client_data;
XtPointer call_data; { char *message = (char *) client_data; printf ("OK was selected: %s0, message); } /* cancel_pushed() --the Cancel button was selected. */ void cancel_pushed(widget, client_data, call_data) Widget widget; XtPointer client_data;
XtPointer call_data; { char *message = (char *) client_data; printf ("Cancel was selected: %s0, message); } /* help_pushed() --the Help button was selected. */ void help_pushed(widget, client_data, call_data) Widget widget; XtPointer client_data;
XtPointer call_data; { printf ("Help was selected0);
} In this example, a dialog is created and callback routines for each
of the three responses are added using XtAddCallback(). We
also provide simple client data to demonstrate how the data is passed
to the callback routines. These callback routines simply print the fact
that they have been activated; the messages they print are taken from
the client data.
All of the dialog callback routines take three
parameters, just like any standard callback routine. The widget
parameter is the dialog widget that contains the button that was
selected; it is not the DialogShell widget or the PushButton that the
user selected from the action area. The second parameter is the
client_data, which is supplied to XtAddCallback(), and the
third is the call_data, which is provided by the internals of
the widget that invoked the callback.
The client_data parameter is of type
XtPointer, which means that you can pass arbitrary values to the
function, depending on what is necessary. However, you cannot pass a
float or a double value or an actual data structure. If
you need to pass such values, you must pass the address of the variable
or a pointer to the data structure. In keeping with the philosophy of
abstracting and generalizing code, you should use the client_data
parameter as much as possible because it eliminates the need for some
global variables and it keeps the structure of an application modular.
For the predefined Motif dialogs, the call_data
parameter is a pointer to a data structure that is filled in by the
dialog box when the callback is invoked. The data structure contains a
callback reason and the event that invoked the callback. The structure
is of type XmAnyCallbackStruct, which is declared as follows:
typedef struct { int reason; XEvent *event; } XmAnyCallbackStruct;The value of the reason field is an integer value that can be any one of XmCR_HELP, XmCR_OK, or XmCR_CANCEL . The value specifies the button that the user pressed in the dialog box. The values for the reason field remain the same, no matter how you change the button labels for a dialog. For example, you can change the label for the OK button to say Help, using the resource XmNokLabelString, but the reason parameter will still be XmCR_OK when the button is activated.
Because the reason field provides
information about the user's response to the dialog in terms of the
button that was pushed, we can simplify the previous code fragment and
use one callback function for all of the possible actions. The callback
function can determine which button was selected by examining
reason. the source code demonstrates this simplification.
XtSetLanguageProc() is only available in X11R5; there is no
corresponding function in X11R4. XmStringCreateLocalized() is
only available in Motif 1.2; XmStringCreateSimple() is the
corresponding function in Motif 1.1.
/* reason.c -- examine the reason field of the callback structure * passed as the call_data of the callback function. This field * indicates which action area button in the dialog was pressed. */ #include <Xm/RowColumn.h> #include <Xm/MessageB.h> #include <Xm/PushB.h> /* main() --create a pushbutton whose callback pops up a dialog box */ main(argc, argv) char *argv[]; { XtAppContext app; Widget toplevel, rc, pb; extern void pushed(); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); rc = XtVaCreateWidget ("rowcol", xmRowColumnWidgetClass, toplevel, NULL); pb = XtVaCreateManagedWidget ("Hello", xmPushButtonWidgetClass, rc, NULL); XtAddCallback (pb, XmNactivateCallback, pushed, "Hello World"); pb = XtVaCreateManagedWidget ("Goodbye", xmPushButtonWidgetClass, rc, NULL); XtAddCallback (pb, XmNactivateCallback, pushed, "Goodbye World"); XtManageChild (rc); XtRealizeWidget (toplevel); XtAppMainLoop (app); } /* pushed() --the callback routine for the main app's pushbuttons. * Create and popup a dialog box that has callback functions for * the OK, Cancel and Help buttons. */ void pushed(widget, client_data, call_data) Widget widget; XtPointer client_data; XtPointer call_data; { static Widget dialog; char *message = (char *) client_data; XmString t = XmStringCreateLocalized (message); /* See if we've already created this dialog -- if so, * we don't need to create it again. Just set the message * and manage it (repop it up). */ if (!dialog) { extern void callback(); Arg args[5]; int n = 0; XtSetArg (args[n], XmNautoUnmanage, False); n++; dialog = XmCreateMessageDialog (widget, "notice", args, n); XtAddCallback (dialog, XmNokCallback, callback, "Hi"); XtAddCallback (dialog, XmNcancelCallback, callback, "Foo"); XtAddCallback (dialog, XmNhelpCallback, callback, "Bar"); } XtVaSetValues (dialog, XmNmessageString, t, NULL); XmStringFree (t); XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); } /* callback() --One of the dialog buttons was selected. * Determine which one by examining the "reason" parameter. */ void callback(widget, client_data, call_data) Widget widget; XtPointer client_data; XtPointer call_data; { char *button; char *message = (char *) client_data; XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) call_data; switch (cbs->reason) { case XmCR_OK : button = "OK"; break; case XmCR_CANCEL : button = "Cancel"; break; case XmCR_HELP : button = "Help"; } printf ("%s was selected: %s0, button, message); if (cbs->reason != XmCR_HELP) { /* the ok and cancel buttons "close" the widget */ XtPopdown (XtParent (widget)); } }Another interesting change in this application is the way pushed() determines if the dialog has already been created. By making the dialog widget handle static to the pushed() callback function, we retain a handle to this object across multiple button presses. For each invocation of the callback, the dialog's message is reset and it is popped up again.
Considering style guide issues again, it is
important to know when it is appropriate to dismiss a dialog. As noted
earlier, the toolkit automatically unmanages a dialog whenever any of
the action area buttons are activated, except for the Help
button. This behavior is controlled by XmNautoUnmanage, which
defaults to True. However, if you set this resource to
False, the callback routines for the buttons in the action area
have to control the behavior on their own. In the source code the
callback routine pops down the dialog when the reason is XmCR_OK
or XmCR_CANCEL, but not when it is XmCR_HELP.
As described earlier, Motif treats dialogs as if
they are single user-interface objects. However, there are times when
you need to break this abstraction and work with some of the individual
widgets that make up a dialog. This section describes how the dialog
convenience routines work, how to work directly with the DialogShell,
and how to access the widgets that are internal to dialogs.
The fact that Motif dialogs are self-sufficient does
not imply that they are black boxes that perform magic that you cannot
perform yourself. For example, the convenience routines for the
MessageDialog types follow these basic steps:
XmDIALOG_ERROR XmDIALOG_INFORMATION XmDIALOG_MESSAGE XmDIALOG_QUESTION XmDIALOG_TEMPLATE XmDIALOG_WARNING XmDIALOG_WORKINGThe type of the dialog does not affect the kind of widget that is created. The only thing the type affects is the graphical symbol that is displayed in the control area of the dialog. The convenience routines set the resource based on the routine that is called (e.g. XmCreateErrorDialog() sets the resource to XmDIALOG_ERROR ). The widget automatically sets the graphical symbol based on the dialog type. You can change the type of a dialog after it is created using XtVaSetValues(); modifying the type also changes the dialog symbol that is displayed.
The Motif dialog convenience routines create
DialogShells internally to support the single-object dialog
abstraction. With these routines, the toolkit is responsible for the
DialogShell, so the dialog widget uses its XmNdestroyCallback
to destroy its parent upon its own destruction. If the dialog is
unmapped or unmanaged, so is its DialogShell parent. The convenience
routines do not add any resources or call any functions to support the
special relationship between the dialog widget and the DialogShell,
since most of the code that handles the interaction is written into the
internals of the BulletinBoard.
As your programs become more complex, you may
eventually have to access the DialogShell parent of a dialog widget in
order to get certain things done. This section examines DialogShells as
independent widgets and describes how they are different from other
shell widgets. There are three main features of a DialogShell that
differentiate it from an ApplicationShell and a TopLevelShell.
The parent-child relationship between a DialogShell
and its parent is different from the classic case, where the parent
actually contains the child within its geometrical bounds. The
DialogShell widget is a popup child of its parent, which means that the
usual geometry-management relationship does not apply. Nonetheless, the
parent widget must be managed in order for the child to be displayed.
If a widget has popup children, those children are not mapped to the
screen if the parent is not managed, which means that you must never
make a menu item the parent of a DialogShell.
Assuming that the parent is displayed, the window
manager attempts to place the DialogShell based on the value of the
XmNdefaultPosition BulletinBoard resource. The default value of
this resource is True, which means that the window manager
positions the DialogShell so that it is centered on top of its parent.
If the resource is set to False, the application and the
window manager negotiate about where the dialog is placed. This
resource is only relevant when the BulletinBoard is the immediate child
of a DialogShell, which is always the case for Motif dialogs. If you
want, you can position the dialog by setting the XmNx and
XmNy resources for the dialog widget. Positioning the dialog on the
screen must be done through a XmNmapCallback routine, which is
called whenever the application calls XtManageChild(). See
Chapter 7, Custom Dialogs, for a discussion about dialog
positioning.
The Motif Window Manager imposes an additional
constraint on the stacking order of the DialogShell and its parent.
mwm always forces the DialogShell to be directly on top of its
parent in the stacking order. The result is that the shell that
contains the widget acting as the parent of the DialogShell cannot be
placed on top of the dialog. This behavior is defined by the Motif
Style Guide and is enforced by the Motif Window Manager and the
Motif toolkit. Many end-users have been known to report the behavior as
an application-design bug, so you may want to describe this behavior
explicitly in the documentation for your application, in order to
prepare the user ahead of time.
Internally, DialogShell widgets communicate
frequently with dialog widgets in order to support the single-entity
abstraction promoted by the Motif toolkit. However, you may find that
you need to access the DialogShell part of a Motif dialog in order to
query information from the shell or to perform certain actions on it.
The include file <Xm/DialogS.h> provides a convenient macro for
identifying whether or not a particular widget is a DialogShell:
#define XmIsDialogShell(w) XtIsSubclass(w, xmDialogShellWidgetClass)If you need to use this macro, or you want to create a DialogShell using XmCreateDialogShell(), you need to include < Xm/DialogS.h>.
The macro is useful if you want to determine whether
or not a dialog widget is the direct child of a DialogShell. For
example, earlier in this chapter, we mentioned that the Motif Style
Guide suggests that if the user activates the OK button in a
MessageDialog, the entire dialog should be popped down. If you have
created a MessageDialog without using XmCreateMessageDialog()
and you want to be sure that the same thing happens when the user
presses the OK button in that dialog, you need to test whether
or not the parent is a DialogShell before you pop down the dialog. The
following code fragment shows the use of the macro in this type of
situation:
/* traverse up widget tree till we find a window manager shell */ Widget GetTopShell(widget) Widget widget; { while (widget && !XmIsWMShell (widget)) widget = XtParent (widget)); return widget; } void ok_callback(dialog, client_data, call_data) Widget dialog; XtPointer client_data; XtPointer call_data; { /* do whatever the callback needs to do ... */
/* if immediate parent is not a DialogShell, mimic
the same * behavior as if it were (i.e., pop down the parent.) */ if
(!XmIsDialogShell (XtParent (dialog))) XtPopdown (GetTopShell
(dialog)); } The Motif toolkit defines similar macros for all of its
widget classes. For example, <Xm/MessageB.h> defines the macro
XmIsMessageBox():
#define XmIsMessageBox(w) XtIsSubclass (w, xmMessageBoxWidgetClass)This macro determines whether or not a particular widget is subclassed from the MessageBox widget class. Since all of the MessageDialogs are really instances of the MessageBox class, the macro covers all of the different types of MessageDialogs. If the widget is a MessageBox, the macro returns True whether or not the widget is an immediate child of a DialogShell. Note that this macro does not return True if the widget is a DialogShell.
All of the Motif dialog widgets are composed of
primitive subwidgets such as Labels, PushButtons, and TextField
widgets. For most tasks, it is possible to treat a dialog as a single
entity. However, there are some situations when it is useful to be able
to get a handle to the widgets internal to the dialog. For example, one
way to set the default button for a dialog is to use the
XmNdefaultButton resource. The value that you specify for this
resource must be a widget ID, so this is one of those times when it is
necessary to get a handle to the actual subwidgets contained within a
dialog.
The Motif toolkit provides routines that allow you
to access the internal widgets. For MessageDialogs, you can retrieve
the subwidgets using XmMessageBoxGetChild(), which has the
following form:
Widget XmMessageBoxGetChild(widget, child) Widget widget; unsigned char child;The widget parameter is a handle to a dialog widget, not its DialogShell parent. The child parameter is an enumerated value that specifies a particular subwidget in the dialog. The parameter can have any one of the following values:
XmDIALOG_OK_BUTTON XmDIALOG_CANCEL_BUTTON XmDIALOG_HELP_BUTTON XmDIALOG_DEFAULT_BUTTON XmDIALOG_MESSAGE_LABEL XmDIALOG_SEPARATOR XmDIALOG_SYMBOL_LABELThe values refer to the different widgets in a MessageDialog and they should be self-explanatory. For SelectionDialogs, the toolkit provides the XmSelectionBoxGetChild() routine. This routine is identical to XmMessageBoxGetChild(), except that it takes different values for the different widgets in a SelectionDialog. The routine is discussed in Chapter 6, Selection Dialogs.
One method that you can use to customize the
predefined Motif dialogs is to unmanage the subwidgets that are
inappropriate for your purposes. To get the widget ID for a widget, so
that you can pass it to XtUnmanageChild(), you need to call
XmMessageBoxGetChild(). You can also use this routine to get a
handle to a widget that you want to temporarily disable. These
techniques are demonstrated in the following code fragment:
text = XmStringCreateLocalized ("You have new mail."); XtSetArg (args[0], XmNmessageString, text); dialog = XmCreateInformationDialog (parent, "message", args, 1); XmStringFree (text); XtSetSensitive ( XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON), False); XtUnmanageChild ( XmMessageBoxGetChild (dialog, XmDIALOG_CANCEL_BUTTON));The output of a program using this code fragment is shown in the figure.
Since the message in this dialog is so simple, it
does not make sense to have both an OK and a Cancel
button, so we unmanage the latter. On the other hand, it does make
sense to have a Help button. However, there is currently no help
available, so we make the button unselectable by desensitizing it using
XtSetSensitive().
The concept of forcing the user to respond to a
dialog is known as modality. Modality governs whether or not the
user can interact with other windows on the desktop while a particular
dialog is active. Dialogs are either modal or modeless. There are three
levels of modality: primary application modal, full application modal,
and system modal. In all cases, the user must interact with a modal
dialog before control is released and normal input is resumed. In a
system modal dialog, the user is prevented from interacting with any
other window on the display. Full application modal dialogs allow the
user to interact with any window on the desktop except those that are
part of the same application as the modal window. Primary application
modal dialogs allow the user to interact with any other window on the
display except for the window that is acting as the parent for this
particular dialog.
For example, if the user selected an action that
caused an error dialog to be displayed, the dialog could be primary
application modal, so that the user would have to acknowledge the error
before she interacts with the same window again. This type of modality
does not restrict her ability to interact with another window in the
same application, provided that the other window is not the one acting
as the parent for the modal dialog.
Modal dialogs are perhaps the most frequently
misused feature of a graphical user interface. Programmers who fail to
grasp the concept of event-driven programming and design, whereby the
user is in control, often fall into the convenient escape route that
modal dialogs provide. This problem is difficult to detect, let alone
cure, because there are just as many right ways to invoke modal dialogs
as there are wrong ways. Modality should be used in moderation, but it
should also be used consistently. Let's examine a common scenario. Note
that this example does not necessarily favor using modal dialogs; it is
presented as a reference point for the types of things that people are
used to doing in tty-based programs.
A text editor has a function that allows the user to
save its text to a file. In order to save the text, the program needs a
filename. Once it has a filename, the program needs to check that the
user has sufficient permission to open or create the file and it also
needs to see if there is already some text in the file. If an error
condition occurs, the program needs to notify the user of the error,
ask for a new filename, or get permission to overwrite the file's
contents. Whatever the case, some interaction with the user is
necessary in order to proceed. If this were a typical terminal-based
application, the program flow would be similar to that in the following
code fragment:
FILE *fp; char buf[BUFSIZ], file[BUFSIZ]; extern char *index(); printf ("What file would you like to use? "); if (!(fgets (file, sizeof file, stdin)) || file[0] == 0) { puts ("Cancelled."); return; } *(index (file, '0)) = 0; /* get rid of newline terminator */ /* "a+" creates file if it doesn't exist */ if (!(fp = fopen (file, "a+"))) { perror (file); return; } if (ftell (fp) > 0) { /* There's junk in the file already */ printf ("Overwrite contents of %s? ", file); buf[0] = 0; if (!(fgets (buf, sizeof buf, stdin)) || buf[0] == 0 || buf[0] == 'n' || buf[0] == 'N') { puts ("Cancelled."); fclose (fp); return; } } rewind (fp);This style of program flow is still possible with a graphical user interface system using modal dialogs. In fact, the style is frequently used by engineers who are trying to port tty-based applications to Motif. It is also a logical approach to programming, since it does one task followed by another, asking only for information that it needs when it needs it.
However, in an event-driven environment, where the
user can interact with many different parts of the program
simultaneously, displaying a series of modal dialogs is not the best
way to handle input and frequently it's just plain wrong as a design
approach. You must adopt a new paradigm in interface design that
conforms to the capabilities of the window system and meets the
expectations of the user. It is essential that you understand the
event-driven model if you want to create well-written, easy-to-use
applications.
Window-based applications should be modeled on the
behavior of a person filling out a form, such as an employment
application or a medical questionnaire. Under this scenario, you are
given a form asking various questions. You take it to your seat and
fill it out however you choose. If it asks for your license number, you
can get out your driver's license and copy down the number. If it asks
for your checking account number, you can examine your checkbook for
that information. The order in which you fill out the application is
entirely up to you. You are free to examine the entire form and fill
out whatever portions you like, in whatever order you like.
When the form is complete, you return it to the
person who gave it to you. The attendant can check it over to see if
you forgot something. If there are errors, you typically take it back
and continue until it's right. The attendant can simply ask you the
question straight out and write down whatever you say, but this
prevents him from doing other work or dealing with other people.
Furthermore, if you don't know the answer to the question right away,
then you have to take the form back and fill it out the way you were
doing it before. No matter how you look at it, this process is not an
interview where you are asked questions in sequence and must answer
them that way. You are supposed to prepare the form off-line, without
requiring interaction from anyone else.
Window-based applications should be treated no
differently. Each window, or dialog, can be considered to be a form of
some sort. Allow the user to fill out the form at her own convenience
and however she chooses. If she wants to interact with other parts of
the application or other programs on the desktop, she should be allowed
to do so. When the user selects one of the buttons in the action area,
this action is her way of returning the form. At this time, you may
either accept it or reject it. At no point in the process so far have
we needed a modal dialog.
Once the form has been submitted, you can take
whatever action is appropriate. If there are errors in any section of
the dialog, you may need to notify the user of the error. Here is where
a modal dialog can be used legitimately. For example, if the user is
using a FileSelectionDialog to specify the file she wants to read and
the file is unreadable, then you must notify her so that she can make
another selection. In this case, the notification is usually in the
form of an ErrorDialog, with a message that explains the error and an
OK button. The user can read the message and press the button to
acknowledge the error.
It is often difficult to judge what types of
questions or how much information is appropriate in modal dialogs. The
rule of thumb is that questions in modal dialogs should be limited to
simple, yes/no questions. You should not prompt for any information
that is already available through an existing dialog, but instead bring
up that dialog and instruct the user to provide the necessary
information there. You should also avoid posting modal dialogs that
prompt for a filename or anything else that requires typing. You should
be requesting this type of information through the text fields of
modeless dialog boxes.
As for the issue of forcing the user to fill out
forms in a particular order, it may be perfectly reasonable to require
this type of interaction. You should implement these restrictions by
managing and unmanaging separate dialogs, rather than by using modal
dialogs to prevent interaction with all but a single dialog.
All of these admonitions are not to suggest that
modal dialogs are rare or that you should avoid using them at all
costs. On the contrary, they are extremely useful in certain
situations, are quite common, and are used in a wide variety of
ways--even those that we might not recommend. We have presented all of
these warnings because modal dialogs are frequently misused and
programs that use fewer of them are usually better than those that use
more of them. Modal dialogs interrupt the user and disrupt the flow of
work in an application. There is no sanity checking to prevent you from
misusing dialogs so it is up to you to keep the use of modal dialogs to
a minimum.
Once you have determined that you need to implement
a modal dialog, you can use the XmNdialogStyle resource to
set the modality of the dialog. This resource is defined by the
BulletinBoard widget class; it is only relevant when the widget is an
immediate child of a DialogShell. The resource can be set to one of the
following values: The value XmDIALOG_APPLICATION_MODAL is used
for backwards compatibility with Motif 1.0; it is defined to be the
same as XmDIALOG_PRIMARY_APPLICATION_MODAL.
XmDIALOG_MODELESS XmDIALOG_PRIMARY_APPLICATION_MODAL XmDIALOG_FULL_APPLICATION_MODAL XmDIALOG_SYSTEM_MODALXmDIALOG_MODELESS is the default value for the resource, so unless you change the value any dialog that you create will be modeless.
When you use one of the modal values, the user has
no choice but to respond to your dialog box before continuing to
interact with the application. If you use modality at all, you should
probably avoid using XmDIALOG_SYSTEM_MODAL, since it is rarely
necessary to restrict the user from interacting with all of the other
applications on the desktop. This style of modality is typically
reserved for system-level interactions. Under the Motif Window Manager,
when a system modal dialog is popped up, if the user moves the mouse
outside of the modal dialog, the cursor turns into the international
"do not enter" symbol. Attempts to interact with other windows cause
the server to beep.
the source code shows a sample program that displays
a dialog box that the user must reply to before continuing to interact
with the application. XtSetLanguageProc() is only available in
X11R5; there is no corresponding function in X11R4.
XmStringCreateLocalized() is only available in Motif 1.2;
XmStringCreateSimple() is the corresponding function in Motif 1.1.
/* modal.c -- demonstrate modal dialogs. Display two pushbuttons * each activating a modal dialog. */ #include <Xm/RowColumn.h> #include <Xm/MessageB.h> #include <Xm/PushB.h> /* main() --create a pushbutton whose callback pops up a dialog box */ main(argc, argv) char *argv[]; { XtAppContext app; Widget toplevel, button, rowcolumn; void pushed(); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); rowcolumn = XtCreateManagedWidget ("rowcolumn", xmRowColumnWidgetClass, toplevel, NULL, 0); button = XtCreateManagedWidget ("Application Modal", xmPushButtonWidgetClass, rowcolumn, NULL, 0); XtAddCallback (button, XmNactivateCallback, pushed, XmDIALOG_FULL_APPLICATION_MODAL); button = XtCreateManagedWidget ("System Modal", xmPushButtonWidgetClass, rowcolumn, NULL, 0); XtAddCallback (button, XmNactivateCallback, pushed, XmDIALOG_SYSTEM_MODAL); XtRealizeWidget (toplevel); XtAppMainLoop (app); } /* pushed() --the callback routine for the main app's pushbutton. * Create either a full-application or system modal dialog box. */ void pushed(widget, client_data, call_data) Widget widget; XtPointer client_data; XtPointer call_data; { static Widget dialog; XmString t; extern void dlg_callback(); unsigned char modality = (unsigned char) client_data; /* See if we've already created this dialog -- if so, * we don't need to create it again. Just re-pop it up. */ if (!dialog) { Arg args[5]; int n = 0; XmString ok = XmStringCreateLocalized ("OK"); XtSetArg(args[n], XmNautoUnmanage, False); n++; XtSetArg(args[n], XmNcancelLabelString, ok); n++; dialog = XmCreateInformationDialog (widget, "notice", args, n); XtAddCallback (dialog, XmNcancelCallback, dlg_callback, NULL); XtUnmanageChild ( XmMessageBoxGetChild (dialog, XmDIALOG_OK_BUTTON)); XtUnmanageChild ( XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON)); } t = XmStringCreateLocalized ("You must reply to this message now!"); XtVaSetValues (dialog, XmNmessageString, t, XmNdialogStyle, modality, NULL); XmStringFree (t); XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); } void dlg_callback(dialog, client_data, call_data) Widget dialog; XtPointer client_data; XtPointer call_data; { XtPopdown (XtParent (dialog)); }The output of this program is shown in the figure.
This program demonstrates both application modal and
system modal dialogs. The value for the XmNdialogType resource
is passed as client data to the callback routine that posts the dialog.
In the source code once the dialog is posted, the
function returns so that XtAppMainLoop() can continue to
process the events. If the function does not return, the application
will not respond to user events and, for that matter, the dialog will
not even be displayed. Just because a dialog is realized and managed
does not mean that it is displayed on the screen, as events must be
processed in order for it to appear. See Chapter 21, Advanced Dialog
Programming, for a discussion of this phenomenon. (See Volume One,
Xlib Programming Manual, for more information on event processing.)
However, there are situations where it would be nice
not to have to return from the function and break its flow of control.
As an example, consider a function that allows the user to perform a
particularly dangerous action, such as removing or overwriting a file.
What you'd like to do is prompt the user first and allow her to
reconsider the action before proceeding. If she confirms the action,
you'd like to continue from within the same function without having to
return in order to process events.
In order to write this type of function, we need to
find a way to process the events that display and manage the dialog
without returning to the main loop. The user also needs to be able to
respond to the dialog, so we really need to allow normal event
processing to continue in the context of the function. Let's assume
that there is a hypothetical function, AskUser(), that we can
use in the following way:
if (AskUser ("Are you sure you want to do this?") == YES) { /* proceed with action... */ }The function AskUser() should post a full application modal MessageDialog, wait for the user to respond to the dialog, and return a predefined value for either YES or NO. The magic of the function is to get around the requirement that events can only be read and processed directly from XtAppMainLoop(). The code for such a function is shown in the source code XmStringCreateLocalized() is only available in Motif 1.2; XmStringCreateSimple() is the corresponding function in Motif 1.1.
#define YES 1 #define NO 2 /* * AskUser() -- a generalized routine that asks the user a question * and returns the Yes/No response. */ int AskUser(parent, question) Widget parent; char *question; { static Widget dialog; XmString text, yes, no; static int answer; extern void response(); extern XtAppContext app; if (!dialog) { dialog = XmCreateQuestionDialog (parent, "dialog", NULL, 0); yes = XmStringCreateLocalized ("Yes"); no = XmStringCreateLocalized ("No"); XtVaSetValues (dialog, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, XmNokLabelString, yes, XmNcancelLabelString, no, NULL); XtSetSensitive ( XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON), False); XtAddCallback (dialog, XmNokCallback, response, &answer); XtAddCallback (dialog, XmNcancelCallback, response, &answer); XmStringFree (yes); XmStringFree (no); } answer = 0; text = XmStringCreateLocalized (question); XtVaSetValues (dialog, XmNmessageString, text, NULL); XmStringFree (text); XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); /* while the user hasn't provided an answer, simulate main loop. * The answer changes as soon as the user selects one of the * buttons and the callback routine changes its value. */ while (answer == 0) XtAppProcessEvent (app, XtIMAll); XtPopdown (XtParent (dialog)); return answer; } /* response() --The user made some sort of response to the * question posed in AskUser(). Set the answer (client_data) * accordingly and destroy the dialog. */ void response(widget, client_data, call_data) Widget widget; XtPointer client_data; XtPointer call_data; { int *answer = (int *) client_data; XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) call_data; switch (cbs->reason) { case XmCR_OK: *answer = YES; break; case XmCR_CANCEL: *answer = NO; break; default: return; } }The first parameter to the function is the widget that acts as the parent of the new dialog. It is important to choose this widget wisely. The parent widget must not be a gadget or an unrealized widget; it should be a widget that is currently mapped to the screen. Widgets that are menu items are not good candidates, since they are not mapped to the screen for very long. The top-level shell widget of the widget that caused the callback function to be invoked is typically a good choice. The second parameter is the string that is displayed in the dialog.
The routine is intended to be used to display a
dialog that asks a Yes/No question, so we change the OK and
Cancel labels to say Yes and No, respectively. The
routine creates a QuestionDialog as a static Widget, which
allows us to reuse the dialog, rather than create it each time the
function is called. This technique may improve performance on some
machines. The modality of the dialog and the labels for the PushButtons
in the action area are set at creation time, but the actual message
string is set each time that the function is called, since the message
can change. When we install the callback routines for the buttons, we
use the address of the answer variable as the client data. As
a result, when the user responds to the question by selecting the
Yes or No button, the callback routine has access to the
variable and can change its value accordingly.
The while loop is where the application
waits for the user to make a selection. The loop exits when the
variable answer is changed from its initial value (0) to
either YES (1) or NO (2) by the callback routine. By
using XtAppProcessEvent(), we have effectively reproduced the
XtAppMainLoop() function that is used in the main application.
Rather than returning to that level and breaking our flow of control,
we have introduced a miniature main loop in the function itself.
While the AskUser() routine in the source
code is useful as it is written, there are a number of enhancements
that will make it even more useful. By using what we've learned in this
chapter, we can come up with a simple, yet extremely robust interface
for prompting the user for responses to questions without breaking the
natural flow of control in the application. the source code
demonstrates a generalized version of AskUser() in a complete
application. The program ask_user.c allows the user to execute
UNIX commands that create and remove a temporary file.
XtSetLanguageProc() is only available in X11R5; there is no
corresponding function in X11R4. XmStringCreateLocalized() is
only available in Motif 1.2; XmStringCreateSimple() is the
corresponding function in Motif 1.1.
/* ask_user.c -- the user is presented with two pushbuttons. * The first creates a file (/tmp/foo) and the second removes it. * In each case, a dialog pops up asking for verification of the action. * * This program is intended to demonstrate an advanced implementation * of the AskUser() function. This time, the function is passed the * strings to use for the OK button and the Cancel button as well as * the button to use as the default value. */ #include <Xm/DialogS.h> #include <Xm/SelectioB.h> #include <Xm/RowColumn.h> #include <Xm/MessageB.h> #include <Xm/PushB.h> #define YES 1 #define NO 2 /* Generalize the question/answer process by creating a data structure * that has the necessary labels, questions and everything needed to * execute a command. */ typedef struct { char *label; /* label for pushbutton used to invoke cmd */ char *question; /* question for dialog box to confirm cmd */ char *yes; /* what the "OK" button says */ char *no; /* what the "Cancel" button says */ int dflt; /* which should be the default answer */ char *cmd; /* actual command to execute (using system()) */ } QandA; QandA touch_foo = { "Create", "Create /tmp/foo?", "Yes", "No", YES, "touch /tmp/foo" }; QandA rm_foo = { "Remove", "Remove /tmp/foo?", "Yes", "No", NO, "rm /tmp/foo" }; XtAppContext app; main(argc, argv) int argc; char *argv[]; { Widget toplevel, button, rowcolumn; XmString label; void pushed(); XtSetLanguageProc (NULL, NULL, NULL); toplevel = XtVaAppInitialize (&app, "Demos", NULL, 0, &argc, argv, NULL, NULL); rowcolumn = XtVaCreateManagedWidget ("rowcolumn", xmRowColumnWidgetClass, toplevel, NULL); label = XmStringCreateLocalized (touch_foo.label); button = XtVaCreateManagedWidget ("button", xmPushButtonWidgetClass, rowcolumn, XmNlabelString, label, NULL); XtAddCallback (button, XmNactivateCallback, pushed, &touch_foo); XmStringFree (label); label = XmStringCreateLocalized (rm_foo.label); button = XtVaCreateManagedWidget ("button", xmPushButtonWidgetClass, rowcolumn, XmNlabelString, label, NULL); XtAddCallback (button, XmNactivateCallback, pushed, &rm_foo); XmStringFree (label); XtManageChild (rowcolumn); XtRealizeWidget (toplevel); XtAppMainLoop (app); } /* pushed() --when a button is pressed, ask the question described * by the QandA parameter (client_data). Execute the cmd if YES. */ void pushed(widget, client_data, call_data) Widget widget; XtPointer client_data; XtPointer call_data; { QandA *quest = (QandA *) client_data; if (AskUser (widget, quest->question, quest->yes, quest->no, quest->dflt) == YES) { printf ("Executing: %s0, quest->cmd); system (quest->cmd); } else printf ("Not executing: %s0, quest->cmd); } /* * AskUser() -- a generalized routine that asks the user a question * and returns a response. Parameters are: the question, the labels * for the "Yes" and "No" buttons, and the default selection to use. */ AskUser(parent, question, ans1, ans2, default_ans) Widget parent; char *question, *ans1, *ans2; int default_ans; { static Widget dialog; /* static to avoid multiple creation */ XmString text, yes, no; static int answer; extern void response(); if (!dialog) { dialog = XmCreateQuestionDialog (parent, "dialog", NULL, 0); XtVaSetValues (dialog, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, NULL); XtSetSensitive ( XmMessageBoxGetChild (dialog, XmDIALOG_HELP_BUTTON), False); XtAddCallback (dialog, XmNokCallback, response, &answer); XtAddCallback (dialog, XmNcancelCallback, response, &answer); } answer = 0; text = XmStringCreateLocalized (question); yes = XmStringCreateLocalized (ans1); no = XmStringCreateLocalized (ans2); XtVaSetValues (dialog, XmNmessageString, text, XmNokLabelString, yes, XmNcancelLabelString, no, XmNdefaultButtonType, default_ans == YES ? XmDIALOG_OK_BUTTON : XmDIALOG_CANCEL_BUTTON, NULL); XmStringFree (text); XmStringFree (yes); XmStringFree (no); XtManageChild (dialog); XtPopup (XtParent (dialog), XtGrabNone); while (answer == 0) XtAppProcessEvent (app, XtIMAll); XtPopdown (XtParent (dialog)); /* make sure the dialog goes away before returning. Sync with server * and update the display. */ XSync (XtDisplay (dialog), 0); XmUpdateDisplay (parent); return answer; } /* response() --The user made some sort of response to the * question posed in AskUser(). Set the answer (client_data) * accordingly. */ void response(widget, client_data, call_data) Widget widget; XtPointer client_data; XtPointer call_data; { int *answer = (int *) client_data; XmAnyCallbackStruct *cbs = (XmAnyCallbackStruct *) call_data; if (cbs->reason == XmCR_OK) *answer = YES; else if (cbs->reason == XmCR_CANCEL) *answer = NO; }The new version of AskUser() is more dynamic than before, since more of the dialog is configurable upon each invocation of the function. The routine now allows you to specify the message, the labels for the OK and Cancel buttons, and the default button for the dialog. The flexibility of the routine is achieved at the cost of a few more lines of source code and additional parameters to the function. The performance of the function is completely unaffected.
One case that the new version of AskUser()
does not deal with is the need for additional buttons in the action
area of the dialog. For example, what if you need to provide a
Cancel button in addition to the Yes and No answers?
Let's say that the user has selected the Quit menu item in a
text editor application. Since the user has yet to update the changes
to the file that she has been editing, the application posts a dialog
that asks her if she wants to update her changes before exiting. There
are three possible responses:
However, this solution does not work if you want to
provide help in addition to these choices. The default MessageDialog
only provides three buttons in the action area, although in Motif 1.2
you can add additional action area buttons to the dialog. For more
information on how to handle this situation, see Chapter 7, Custom
Dialogs.
Dialogs are used extensively in all window-oriented
applications and their uses are quite diverse. As a result, it is
impossible to provide numerous examples of the use of any one
particular style of dialog. This chapter introduced the implementation
of Motif dialogs by using the predefined MessageDialogs as examples. We
described how to create the dialogs, how to set various dialog
resources, how to handle dialog callback routines, and how to implement
modal dialogs. Although our examples used MessageDialogs, much of the
discussion is applicable to other types of Motif dialogs.
The next chapter deals with the predefined Motif
selection dialogs. These dialogs allow you to provide the user with a
group of choices from which to make a selection. Chapter 7, Custom
Dialogs, discusses how you can break away from the predefined Motif
dialogs and build dialogs on your own. Chapter 21, Advanced Dialog
Programming, gets into advanced topics in Xt and Motif programming,
using various types of MessageDialogs as examples.