
com.globalmentor.swing.BasicOptionPane Maven / Gradle / Ivy
Show all versions of globalmentor-swing Show documentation
/*
* Copyright © 1996-2009 GlobalMentor, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.globalmentor.swing;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import com.globalmentor.awt.*;
import com.globalmentor.model.Verifiable;
import com.globalmentor.util.*;
/**
* An option pane that knows how to verify its contents before closing the dialog.
*
* This class offers several improvements over {@link JOptionPane}:
*
*
* - If the message object implements {@link DefaultFocusable}, the initial focus component is given the focus when the pane is displayed.
* - If the message object implements {@link Verifiable}, the input is verified before the pane is allowed to close.
* - If {@link #setValue(Object)} is called with an explicit {@link Integer} value, that value will be returned even if options are present.
*
* @author Garret Wilson
* @see BasicDialog
* @see DefaultFocusable
* @see Verifiable
*/
public class BasicOptionPane extends JOptionPane {
/**
* Creates a BasicOptionPane
with a test message.
*/
public BasicOptionPane() {
super();
}
/**
* Creates a instance of BasicOptionPane
to display a message using the plain-message message type and the default options delivered by the UI.
*
* @param message the Object
to display
*/
public BasicOptionPane(Object message) {
super(message);
}
/**
* Creates an instance of BasicOptionPane
to display a message with the specified message type and the default options,
*
* @param message the Object
to display
* @param messageType the type of message to be displayed: ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
*/
public BasicOptionPane(Object message, int messageType) {
super(message, messageType);
}
/**
* Creates an instance of BasicOptionPane
to display a message with the specified message type and options.
*
* @param message the Object
to display
* @param messageType the type of message to be displayed: ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
* @param optionType the options to display in the pane: DEFAULT_OPTION, YES_NO_OPTION, YES_NO_CANCEL_OPTION OK_CANCEL_OPTION
*/
public BasicOptionPane(Object message, int messageType, int optionType) {
super(message, messageType, optionType);
}
/**
* Creates an instance of BasicOptionPane
to display a message with the specified message type, options, and icon.
*
* @param message the Object
to display
* @param messageType the type of message to be displayed: ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
* @param optionType the options to display in the pane: DEFAULT_OPTION, YES_NO_OPTION, YES_NO_CANCEL_OPTION OK_CANCEL_OPTION
* @param icon the Icon
image to display
*/
public BasicOptionPane(Object message, int messageType, int optionType, Icon icon) {
super(message, messageType, optionType, icon);
}
/**
* Creates an instance of BasicOptionPane
to display a message with the specified message type, icon, and options. None of the options is
* initially selected.
*
* The options objects should contain either instances of Component
s, (which are added directly) or Strings
(which are wrapped in a
* JButton
). If you provide Component
s, you must ensure that when the Component
is clicked it messages
* setValue
in the created BasicOptionPane
.
*
* @param message the Object
to display
* @param messageType the type of message to be displayed: ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
* @param optionType the options to display in the pane: DEFAULT_OPTION, YES_NO_OPTION, YES_NO_CANCEL_OPTION OK_CANCEL_OPTION; only meaningful if the
* options
parameter is null
* @param icon the Icon
image to display
* @param options the choices the user can select
*/
public BasicOptionPane(Object message, int messageType, int optionType, Icon icon, Object[] options) {
super(message, messageType, optionType, icon, options);
}
/**
* Creates an instance of BasicOptionPane
to display a message with the specified message type, icon, and options, with the initially-selected
* option specified.
*
* @param message the Object
to display
* @param messageType the type of message to be displayed: ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
* @param optionType the options to display in the pane: DEFAULT_OPTION, YES_NO_OPTION, YES_NO_CANCEL_OPTION OK_CANCEL_OPTION; only meaningful if the
* options
parameter is null
* @param icon the Icon image to display
* @param options the choices the user can select
* @param initialValue the choice that is initially selected
*/
public BasicOptionPane(Object message, int messageType, int optionType, Icon icon, Object[] options, Object initialValue) {
super(message, messageType, optionType, icon, options, initialValue);
}
//TODO add other methods here
/**
* Brings up an information-message dialog titled "Message".
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message the Object
to display
* @throws HeadlessException if GraphicsEnvironment.isHeadless
returns true
* @see java.awt.GraphicsEnvironment#isHeadless
*/
public static void showMessageDialog(Component parentComponent, Object message) throws HeadlessException {
//TODO fix UIManager.getString("OptionPane.messageDialogTitle", parentComponent)
showMessageDialog(parentComponent, message, "Message", INFORMATION_MESSAGE);
}
/**
* Brings up a dialog that displays a message using a default icon determined by the messageType
parameter.
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message the Object
to display
* @param title the title string for the dialog
* @param messageType the type of message to be displayed: ERROR_MESSAGE
, INFORMATION_MESSAGE
, WARNING_MESSAGE
,
* QUESTION_MESSAGE
, or PLAIN_MESSAGE
* @throws HeadlessException if GraphicsEnvironment.isHeadless
returns true
* @see java.awt.GraphicsEnvironment#isHeadless
*/
public static void showMessageDialog(Component parentComponent, Object message, String title, int messageType) throws HeadlessException {
showMessageDialog(parentComponent, message, title, messageType, null);
}
/**
* Brings up a dialog displaying a message, specifying all parameters.
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message the Object
to display
* @param title the title string for the dialog
* @param messageType the type of message to be displayed: ERROR_MESSAGE
, INFORMATION_MESSAGE
, WARNING_MESSAGE
,
* QUESTION_MESSAGE
, or PLAIN_MESSAGE
* @param icon an icon to display in the dialog that helps the user identify the kind of message that is being displayed
* @throws HeadlessException if GraphicsEnvironment.isHeadless
returns true
* @see java.awt.GraphicsEnvironment#isHeadless
*/
public static void showMessageDialog(Component parentComponent, Object message, String title, int messageType, Icon icon) throws HeadlessException {
showOptionDialog(parentComponent, message, title, DEFAULT_OPTION, messageType, icon, null, null);
}
/**
* Brings up a modal dialog with the options Yes, No and Cancel; with the title, "Select an Option".
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message the Object
to display
* @return an integer indicating the option selected by the user
*/
public static int showConfirmDialog(Component parentComponent, Object message) {
return showConfirmDialog(parentComponent, message, UIManager.getString("OptionPane.titleText"), YES_NO_CANCEL_OPTION);
}
/**
* Brings up a modal dialog where the number of choices is determined by the optionType
parameter.
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message the Object
to display
* @param title the title string for the dialog
* @param optionType an int designating the options available on the dialog: YES_NO_OPTION, or YES_NO_CANCEL_OPTION
* @return an int indicating the option selected by the user
*/
public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType) {
//TODO del Log.trace("show confirm dialog 1"); //TODO del
return showConfirmDialog(parentComponent, message, title, optionType, QUESTION_MESSAGE);
}
/**
* Brings up a modal dialog where the number of choices is determined by the optionType
parameter, where the messageType
parameter
* determines the icon to display. The messageType
parameter is primarily used to supply a default icon from the Look and Feel.
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used.
* @param message the Object
to display
* @param title the title string for the dialog
* @param optionType an integer designating the options available on the dialog: YES_NO_OPTION, or YES_NO_CANCEL_OPTION
* @param messageType an integer designating the kind of message this is, primarily used to determine the icon from the pluggable Look and Feel:
* ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
* @return an integer indicating the option selected by the user
*/
public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType) {
//TODO del Log.trace("show confirm dialog 2"); //TODO del
return showConfirmDialog(parentComponent, message, title, optionType, messageType, null);
}
/**
* Brings up a modal dialog with a specified icon, where the number of choices is determined by the optionType
parameter. The
* messageType
parameter is primarily used to supply a default icon from the Look and Feel.
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message The Object to display
* @param title the title string for the dialog
* @param optionType an int designating the options available on the dialog: YES_NO_OPTION, or YES_NO_CANCEL_OPTION
* @param messageType an int designating the kind of message this is, primarily used to determine the icon from the pluggable Look and Feel: ERROR_MESSAGE,
* INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
* @param icon the icon to display in the dialog
* @return an int indicating the option selected by the user
*/
public static int showConfirmDialog(Component parentComponent, Object message, String title, int optionType, int messageType, Icon icon) {
//TODO del Log.trace("show confirm dialog 3"); //TODO del
return showOptionDialog(parentComponent, message, title, optionType, messageType, icon, null, null);
}
/**
* Brings up a modal dialog with a specified icon, where the initial choice is dermined by the initialValue
parameter and the number of choices
* is determined by the optionType
parameter.
*
* If optionType
is YES_NO_OPTION, or YES_NO_CANCEL_OPTION and the options
parameter is null
, then the options are
* supplied by the Look and Feel.
*
* The messageType
parameter is primarily used to supply a default icon from the Look and Feel.
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message the Object
to display
* @param title the title string for the dialog
* @param optionType an integer designating the options available on the dialog: YES_NO_OPTION, or YES_NO_CANCEL_OPTION
* @param messageType an integer designating the kind of message this is, primarily used to determine the icon from the pluggable Look and Feel:
* ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, QUESTION_MESSAGE, or PLAIN_MESSAGE
* @param icon the icon to display in the dialog
* @param options an array of objects indicating the possible choices the user can make; if the objects are components, they are rendered properly; non-
* String
objects are rendered using their toString
methods; if this parameter is null
, the options are
* determined by the Look and Feel.
* @param initialValue the object that represents the default selection for the dialog
* @return an integer indicating the option chosen by the user, or CLOSED_OPTION if the user closed the Dialog
*/
/*TODO del when new JDK1.4 version works
public static int showOptionDialog(Component parentComponent, Object message,
String title, int optionType,
int messageType, Icon icon,
Object[] options, Object initialValue) {
//TODO del Log.trace("inside custom showOptionDialog()"); //TODO del
JOptionPane pane = new OptionPane(message, messageType,
optionType, icon,
options, initialValue);
pane.setInitialValue(initialValue);
JDialog dialog = pane.createDialog(parentComponent, title);
pane.selectInitialValue();
dialog.show();
Object selectedValue = pane.getValue();
if(selectedValue == null)
return CLOSED_OPTION;
if(options == null) {
if(selectedValue instanceof Integer)
return ((Integer)selectedValue).intValue();
return CLOSED_OPTION;
}
for(int counter = 0, maxCounter = options.length;
counter < maxCounter; counter++) {
if(options[counter].equals(selectedValue))
return counter;
}
return CLOSED_OPTION;
}
*/
/**
* Brings up a dialog with a specified icon, where the initial choice is determined by the initialValue
parameter and the number of choices is
* determined by the optionType
parameter.
*
* If optionType
is YES_NO_OPTION
, or YES_NO_CANCEL_OPTION
and the options
parameter is null
,
* then the options are supplied by the look and feel.
*
* The messageType
parameter is primarily used to supply a default icon from the look and feel.
*
* @param parentComponent determines the Frame
in which the dialog is displayed; if null
, or if the parentComponent
has
* no Frame
, a default Frame
is used
* @param message the Object
to display
* @param title the title string for the dialog
* @param optionType an integer designating the options available on the dialog: YES_NO_OPTION
, or YES_NO_CANCEL_OPTION
* @param messageType an integer designating the kind of message this is, primarily used to determine the icon from the pluggable Look and Feel:
* ERROR_MESSAGE
, INFORMATION_MESSAGE
, WARNING_MESSAGE
, QUESTION_MESSAGE
, or
* PLAIN_MESSAGE
* @param icon the icon to display in the dialog
* @param options an array of objects indicating the possible choices the user can make; if the objects are components, they are rendered properly; non-
* String
objects are rendered using their toString
methods; if this parameter is null
, the options are
* determined by the Look and Feel
* @param initialValue the object that represents the default selection for the dialog; only meaningful if options
is used; can be
* null
* @return an integer indicating the option chosen by the user, or CLOSED_OPTION
if the user closed the dialog
* @throws HeadlessException if GraphicsEnvironment.isHeadless
returns true
* @see java.awt.GraphicsEnvironment#isHeadless
*/
public static int showOptionDialog(Component parentComponent, Object message, String title, int optionType, int messageType, Icon icon, Object[] options,
Object initialValue) throws HeadlessException {
JOptionPane pane = new BasicOptionPane(message, messageType, optionType, icon, options, initialValue);
pane.setInitialValue(initialValue);
pane.setComponentOrientation(((parentComponent == null) ? getRootFrame() : parentComponent).getComponentOrientation());
JDialog dialog = pane.createDialog(parentComponent, title);
pane.selectInitialValue();
dialog.show();
dialog.dispose();
Object selectedValue = pane.getValue();
if(selectedValue == null)
return CLOSED_OPTION;
if(options != null) {
for(int counter = 0, maxCounter = options.length; counter < maxCounter; counter++) {
if(options[counter].equals(selectedValue))
return counter;
}
}
if(selectedValue instanceof Integer) //if the selected value is an integer, always return it, even if there are options present (newswing)
return ((Integer)selectedValue).intValue();
return CLOSED_OPTION;
}
/**
* Creates and returns a new JDialog
wrapping this
centered on the parentComponent
in the parentComponent
* 's frame. title
is the title of the returned dialog. The returned JDialog
will be set up such that once it is closed, or the user
* clicks on the OK button, the dialog will be disposed and closed.
*
* This version first verifies the content pane, if it is Verifiable
, and only closes the dialog if the object verifies correctly or is not
* Verifiable
.
*
* @param parentComponent determines the frame in which the dialog is displayed; if the parentComponent
has no Frame
, a default
* Frame
is used
* @param title the title string for the dialog
* @return a new JDialog
containing this instance
*/
public JDialog createDialog(Component parentComponent, String title) {
//TODO del final JDialog dialog=super.createDialog(parentComponent, title); //create the default dialog
final JDialog dialog;
Window window = getWindowForComponent(parentComponent);
if(window instanceof Frame) {
dialog = new BasicDialog((Frame)window, title, true);
} else {
dialog = new BasicDialog((Dialog)window, title, true);
}
final Container contentPane = dialog.getContentPane();
//see if the message is verifiable
final Verifiable verifiableMessage = message instanceof Verifiable ? (Verifiable)message : null; //TODO newswing
contentPane.setLayout(new BorderLayout());
contentPane.add(this, BorderLayout.CENTER);
dialog.pack();
dialog.setLocationRelativeTo(parentComponent);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); //don't automatically close the dialog TODO newswing
dialog.addWindowListener(new WindowAdapter() {
boolean gotFocus = false;
public void windowClosing(WindowEvent we) {
setValue(null);
}
public void windowActivated(WindowEvent we) {
// Once window gets focus, set initial focus
if(!gotFocus) {
selectInitialValue();
gotFocus = true;
}
}
});
addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if(dialog.isVisible() && event.getSource() == BasicOptionPane.this
&& (event.getPropertyName().equals(VALUE_PROPERTY) || event.getPropertyName().equals(INPUT_VALUE_PROPERTY))) {
//if the user is pressing "OK", try to verify the message
if(event.getNewValue() instanceof Integer && ((Integer)event.getNewValue()).intValue() == OK_OPTION) { //if the user is pressing "OK"
//TODO del if(OptionPane.this.getValue().equals(new )
if(verifiableMessage != null) { //if the message is verifiable TODO newswing
if(!verifiableMessage.verify()) { //verify the content pane; if it doesn't verify
value = null; //change the value back to nothing so that pressing the button again will bring us back here (but don't call setValue(null), because that will close the dialog)
return; //cancel the closing process
}
}
}
dialog.setVisible(false);
dialog.dispose();
}
}
});
return dialog;
}
/**
* Requests that the initial value be selected, which will set focus to the initial value.
*
* This version first checks to see if the message implements DefaultFocusable
, and if so the message is asked which component should get the
* focus. Otherwise, the default initial value is selected.
*
* @see #getMessage
* @see DefaultFocusable
*/
public void selectInitialValue() {
final Object message = getMessage(); //get the message object used in the pane
/*TODO fix; this might be useful in the future
if(message instanceof Container)
{
final FocusTraversalPolicy focusTraversalPolicy=((Container)message).getFocusTraversalPolicy();
if(focusTraversalPolicy!=null)
{
final Component defaultComponent=focusTraversalPolicy.getDefaultComponent((Container)message);
if(defaultComponent!=null)
defaultComponent.requestFocusInWindow(); //TODO testing
}
//TODO fix else
//TODO fix super.selectInitialValue(); //do the default initial value selection
}
*/
//if the message object knows how to request the default focus, let it do so if it can; if it can't
if(!(message instanceof DefaultFocusable) || !((DefaultFocusable)message).requestDefaultFocusComponentFocus()) {
super.selectInitialValue(); //do the default initial value selection
}
/*TODO del when works
//if the message object knows which component should get the default focus
if(message instanceof DefaultFocusable && ((DefaultFocusable)message).getDefaultFocusComponent()!=null)
((DefaultFocusable)message).getDefaultFocusComponent().requestFocusInWindow(); //request focus for the default component
else //if the message object doesn't know what to focus
super.selectInitialValue(); //do the default initial value selection
*/
}
/**
* Returns the specified component's toplevel Frame
or Dialog
.
*
* @param parentComponent the Component
to check for a Frame
or Dialog
* @return the Frame
or Dialog
that contains the component, or the default frame if the component is null
, or does not
* have a valid Frame
or Dialog
parent
*/
static Window getWindowForComponent(Component parentComponent) {
if(parentComponent == null)
return getRootFrame();
if(parentComponent instanceof Frame || parentComponent instanceof Dialog)
return (Window)parentComponent;
return getWindowForComponent(parentComponent.getParent());
}
}