GTK+ / Gnome Application Development | |||
---|---|---|---|
<<< Previous | Home | Next >>> |
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.
#include <libgnomeui/gnome-dialog.h> |
gint
gnome_dialog_run
(GnomeDialog* dialog);
gint
gnome_dialog_run_and_close
(GnomeDialog* dialog);
Figure 6. "Running" a Dialog
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.