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

Last change on this file since 11604 was 9496, checked in by Don-vip, 8 years ago

massive refactoring of GenericRelationEditor. As JDialog cannot be instantiated in headless mode, extract all actions to separate classes in new package gui.dialogs.relation.actions in order to test them with JUnit

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