Sometimes you need to prevent interaction with the rest of
your application while the user manipulates a dialog.
Dialogs that freeze the rest of the application in this way
are called modal dialogs.
There is a lot of debate about when to use modal dialogs;
some users hate them passionately, but there are times when
they are necessary. Unfortunately, it is a little bit
easier to write code using modal dialogs, because you can
stop in the middle of a function, wait for a user response,
then continue. With nonmodal dialogs, you have to return
the flow of control to the main application, and arrange
callbacks to pick up where you left off when the user
finally deals with the dialog. With a complex sequence of
dialogs, the result is ugly spaghetti code. This tempts
many programmers to use modal dialogs all the time, or at
least a little too often. Avoid the temptation, and your
users will thank you.
Avoid modal dialogs if users might want to refer back to
information in the main application as they use the dialog,
or if they might want to cut-and-paste between the
application and the dialog. "Properties" dialogs should
usually be nonmodal; because users will want to experiment
with the effects of the changes they make, without having
to close the dialog. And there's no reason to make trivial
message boxes modal, since clicking on them has no effect
on the rest of the application.
Do not be afraid to use a modal dialog if it makes sense,
however. For example, I wrote a frontend for the Debian
package manager, called
gnome-apt. The main application allows the user to
select software packages for installation and removal; then
there are a series of dialogs which ask for confirmation
and perform the requested changes. These dialogs are modal,
because it would make no sense to change a request in the
middle of performing it. Changing the request should imply
restarting the request-fulfillment process and asking for
confirmation a second time. Another example: the "File
Properties" dialog for the Gnome file manager is modal,
because otherwise the user could delete the file while its
properties were being edited---a strange situation. There
are no hard and fast rules; you'll have to use your
judgment on a dialog-by-dialog basis.
All that said, it is very easy to create a modal dialog. In
GTK+, any window can be made modal with gtk_window_set_modal() (Figure 5).
Since GnomeDialog is a GtkWindow subclass, this function
works fine. It simply blocks all interaction with windows
other than the modal one.
Typically you want to go a step further, and wait for the
user to click one of the dialog buttons without setting up
a lot of callbacks. In GTK+ this is done by running a
second instance of gtk_main(),
entering another, nested event loop. When the second loop
exits, the flow of control returns to just after your gtk_main() call. However there are a
host of complications and race conditions, due to the large
number of ways to close a dialog; the resulting code is
somewhat unpleasant and error-prone. The two functions in
Figure 6
are provided to save your from the mess.
These two functions block until the user clicks a dialog
button, clicks the window manager's close decoration, or
does the equivalent with a key shortcut. If a button was
clicked, they return that button's number; recall that GnomeDialog buttons are numbered
from left to right starting with
0. If no button was clicked (the dialog was closed
via window manager), they return
-1 instead.
The dialog is automatically made modal for the duration of
the call; otherwise chaos would reign. (For example,
calling gtk_main_quit() from your
main application code would quit the nested gtk_main() rather than the primary one.)
However, if you plan to leave the dialog open after gnome_dialog_run() returns, and you
want it to be modal, you should manually make it modal; gnome_dialog_run() will only change
the dialog's modality temporarily.
It is your responsibility to figure out how the dialog will
be closed or destroyed before you call gnome_dialog_run(). You can set the dialog
up so that no user actions destroy it, then destroy it
yourself after gnome_dialog_run()
returns. Or you can set the dialog up so that all user
actions destroy it, then forget about it after gnome_dialog_run() returns. You could also
write a loop, calling
gnome_dialog_run() repeatedly until the user gives
valid input, and closing the dialog only after the loop
ends. If you write a loop, be careful to manually make the
dialog modal; otherwise there will be short intervals where
it is not.
gnome_dialog_run_and_close()
monitors the dialog's "close"
and "destroy" signals, and
closes the dialog if and only if it does not close
"naturally" in response to user clicks or keystrokes. Using
this function guarantees that
gnome_dialog_close() will be called exactly once
before it returns, unless you connect truly malicious
callbacks to sabotage the process.
gnome_dialog_run_and_close() is not very useful in my
opinion; it is little more than a way to avoid thinking
about how the dialog will be closed.