001    // License: GPL. Copyright 2007 by Immanuel Scholz and others
002    package org.openstreetmap.josm.gui;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.awt.Component;
007    import java.awt.GridBagLayout;
008    import java.awt.HeadlessException;
009    
010    import javax.swing.JCheckBox;
011    import javax.swing.JLabel;
012    import javax.swing.JOptionPane;
013    import javax.swing.JPanel;
014    
015    import org.openstreetmap.josm.Main;
016    import 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     */
028    public 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  the preference key
041         * @return prefKey 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  the preference key
062         * @return prefKey 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
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    }