001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Component;
007import java.awt.GridBagLayout;
008import java.awt.HeadlessException;
009
010import javax.swing.JCheckBox;
011import javax.swing.JLabel;
012import javax.swing.JOptionPane;
013import javax.swing.JPanel;
014
015import org.openstreetmap.josm.Main;
016import org.openstreetmap.josm.tools.GBC;
017
018/**
019 * ConditionalOptionPaneUtil provides static utility methods for displaying modal message dialogs
020 * which can be enabled/disabled by the user.
021 *
022 * They wrap the methods provided by {@link JOptionPane}. Within JOSM you should use these
023 * methods rather than the bare methods from {@link JOptionPane} because the methods provided
024 * by ConditionalOptionPaneUtil ensure that a dialog window is always on top and isn't hidden by one of the
025 * JOSM windows for detached dialogs, relation editors, history browser and the like.
026 *
027 */
028public final class ConditionalOptionPaneUtil {
029    static public final int DIALOG_DISABLED_OPTION = Integer.MIN_VALUE;
030
031    /**
032     * this is a static utility class only
033     */
034    private ConditionalOptionPaneUtil() {}
035
036    /**
037     * Replies the preference value for the preference key "message." + <code>prefKey</code>.
038     * The default value if the preference key is missing is true.
039     *
040     * @param  prefKey the preference key
041     * @return the preference value for the preference key "message." + <code>prefKey</code>
042     */
043    public static boolean getDialogShowingEnabled(String prefKey) {
044        return Main.pref.getBoolean("message."+prefKey, true);
045    }
046
047    /**
048     * sets the value for the preference key "message." + <code>prefKey</code>.
049     *
050     * @param prefKey the key
051     * @param enabled the value
052     */
053    public static void setDialogShowingEnabled(String prefKey, boolean enabled) {
054        Main.pref.put("message."+prefKey, enabled);
055    }
056
057    /**
058     * Returns the preference value for the preference key "message." + <code>prefKey</code> + ".value".
059     * The default value if the preference key is missing is -1.
060     *
061     * @param  prefKey the preference key
062     * @return the preference value for the preference key "message." + <code>prefKey</code> + ".value"
063     */
064    public static Integer getDialogReturnValue(String prefKey) {
065        return Main.pref.getInteger("message."+prefKey+".value", -1);
066    }
067
068    /**
069     * sets the value for the preference key "message." + <code>prefKey</code> + ".value".
070     *
071     * @param prefKey the key
072     * @param value the value
073     */
074    public static void setDialogReturnValue(String prefKey, Integer value) {
075        Main.pref.putInteger("message."+prefKey+".value", value);
076    }
077
078    /**
079     * Displays an confirmation dialog with some option buttons given by <code>optionType</code>.
080     * It is always on top even if there are other open windows like detached dialogs,
081     * relation editors, history browsers and the like.
082     *
083     * Set <code>optionType</code> to {@link JOptionPane#YES_NO_OPTION} for a dialog with a YES and
084     * a NO button.
085
086     * Set <code>optionType</code> to {@link JOptionPane#YES_NO_CANCEL_OPTION} for a dialog with a YES,
087     * a NO and a CANCEL button
088     *
089     * Returns one of the constants JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
090     * JOptionPane.CANCEL_OPTION or JOptionPane.CLOSED_OPTION depending on the action chosen by
091     * the user.
092     *
093     * @param preferenceKey the preference key
094     * @param parent  the parent component
095     * @param message  the message
096     * @param title the title
097     * @param optionType  the option type
098     * @param messageType the message type
099     * @param options a list of options
100     * @param defaultOption the default option; only meaningful if options is used; can be null
101     *
102     * @return the option selected by user. {@link JOptionPane#CLOSED_OPTION} if the dialog was closed.
103     */
104    static public int showOptionDialog(String preferenceKey, Component parent, Object message, String title, int optionType, int messageType, Object [] options, Object defaultOption) throws HeadlessException {
105        int ret = getDialogReturnValue(preferenceKey);
106        if (!getDialogShowingEnabled(preferenceKey) && ((ret == JOptionPane.YES_OPTION) || (ret == JOptionPane.NO_OPTION)))
107            return ret;
108        MessagePanel pnl = new MessagePanel(false, message);
109        ret = JOptionPane.showOptionDialog(parent, pnl, title, optionType, messageType, null, options, defaultOption);
110
111        if (((ret == JOptionPane.YES_OPTION) || (ret == JOptionPane.NO_OPTION)) && !pnl.getDialogShowingEnabled()) {
112            setDialogShowingEnabled(preferenceKey, false);
113            setDialogReturnValue(preferenceKey, ret);
114        }
115        return ret;
116    }
117
118    /**
119     * Displays a confirmation dialog with some option buttons given by <code>optionType</code>.
120     * It is always on top even if there are other open windows like detached dialogs,
121     * relation editors, history browsers and the like.
122     *
123     * Set <code>optionType</code> to {@link JOptionPane#YES_NO_OPTION} for a dialog with a YES and
124     * a NO button.
125
126     * Set <code>optionType</code> to {@link JOptionPane#YES_NO_CANCEL_OPTION} for a dialog with a YES,
127     * a NO and a CANCEL button
128     *
129     * Replies true, if the selected option is equal to <code>trueOption</code>, otherwise false.
130     * Replies true, if the dialog is not displayed because the respective preference option
131     * <code>preferenceKey</code> is set to false and the user has previously chosen
132     * <code>trueOption</code>.
133     *
134     * @param preferenceKey the preference key
135     * @param parent  the parent component
136     * @param message  the message
137     * @param title the title
138     * @param optionType  the option type
139     * @param messageType the message type
140     * @param trueOption  if this option is selected the method replies true
141     *
142     *
143     * @return true, if the selected option is equal to <code>trueOption</code>, otherwise false.
144     *
145     * @see JOptionPane#INFORMATION_MESSAGE
146     * @see JOptionPane#WARNING_MESSAGE
147     * @see JOptionPane#ERROR_MESSAGE
148     */
149    static public boolean showConfirmationDialog(String preferenceKey, Component parent, Object message, String title, int optionType, int messageType, int trueOption) throws HeadlessException {
150        int ret = getDialogReturnValue(preferenceKey);
151        if (!getDialogShowingEnabled(preferenceKey) && ((ret == JOptionPane.YES_OPTION) || (ret == JOptionPane.NO_OPTION)))
152            return ret == trueOption;
153        MessagePanel pnl = new MessagePanel(false, message);
154        ret = JOptionPane.showConfirmDialog(parent, pnl, title, optionType, messageType);
155        if (((ret == JOptionPane.YES_OPTION) || (ret == JOptionPane.NO_OPTION)) && !pnl.getDialogShowingEnabled()) {
156            setDialogShowingEnabled(preferenceKey, false);
157            setDialogReturnValue(preferenceKey, ret);
158        }
159        return ret == trueOption;
160    }
161
162    /**
163     * Displays an message in modal dialog with an OK button. Makes sure the dialog
164     * is always on top even if there are other open windows like detached dialogs,
165     * relation editors, history browsers and the like.
166     *
167     * If there is a preference with key <code>preferenceKey</code> and value <code>false</code>
168     * the dialog is not show.
169     *
170     * @param preferenceKey the preference key
171     * @param parent  the parent component
172     * @param message  the message
173     * @param title the title
174     * @param messageType the message type
175     *
176     * @see JOptionPane#INFORMATION_MESSAGE
177     * @see JOptionPane#WARNING_MESSAGE
178     * @see JOptionPane#ERROR_MESSAGE
179     */
180    static public void showMessageDialog(String preferenceKey, Component parent, Object message, String title,int messageType) {
181        if (!getDialogShowingEnabled(preferenceKey))
182            return;
183        MessagePanel pnl = new MessagePanel(false, message);
184        JOptionPane.showMessageDialog(parent, pnl, title, messageType);
185        if(!pnl.getDialogShowingEnabled()) {
186            setDialogShowingEnabled(preferenceKey, false);
187        }
188    }
189
190    /**
191     * This is a message panel used in dialogs which can be enabled/disabled with a preference
192     * setting.
193     * In addition to the normal message any {@link JOptionPane} would display it includes
194     * a checkbox for enabling/disabling this particular dialog.
195     *
196     */
197    private static class MessagePanel extends JPanel {
198        JCheckBox cbShowDialog;
199
200        public MessagePanel(boolean donotshow, Object message) {
201            cbShowDialog = new JCheckBox(tr("Do not show again (remembers choice)"));
202            cbShowDialog.setSelected(donotshow);
203            setLayout(new GridBagLayout());
204
205            if (message instanceof Component) {
206                add((Component)message, GBC.eop());
207            } else {
208                add(new JLabel(message.toString()),GBC.eop());
209            }
210            add(cbShowDialog, GBC.eol());
211        }
212
213        public boolean getDialogShowingEnabled() {
214            return !cbShowDialog.isSelected();
215        }
216    }
217}