source: josm/trunk/src/org/openstreetmap/josm/gui/ConditionalOptionPaneUtil.java@ 13014

Last change on this file since 13014 was 12846, checked in by bastiK, 7 years ago

see #15229 - use Config.getPref() wherever possible

  • Property svn:eol-style set to native
File size: 13.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.GraphicsEnvironment;
8import java.awt.GridBagLayout;
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.Map;
12import java.util.Set;
13
14import javax.swing.ButtonGroup;
15import javax.swing.JOptionPane;
16import javax.swing.JPanel;
17import javax.swing.JRadioButton;
18
19import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
20import org.openstreetmap.josm.spi.preferences.Config;
21import org.openstreetmap.josm.tools.GBC;
22import org.openstreetmap.josm.tools.Utils;
23
24/**
25 * ConditionalOptionPaneUtil provides static utility methods for displaying modal message dialogs
26 * which can be enabled/disabled by the user.
27 *
28 * They wrap the methods provided by {@link JOptionPane}. Within JOSM you should use these
29 * methods rather than the bare methods from {@link JOptionPane} because the methods provided
30 * by ConditionalOptionPaneUtil ensure that a dialog window is always on top and isn't hidden by one of the
31 * JOSM windows for detached dialogs, relation editors, history browser and the like.
32 *
33 */
34public final class ConditionalOptionPaneUtil {
35 public static final int DIALOG_DISABLED_OPTION = Integer.MIN_VALUE;
36
37 /** (preference key => return value) mappings valid for the current operation (no, those two maps cannot be combined) */
38 private static final Map<String, Integer> sessionChoices = new HashMap<>();
39 /** (preference key =&gt; return value) mappings valid for the current session */
40 private static final Map<String, Integer> immediateChoices = new HashMap<>();
41 /** a set indication that (preference key) is or may be stored for the currently active bulk operation */
42 private static final Set<String> immediateActive = new HashSet<>();
43
44 /**
45 * this is a static utility class only
46 */
47 private ConditionalOptionPaneUtil() {
48 // Hide default constructor for utility classes
49 }
50
51 /**
52 * Returns the preference value for the preference key "message." + <code>prefKey</code> + ".value".
53 * The default value if the preference key is missing is -1.
54 *
55 * @param prefKey the preference key
56 * @return the preference value for the preference key "message." + <code>prefKey</code> + ".value"
57 */
58 public static int getDialogReturnValue(String prefKey) {
59 return Utils.firstNonNull(immediateChoices.get(prefKey),
60 sessionChoices.get(prefKey),
61 !Config.getPref().getBoolean("message." + prefKey, true) ? Config.getPref().getInt("message." + prefKey + ".value", -1) : -1
62 );
63 }
64
65 /**
66 * Marks the beginning of a bulk operation in order to provide a "Do not show again (this operation)" option.
67 * @param prefKey the preference key
68 */
69 public static void startBulkOperation(final String prefKey) {
70 immediateActive.add(prefKey);
71 }
72
73 /**
74 * Determines whether the key has been marked to be part of a bulk operation
75 * (in order to provide a "Do not show again (this operation)" option).
76 * @param prefKey the preference key
77 * @return {@code true} if the key has been marked to be part of a bulk operation
78 */
79 public static boolean isInBulkOperation(final String prefKey) {
80 return immediateActive.contains(prefKey);
81 }
82
83 /**
84 * Marks the ending of a bulk operation. Removes the "Do not show again (this operation)" result value.
85 * @param prefKey the preference key
86 */
87 public static void endBulkOperation(final String prefKey) {
88 immediateActive.remove(prefKey);
89 immediateChoices.remove(prefKey);
90 }
91
92 /**
93 * Displays an confirmation dialog with some option buttons given by <code>optionType</code>.
94 * It is always on top even if there are other open windows like detached dialogs,
95 * relation editors, history browsers and the like.
96 *
97 * Set <code>optionType</code> to {@link JOptionPane#YES_NO_OPTION} for a dialog with a YES and
98 * a NO button.
99
100 * Set <code>optionType</code> to {@link JOptionPane#YES_NO_CANCEL_OPTION} for a dialog with a YES,
101 * a NO and a CANCEL button
102 *
103 * Returns one of the constants JOptionPane.YES_OPTION, JOptionPane.NO_OPTION,
104 * JOptionPane.CANCEL_OPTION or JOptionPane.CLOSED_OPTION depending on the action chosen by
105 * the user.
106 *
107 * @param preferenceKey the preference key
108 * @param parent the parent component
109 * @param message the message
110 * @param title the title
111 * @param optionType the option type
112 * @param messageType the message type
113 * @param options a list of options
114 * @param defaultOption the default option; only meaningful if options is used; can be null
115 *
116 * @return the option selected by user.
117 * {@link JOptionPane#CLOSED_OPTION} if the dialog was closed.
118 * {@link JOptionPane#YES_OPTION} if <code>GraphicsEnvironment.isHeadless</code> returns <code>true</code>
119 */
120 public static int showOptionDialog(String preferenceKey, Component parent, Object message, String title, int optionType,
121 int messageType, Object[] options, Object defaultOption) {
122 int ret = getDialogReturnValue(preferenceKey);
123 if (isYesOrNo(ret))
124 return ret;
125 MessagePanel pnl = new MessagePanel(message, isInBulkOperation(preferenceKey));
126 if (GraphicsEnvironment.isHeadless()) {
127 // for unit tests
128 ret = JOptionPane.YES_OPTION;
129 } else {
130 ret = JOptionPane.showOptionDialog(parent, pnl, title, optionType, messageType, null, options, defaultOption);
131 }
132 if (isYesOrNo(ret)) {
133 pnl.getNotShowAgain().store(preferenceKey, ret);
134 }
135 return ret;
136 }
137
138 /**
139 * Displays a confirmation dialog with some option buttons given by <code>optionType</code>.
140 * It is always on top even if there are other open windows like detached dialogs,
141 * relation editors, history browsers and the like.
142 *
143 * Set <code>optionType</code> to {@link JOptionPane#YES_NO_OPTION} for a dialog with a YES and
144 * a NO button.
145
146 * Set <code>optionType</code> to {@link JOptionPane#YES_NO_CANCEL_OPTION} for a dialog with a YES,
147 * a NO and a CANCEL button
148 *
149 * Replies true, if the selected option is equal to <code>trueOption</code>, otherwise false.
150 * Replies true, if the dialog is not displayed because the respective preference option
151 * <code>preferenceKey</code> is set to false and the user has previously chosen
152 * <code>trueOption</code>.
153 *
154 * @param preferenceKey the preference key
155 * @param parent the parent component
156 * @param message the message
157 * @param title the title
158 * @param optionType the option type
159 * @param messageType the message type
160 * @param trueOption if this option is selected the method replies true
161 *
162 *
163 * @return true, if the selected option is equal to <code>trueOption</code>, otherwise false.
164 * {@code trueOption} if <code>GraphicsEnvironment.isHeadless</code> returns <code>true</code>
165 *
166 * @see JOptionPane#INFORMATION_MESSAGE
167 * @see JOptionPane#WARNING_MESSAGE
168 * @see JOptionPane#ERROR_MESSAGE
169 */
170 public static boolean showConfirmationDialog(String preferenceKey, Component parent, Object message, String title,
171 int optionType, int messageType, int trueOption) {
172 int ret = getDialogReturnValue(preferenceKey);
173 if (isYesOrNo(ret))
174 return ret == trueOption;
175 MessagePanel pnl = new MessagePanel(message, isInBulkOperation(preferenceKey));
176 if (GraphicsEnvironment.isHeadless()) {
177 // for unit tests
178 ret = trueOption;
179 } else {
180 ret = JOptionPane.showConfirmDialog(parent, pnl, title, optionType, messageType);
181 }
182 if (isYesOrNo(ret)) {
183 pnl.getNotShowAgain().store(preferenceKey, ret);
184 }
185 return ret == trueOption;
186 }
187
188 private static boolean isYesOrNo(int returnCode) {
189 return (returnCode == JOptionPane.YES_OPTION) || (returnCode == JOptionPane.NO_OPTION);
190 }
191
192 /**
193 * Displays an message in modal dialog with an OK button. Makes sure the dialog
194 * is always on top even if there are other open windows like detached dialogs,
195 * relation editors, history browsers and the like.
196 *
197 * If there is a preference with key <code>preferenceKey</code> and value <code>false</code>
198 * the dialog is not show.
199 *
200 * @param preferenceKey the preference key
201 * @param parent the parent component
202 * @param message the message
203 * @param title the title
204 * @param messageType the message type
205 *
206 * @see JOptionPane#INFORMATION_MESSAGE
207 * @see JOptionPane#WARNING_MESSAGE
208 * @see JOptionPane#ERROR_MESSAGE
209 */
210 public static void showMessageDialog(String preferenceKey, Component parent, Object message, String title, int messageType) {
211 if (getDialogReturnValue(preferenceKey) == Integer.MAX_VALUE)
212 return;
213 MessagePanel pnl = new MessagePanel(message, isInBulkOperation(preferenceKey));
214 JOptionPane.showMessageDialog(parent, pnl, title, messageType);
215 pnl.getNotShowAgain().store(preferenceKey, Integer.MAX_VALUE);
216 }
217
218 /**
219 * An enum designating how long to not show this message again, i.e., for how long to store
220 */
221 enum NotShowAgain {
222 NO, OPERATION, SESSION, PERMANENT;
223
224 /**
225 * Stores the dialog result {@code value} at the corresponding place.
226 * @param prefKey the preference key
227 * @param value the dialog result
228 */
229 void store(String prefKey, Integer value) {
230 switch (this) {
231 case NO:
232 break;
233 case OPERATION:
234 immediateChoices.put(prefKey, value);
235 break;
236 case SESSION:
237 sessionChoices.put(prefKey, value);
238 break;
239 case PERMANENT:
240 Config.getPref().putBoolean("message." + prefKey, false);
241 Config.getPref().putInt("message." + prefKey + ".value", value);
242 break;
243 }
244 }
245
246 String getLabel() {
247 switch (this) {
248 case NO:
249 return tr("Show this dialog again the next time");
250 case OPERATION:
251 return tr("Do not show again (this operation)");
252 case SESSION:
253 return tr("Do not show again (this session)");
254 case PERMANENT:
255 return tr("Do not show again (remembers choice)");
256 }
257 throw new IllegalStateException();
258 }
259 }
260
261 /**
262 * This is a message panel used in dialogs which can be enabled/disabled with a preference setting.
263 * In addition to the normal message any {@link JOptionPane} would display it includes
264 * a checkbox for enabling/disabling this particular dialog.
265 *
266 */
267 static class MessagePanel extends JPanel {
268 private final JRadioButton cbShowPermanentDialog = new JRadioButton(NotShowAgain.PERMANENT.getLabel());
269 private final JRadioButton cbShowSessionDialog = new JRadioButton(NotShowAgain.SESSION.getLabel());
270 private final JRadioButton cbShowImmediateDialog = new JRadioButton(NotShowAgain.OPERATION.getLabel());
271 private final JRadioButton cbStandard = new JRadioButton(NotShowAgain.NO.getLabel());
272
273 /**
274 * Constructs a new panel.
275 * @param message the the message (null to add no message, Component instances are added directly,
276 * otherwise a JLabel with the string representation is added)
277 * @param displayImmediateOption whether to provide "Do not show again (this session)"
278 */
279 MessagePanel(Object message, boolean displayImmediateOption) {
280 cbStandard.setSelected(true);
281 ButtonGroup group = new ButtonGroup();
282 group.add(cbShowPermanentDialog);
283 group.add(cbShowSessionDialog);
284 group.add(cbShowImmediateDialog);
285 group.add(cbStandard);
286
287 setLayout(new GridBagLayout());
288 if (message instanceof Component) {
289 add((Component) message, GBC.eop());
290 } else if (message != null) {
291 add(new JMultilineLabel(message.toString()), GBC.eop());
292 }
293 add(cbShowPermanentDialog, GBC.eol());
294 add(cbShowSessionDialog, GBC.eol());
295 if (displayImmediateOption) {
296 add(cbShowImmediateDialog, GBC.eol());
297 }
298 add(cbStandard, GBC.eol());
299 }
300
301 NotShowAgain getNotShowAgain() {
302 return cbStandard.isSelected()
303 ? NotShowAgain.NO
304 : cbShowImmediateDialog.isSelected()
305 ? NotShowAgain.OPERATION
306 : cbShowSessionDialog.isSelected()
307 ? NotShowAgain.SESSION
308 : cbShowPermanentDialog.isSelected()
309 ? NotShowAgain.PERMANENT
310 : null;
311 }
312 }
313}
Note: See TracBrowser for help on using the repository browser.