Wednesday, January 30, 2008

Dialog Modality

his Tech Tip reprinted with permission by java.sun.com

Top-level popup windows in "Java-speak" are called dialog boxes (or simply "dialogs"). They are typically used to interact with a user -- either to display a message or to accept user input. Before Java SE 6, dialog boxes were modeless by default, with an option to be modal. When a dialog box is modal, other windows in the application are blocked from accepting input, unless they have the dialog box as their owner. After a user reacts accordingly to a dialog box, that is, by entering input or just closing the dialog, the other windows of the application become accessible again.

Java SE 6 gives you more options regarding dialog modality. No longer are you limited in scope to one level of modality: on or off. Now you have four distinct settings defined by the new Dialog.ModalityType enumeration:

  • MODELESS
  • APPLICATION_MODAL
  • DOCUMENT_MODAL
  • TOOLKIT_MODAL

First let's look at MODELESS and APPLICATION_MODAL. A MODELESS setting means a modeless dialog box. As before, a modeless dialog box does not block input to any other window of the application. Another modal dialog box could block input to it, but a modeless one has no effect on another. If you call the setModal() method of the Dialog class with a value of false, it sets the Dialog.ModalityType to MODELESS.

The APPLICATION_MODAL setting means a modal dialog box. As before, all windows of the application that do not have the modal dialog box in their owner hierarchy are blocked from getting focus. This means that new windows can be created from the modal dialog and will accept input. However, new windows created from other pre-existing windows cannot. If you call the setModal() method of Dialog with a value of true, it sets the modality of the Dialog to DEFAULT_MODALITY_TYPE, which equates to APPLICATION_MODAL. This keeps legacy code valid, though new code should use the new setModalityType() method.

At this point you might ask what if you don't explicitly specify a modality? The answer is that the initial modality is modeless. Also, if you specify a boolean modality, it produces the same settings as calling setModal() with that boolean value. The last option is explicitly setting the modality, which has the obvious effect.

DOCUMENT_MODAL and TOOLKIT_MODAL are where things get interesting. DOCUMENT_MODAL allows you to have different sets of windows that are modal. For instance, you can have a modal application window that displays a help window. Provided that it has a top-level window that is not part of the main application hierarchy, the help window can be modal. In addition, the help window can create other modal windows that have a modality separate from the main window and separate from any modal dialogs that the help window creates. Having a modal application that displays a modal help window is a common need when utilizing the JavaHelp library. It's typical that users want to be able to interact with help, even when the current window is modal. This need wasn't adequately met prior to support for DOCUMENT_MODAL because the main application window and help window had different owner hierarchies.

Think of TOOLKIT_MODAL as APPLICATION_MODAL, where the application is the browser. (In this paragraph what's said about applets also applies to applications started through Java WebStart technology.) This setting allows one applet in a browser to be modal, blocking other applets from accepting input. That's because all the applets are loaded with the same Toolkit. Your applet must have AWTPermission.toolkitModality enabled for TOOLKIT_MODAL to work.

In addition to setting the modality type of a window, you can set the modal exclusion type by calling the setModalExclusionType() method of Window. This allows you to exclude certain windows from behaving according to the pertinent modality type. The setModalExclusionType() method accepts one of three values from the Dialog.ModalExclusionType enumeration:

  • NO_EXCLUDE
  • APPLICATION_EXCLUDE
  • TOOLKIT_EXCLUDE

The NO_EXCLUDE option means no modal exclusion. The window behaves according to its current modality type. The other two settings allow you to use a modality type, but also allow specific windows to accept input focus. The APPLICATION_EXCLUDE setting specifies that at the application level, the window will not behave according to its modality. TOOLKIT_EXCLUDE specifies that at both the application and Toolkit level, the window will not behave according to its modality. There is no way to have a window exclude behavior at the Toolkit level, but not at the application level.

Before using either the modality types or the exclusion option, you can ask the Toolkit if either is supported. To discover if a particular modality is supported, use the boolean isModalityTypeSupported(Dialog.ModalityType modalityType) method. To discover if an exclusion type is supported, use the boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType modalExclusionType) method.

Here's a program, DualModal, that displays two frames that use the DOCUMENT_MODAL setting. Each frame has a button that creates a document modal option pane, accepting input. The label of the selected button changes to the text that was entered when the option pane closes.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class DualModal {
public static void main(String args[]) {
Runnable runner = new Runnable() {
public void run() {
JFrame frame1 = new JFrame("Left");
JFrame frame2 = new JFrame("Right");
frame1.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
frame2.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);
JButton button1 = new JButton("Left");
JButton button2 = new JButton("Right");
frame1.add(button1, BorderLayout.CENTER);
frame2.add(button2, BorderLayout.CENTER);
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
JButton source = (JButton)e.getSource();
String text = getNewText(source);
if (!JOptionPane.UNINITIALIZED_VALUE.equals(text)
&& text.trim().length() > 0) {
source.setText(text);
}
}
};
button1.addActionListener(listener);
button2.addActionListener(listener);
frame1.setBounds(100, 100, 200, 200);
frame1.setVisible(true);
frame2.setBounds(400, 100, 200, 200);
frame2.setVisible(true);
}
};
EventQueue.invokeLater(runner);
}
private static String getNewText(Component parent) {
JOptionPane pane = new JOptionPane(
"New label", JOptionPane.QUESTION_MESSAGE
);
pane.setWantsInput(true);
JDialog dialog = pane.createDialog(parent, "Enter Text");
// Uncomment line and comment out next
// to see application modal
// dialog.setModalityType(
// Dialog.ModalityType.APPLICATION_MODAL);
dialog.setModalityType(
Dialog.ModalityType.DOCUMENT_MODAL);
dialog.setVisible(true);
return (String)pane.getInputValue();
}
}

Notice how you can interact with the top-level dialog, but not the frame under either of them when the dialog is shown.

Here is what the initial pair of frames look like:


Image

And here are the two frames with their respective option panes:


Image

You might think that changing the setModalityType() line to use APPLICATION_MODAL would allow you to interact with both option frames simultaneously. It won't. You need to finish using one option frame before you can bring up the other.

Two points worth mentioning here. Changing the modality of an already displayed window has no effect. You must hide the dialog box and make it visible again for the new modality setting to take effect.

Also, prior to Java SE 6, any AWT Window or subclass could use the setAlwaysOnTop() method of Window to request that it is always displayed on top. This is not the same as modal and does not prevent other windows from getting input focus.

For more information on dialog modality, see the article The New Modality API in Mustang.

Copyright (c) 2004-2005 Sun Microsystems, Inc.
All Rights Reserved.

No comments: