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

Last change on this file since 17318 was 14380, checked in by Don-vip, 5 years ago

fix #16906 - RelationEditorActionsTest: fix for non-headless mode (patch by ris)

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