// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.gui; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.KeyStroke; import javax.swing.UIManager; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.gui.help.HelpBrowser; import org.openstreetmap.josm.gui.help.HelpUtil; import org.openstreetmap.josm.gui.util.GuiHelper; import org.openstreetmap.josm.gui.widgets.JMultilineLabel; import org.openstreetmap.josm.io.OnlineResource; import org.openstreetmap.josm.tools.GBC; import org.openstreetmap.josm.tools.ImageProvider; import org.openstreetmap.josm.tools.Utils; import org.openstreetmap.josm.tools.WindowGeometry; /** * General configurable dialog window. * * If dialog is modal, you can use {@link #getValue()} to retrieve the * button index. Note that the user can close the dialog * by other means. This is usually equivalent to cancel action. * * For non-modal dialogs, {@link #buttonAction(int, ActionEvent)} can be overridden. * * There are various options, see below. * * Note: The button indices are counted from 1 and upwards. * So for {@link #getValue()}, {@link #setDefaultButton(int)} and * {@link #setCancelButton} the first button has index 1. * * Simple example: *
* ExtendedDialog ed = new ExtendedDialog( * Main.parent, tr("Dialog Title"), * new String[] {tr("Ok"), tr("Cancel")}); * ed.setButtonIcons(new String[] {"ok", "cancel"}); // optional * ed.setIcon(JOptionPane.WARNING_MESSAGE); // optional * ed.setContent(tr("Really proceed? Interesting things may happen...")); * ed.showDialog(); * if (ed.getValue() == 1) { // user clicked first button "Ok" * // proceed... * } **/ public class ExtendedDialog extends JDialog { private final boolean disposeOnClose; private volatile int result; public static final int DialogClosedOtherwise = 0; private boolean toggleable; private String rememberSizePref = ""; private transient WindowGeometry defaultWindowGeometry; private String togglePref = ""; private int toggleValue = -1; private ConditionalOptionPaneUtil.MessagePanel togglePanel; private Component parent; private Component content; private final String[] bTexts; private String[] bToolTipTexts; private transient Icon[] bIcons; private Set
setButtonIcons
setContent
toggleEnable
toggleDisable
setToggleCheckboxText
setRememberWindowGeometry
showDialog
to display it. You can receive
* the user's choice using getValue
. Have a look at this function
* for possible return values.
*
* @param parent The parent element that will be used for position and maximum size
* @param title The text that will be shown in the window titlebar
* @param buttonTexts String Array of the text that will appear on the buttons. The first button is the default one.
*/
public ExtendedDialog(Component parent, String title, String ... buttonTexts) {
this(parent, title, buttonTexts, true, true);
}
/**
* Same as above but lets you define if the dialog should be modal.
* @param parent The parent element that will be used for position and maximum size
* @param title The text that will be shown in the window titlebar
* @param buttonTexts String Array of the text that will appear on the buttons. The first button is the default one.
* @param modal Set it to {@code true} if you want the dialog to be modal
*/
public ExtendedDialog(Component parent, String title, String[] buttonTexts, boolean modal) {
this(parent, title, buttonTexts, modal, true);
}
public ExtendedDialog(Component parent, String title, String[] buttonTexts, boolean modal, boolean disposeOnClose) {
super(searchRealParent(parent), title, modal ? ModalityType.DOCUMENT_MODAL : ModalityType.MODELESS);
this.parent = parent;
this.modal = modal;
bTexts = Utils.copyArray(buttonTexts);
if (disposeOnClose) {
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
}
this.disposeOnClose = disposeOnClose;
}
private static Frame searchRealParent(Component parent) {
if (parent == null) {
return null;
} else {
return GuiHelper.getFrameForComponent(parent);
}
}
/**
* Allows decorating the buttons with icons.
* @param buttonIcons The button icons
* @return {@code this}
*/
public ExtendedDialog setButtonIcons(Icon ... buttonIcons) {
this.bIcons = Utils.copyArray(buttonIcons);
return this;
}
/**
* Convenience method to provide image names instead of images.
* @param buttonIcons The button icon names
* @return {@code this}
*/
public ExtendedDialog setButtonIcons(String ... buttonIcons) {
bIcons = new Icon[buttonIcons.length];
for (int i = 0; i < buttonIcons.length; ++i) {
bIcons[i] = ImageProvider.get(buttonIcons[i]);
}
return this;
}
/**
* Allows decorating the buttons with tooltips. Expects a String array with
* translated tooltip texts.
*
* @param toolTipTexts the tool tip texts. Ignored, if null.
* @return {@code this}
*/
public ExtendedDialog setToolTipTexts(String ... toolTipTexts) {
this.bToolTipTexts = Utils.copyArray(toolTipTexts);
return this;
}
/**
* Sets the content that will be displayed in the message dialog.
*
* Note that depending on your other settings more UI elements may appear.
* The content is played on top of the other elements though.
*
* @param content Any element that can be displayed in the message dialog
* @return {@code this}
*/
public ExtendedDialog setContent(Component content) {
return setContent(content, true);
}
/**
* Sets the content that will be displayed in the message dialog.
*
* Note that depending on your other settings more UI elements may appear.
* The content is played on top of the other elements though.
*
* @param content Any element that can be displayed in the message dialog
* @param placeContentInScrollPane if true, places the content in a JScrollPane
* @return {@code this}
*/
public ExtendedDialog setContent(Component content, boolean placeContentInScrollPane) {
this.content = content;
this.placeContentInScrollPane = placeContentInScrollPane;
return this;
}
/**
* Sets the message that will be displayed. The String will be automatically
* wrapped if it is too long.
*
* Note that depending on your other settings more UI elements may appear.
* The content is played on top of the other elements though.
*
* @param message The text that should be shown to the user
* @return {@code this}
*/
public ExtendedDialog setContent(String message) {
return setContent(string2label(message), false);
}
/**
* Decorate the dialog with an icon that is shown on the left part of
* the window area. (Similar to how it is done in {@link JOptionPane})
* @param icon The icon to display
* @return {@code this}
*/
public ExtendedDialog setIcon(Icon icon) {
this.icon = icon;
return this;
}
/**
* Convenience method to allow values that would be accepted by {@link JOptionPane} as messageType.
* @param messageType The {@link JOptionPane} messageType
* @return {@code this}
*/
public ExtendedDialog setIcon(int messageType) {
switch (messageType) {
case JOptionPane.ERROR_MESSAGE:
return setIcon(UIManager.getIcon("OptionPane.errorIcon"));
case JOptionPane.INFORMATION_MESSAGE:
return setIcon(UIManager.getIcon("OptionPane.informationIcon"));
case JOptionPane.WARNING_MESSAGE:
return setIcon(UIManager.getIcon("OptionPane.warningIcon"));
case JOptionPane.QUESTION_MESSAGE:
return setIcon(UIManager.getIcon("OptionPane.questionIcon"));
case JOptionPane.PLAIN_MESSAGE:
return setIcon(null);
default:
throw new IllegalArgumentException("Unknown message type!");
}
}
/**
* Show the dialog to the user. Call this after you have set all options
* for the dialog. You can retrieve the result using {@link #getValue()}.
* @return {@code this}
*/
public ExtendedDialog showDialog() {
// Check if the user has set the dialog to not be shown again
if (toggleCheckState()) {
result = toggleValue;
return this;
}
setupDialog();
if (defaultButton != null) {
getRootPane().setDefaultButton(defaultButton);
}
// Don't focus the "do not show this again" check box, but the default button.
if (toggleable || focusOnDefaultButton) {
requestFocusToDefaultButton();
}
setVisible(true);
toggleSaveState();
return this;
}
/**
* Retrieve the user choice after the dialog has been closed.
*
* @return null
or to an empty string to disable again.
* By default, it's disabled.
*
* Note: If you want to set the width of this dialog directly use the usual
* setSize, setPreferredSize, setMaxSize, setMinSize
*
* @param pref The preference to save the dimension to
* @param wg The default window geometry that should be used if no
* existing preference is found (only takes effect if
* pref
is not null or empty
* @return {@code this}
*/
public ExtendedDialog setRememberWindowGeometry(String pref, WindowGeometry wg) {
rememberSizePref = pref == null ? "" : pref;
defaultWindowGeometry = wg;
return this;
}
/**
* Calling this will offer the user a "Do not show again" checkbox for the
* dialog. Default is to not offer the choice; the dialog will be shown
* every time.
* Currently, this is not supported for non-modal dialogs.
* @param togglePref The preference to save the checkbox state to
* @return {@code this}
*/
public ExtendedDialog toggleEnable(String togglePref) {
if (!modal) {
throw new IllegalStateException();
}
this.toggleable = true;
this.togglePref = togglePref;
return this;
}
/**
* Sets the button that will react to ENTER.
* @param defaultButtonIdx The button index (starts to 1)
* @return {@code this}
*/
public ExtendedDialog setDefaultButton(int defaultButtonIdx) {
this.defaultButtonIdx = defaultButtonIdx;
return this;
}
/**
* Used in combination with toggle:
* If the user presses 'cancel' the toggle settings are ignored and not saved to the pref
* @param cancelButtonIdx index of the button that stands for cancel, accepts multiple values
* @return {@code this}
*/
public ExtendedDialog setCancelButton(Integer... cancelButtonIdx) {
this.cancelButtonIdx = new HashSet<>(Arrays.