source: josm/trunk/src/org/openstreetmap/josm/gui/ExtendedDialog.java@ 2860

Last change on this file since 2860 was 2824, checked in by Gubaer, 14 years ago

fixed #4082: Upload-Dialog shows in diseabled display
Replaced WindowGeometry.apply() by WindowGeometry.applySafe(), as suggested by bomm

File size: 16.9 KB
Line 
1package org.openstreetmap.josm.gui;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4
5import java.awt.Component;
6import java.awt.Dimension;
7import java.awt.GridBagLayout;
8import java.awt.Toolkit;
9import java.awt.event.ActionEvent;
10import java.util.ArrayList;
11
12import javax.swing.AbstractAction;
13import javax.swing.Action;
14import javax.swing.JButton;
15import javax.swing.JCheckBox;
16import javax.swing.JComponent;
17import javax.swing.JDialog;
18import javax.swing.JOptionPane;
19import javax.swing.JPanel;
20import javax.swing.JScrollBar;
21import javax.swing.JScrollPane;
22import javax.swing.KeyStroke;
23
24import org.openstreetmap.josm.Main;
25import org.openstreetmap.josm.gui.help.HelpBrowser;
26import org.openstreetmap.josm.gui.help.HelpUtil;
27import org.openstreetmap.josm.tools.GBC;
28import org.openstreetmap.josm.tools.ImageProvider;
29import org.openstreetmap.josm.tools.WindowGeometry;
30
31public class ExtendedDialog extends JDialog {
32 private int result = 0;
33 public static final int DialogClosedOtherwise = 0;
34 private boolean toggleable = false;
35 private String rememberSizePref = "";
36 private WindowGeometry defaultWindowGeometry = null;
37 private String togglePref = "";
38 private int toggleValue = -1;
39 private String toggleCheckboxText = tr("Do not show again (remembers choice)");
40 private JCheckBox toggleCheckbox = null;
41 private Component parent;
42 private Component content;
43 private final String[] bTexts;
44 private String[] bToolTipTexts;
45 private String[] bIcons;
46 private int cancelButtonIdx = -1;
47 private int defaultButtonIdx = -1;
48 private JButton defaultButton = null;
49
50 /** true, if the dialog should include a help button */
51 private boolean showHelpButton;
52 /** the help topic */
53 private String helpTopic;
54
55 /**
56 * set to true if the content of the extended dialog should
57 * be placed in a {@see JScrollPane}
58 */
59 private boolean placeContentInScrollPane;
60
61 // For easy access when inherited
62 protected Object contentConstraints = GBC.eol().anchor(GBC.CENTER).fill(GBC.BOTH).insets(5,10,5,0);
63 protected ArrayList<JButton> buttons = new ArrayList<JButton>();
64
65 /**
66 * This method sets up the most basic options for the dialog. Add all more
67 * advanced features with dedicated methods.
68 * Possible features:
69 * <ul>
70 * <li><code>setButtonIcons</code></li>
71 * <li><code>setContent</code></li>
72 * <li><code>toggleEnable</code></li>
73 * <li><code>toggleDisable</code></li>
74 * <li><code>setToggleCheckboxText</code></li>
75 * <li><code>setRememberWindowGeometry</code></li>
76 * </ul>
77 *
78 * When done, call <code>showDialog</code> to display it. You can receive
79 * the user's choice using <code>getValue</code>. Have a look at this function
80 * for possible return values.
81 *
82 * @param parent The parent element that will be used for position and maximum size
83 * @param title The text that will be shown in the window titlebar
84 * @param buttonTexts String Array of the text that will appear on the buttons. The first button is the default one.
85 */
86 public ExtendedDialog(Component parent, String title, String[] buttonTexts) {
87 super(JOptionPane.getFrameForComponent(parent), title, true);
88 this.parent = parent;
89 bTexts = buttonTexts;
90 }
91
92 /**
93 * Same as above but lets you define if the dialog should be modal.
94 */
95 public ExtendedDialog(Component parent, String title, String[] buttonTexts,
96 boolean modal) {
97 super(JOptionPane.getFrameForComponent(parent), title, modal);
98 this.parent = parent;
99 bTexts = buttonTexts;
100 }
101
102 /**
103 * Allows decorating the buttons with icons. Expects an String[] with paths
104 * to images relative to JOSM/images.
105 * @param buttonIcons
106 */
107 public ExtendedDialog setButtonIcons(String[] buttonIcons) {
108 this.bIcons = buttonIcons;
109 return this;
110 }
111
112 /**
113 * Allows decorating the buttons with tooltips. Expects an String[] with translated
114 * tooltip texts.
115 *
116 * @param toolTipTexts the tool tip texts. Ignored, if null.
117 */
118 public ExtendedDialog setToolTipTexts(String[] toolTipTexts) {
119 this.bToolTipTexts = toolTipTexts;
120 return this;
121 }
122
123 /**
124 * Sets the content that will be displayed in the message dialog.
125 *
126 * Note that depending on your other settings more UI elements may appear.
127 * The content is played on top of the other elements though.
128 *
129 * @param content Any element that can be displayed in the message dialog
130 */
131 public ExtendedDialog setContent(Component content) {
132 return setContent(content, true);
133 }
134
135 /**
136 * Sets the content that will be displayed in the message dialog.
137 *
138 * Note that depending on your other settings more UI elements may appear.
139 * The content is played on top of the other elements though.
140 *
141 * @param content Any element that can be displayed in the message dialog
142 * @param placeContentInScrollPane if true, places the content in a JScrollPane
143 *
144 */
145 public ExtendedDialog setContent(Component content, boolean placeContentInScrollPane) {
146 this.content = content;
147 this.placeContentInScrollPane = placeContentInScrollPane;
148 return this;
149 }
150
151 /**
152 * Sets the message that will be displayed. The String will be automatically
153 * wrapped if it is too long.
154 *
155 * Note that depending on your other settings more UI elements may appear.
156 * The content is played on top of the other elements though.
157 *
158 * @param message The text that should be shown to the user
159 */
160 public ExtendedDialog setContent(String message) {
161 return setContent(string2label(message), false);
162 }
163
164 /**
165 * Show the dialog to the user. Call this after you have set all options
166 * for the dialog. You can retrieve the result using <code>getValue</code>
167 */
168 public ExtendedDialog showDialog() {
169 // Check if the user has set the dialog to not be shown again
170 if(toggleCheckState(togglePref)) {
171 result = toggleValue;
172 return this;
173 }
174
175 setupDialog();
176 if (defaultButton != null) {
177 getRootPane().setDefaultButton(defaultButton);
178 }
179 setVisible(true);
180 toggleSaveState();
181 return this;
182 }
183
184 /**
185 * @return int * The selected button. The count starts with 1.
186 * * A return value of ExtendedDialog.DialogClosedOtherwise means the dialog has been closed otherwise.
187 */
188 public int getValue() {
189 return result;
190 }
191
192 private boolean setupDone = false;
193
194 /**
195 * This is called by showDialog().
196 * Only invoke from outside if you need to modify the contentPane
197 */
198 public void setupDialog() {
199 if (setupDone)
200 return;
201 setupDone = true;
202
203 setupEscListener();
204
205 JButton button;
206 JPanel buttonsPanel = new JPanel(new GridBagLayout());
207
208 for(int i=0; i < bTexts.length; i++) {
209 Action action = new AbstractAction(bTexts[i]) {
210 public void actionPerformed(ActionEvent evt) {
211 buttonAction(evt);
212 }
213 };
214
215 button = new JButton(action);
216 if (i == defaultButtonIdx-1) {
217 defaultButton = button;
218 }
219 if(bIcons != null && bIcons[i] != null) {
220 button.setIcon(ImageProvider.get(bIcons[i]));
221 }
222 if (bToolTipTexts != null && i < bToolTipTexts.length && bToolTipTexts[i] != null) {
223 button.setToolTipText(bToolTipTexts[i]);
224 }
225
226 if(i == 0) {
227 rootPane.setDefaultButton(button);
228 }
229 buttonsPanel.add(button, GBC.std().insets(2,2,2,2));
230 buttons.add(button);
231 }
232 if (showHelpButton) {
233 buttonsPanel.add(new JButton(new HelpAction()), GBC.std().insets(2,2,2,2));
234 HelpUtil.setHelpContext(getRootPane(),helpTopic);
235 }
236
237 JPanel cp = new JPanel(new GridBagLayout());
238 cp.add(content, contentConstraints);
239
240 if(toggleable) {
241 toggleCheckbox = new JCheckBox(toggleCheckboxText);
242 boolean showDialog = Main.pref.getBoolean("message."+ togglePref, true);
243 toggleCheckbox.setSelected(!showDialog);
244 cp.add(toggleCheckbox, GBC.eol().anchor(GBC.LINE_START).insets(5,5,5,5));
245 }
246
247 cp.add(buttonsPanel, GBC.eol().anchor(GBC.CENTER).insets(5,5,5,5));
248 if (placeContentInScrollPane) {
249 JScrollPane pane = new JScrollPane(cp);
250 pane.setBorder(null);
251 setContentPane(pane);
252 } else {
253 setContentPane(cp);
254 }
255 pack();
256
257 // Try to make it not larger than the parent window or at least not larger than 2/3 of the screen
258 Dimension d = getSize();
259 Dimension x = findMaxDialogSize();
260
261 boolean limitedInWidth = d.width > x.width;
262 boolean limitedInHeight = d.height > x.height;
263
264 if(x.width > 0 && d.width > x.width) {
265 d.width = x.width;
266 }
267 if(x.height > 0 && d.height > x.height) {
268 d.height = x.height;
269 }
270
271 // We have a vertical scrollbar and enough space to prevent a horizontal one
272 if(!limitedInWidth && limitedInHeight) {
273 d.width += new JScrollBar().getPreferredSize().width;
274 }
275
276 setSize(d);
277 setLocationRelativeTo(parent);
278 }
279
280 /**
281 * This gets performed whenever a button is clicked or activated
282 * @param evt the button event
283 */
284 protected void buttonAction(ActionEvent evt) {
285 String a = evt.getActionCommand();
286 for(int i=0; i < bTexts.length; i++)
287 if(bTexts[i].equals(a)) {
288 result = i+1;
289 break;
290 }
291
292 setVisible(false);
293 }
294
295 /**
296 * Tries to find a good value of how large the dialog should be
297 * @return Dimension Size of the parent Component or 2/3 of screen size if not available
298 */
299 protected Dimension findMaxDialogSize() {
300 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
301 Dimension x = new Dimension(screenSize.width*2/3, screenSize.height*2/3);
302 try {
303 if(parent != null) {
304 x = JOptionPane.getFrameForComponent(parent).getSize();
305 }
306 } catch(NullPointerException e) { }
307 return x;
308 }
309
310 /**
311 * Makes the dialog listen to ESC keypressed
312 */
313 private void setupEscListener() {
314 Action actionListener = new AbstractAction() {
315 public void actionPerformed(ActionEvent actionEvent) {
316 // 0 means that the dialog has been closed otherwise.
317 // We need to set it to zero again, in case the dialog has been re-used
318 // and the result differs from its default value
319 result = ExtendedDialog.DialogClosedOtherwise;
320 setVisible(false);
321 }
322 };
323
324 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
325 .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
326 getRootPane().getActionMap().put("ESCAPE", actionListener);
327 }
328
329 /**
330 * Override setVisible to be able to save the window geometry if required
331 */
332 @Override
333 public void setVisible(boolean visible) {
334 if (visible) {
335 repaint();
336 }
337
338 // Ensure all required variables are available
339 if(rememberSizePref.length() != 0 && defaultWindowGeometry != null) {
340 if(visible) {
341 new WindowGeometry(rememberSizePref,
342 defaultWindowGeometry).applySafe(this);
343 } else {
344 new WindowGeometry(this).remember(rememberSizePref);
345 }
346 }
347 super.setVisible(visible);
348 }
349
350 /**
351 * Call this if you want the dialog to remember the size set by the user.
352 * Set the pref to <code>null</code> or to an empty string to disable again.
353 * By default, it's disabled.
354 *
355 * Note: If you want to set the width of this dialog directly use the usual
356 * setSize, setPreferredSize, setMaxSize, setMinSize
357 *
358 * @param pref The preference to save the dimension to
359 * @param wg The default window geometry that should be used if no
360 * existing preference is found (only takes effect if
361 * <code>pref</code> is not null or empty
362 *
363 */
364 public ExtendedDialog setRememberWindowGeometry(String pref, WindowGeometry wg) {
365 rememberSizePref = pref == null ? "" : pref;
366 defaultWindowGeometry = wg;
367 return this;
368 }
369
370 /**
371 * Calling this will offer the user a "Do not show again" checkbox for the
372 * dialog. Default is to not offer the choice; the dialog will be shown
373 * every time.
374 * @param togglePref The preference to save the checkbox state to
375 */
376 public ExtendedDialog toggleEnable(String togglePref) {
377 this.toggleable = true;
378 this.togglePref = togglePref;
379 return this;
380 }
381
382 /**
383 * Call this if you "accidentally" called toggleEnable. This doesn't need
384 * to be called for every dialog, as it's the default anyway.
385 */
386 public ExtendedDialog toggleDisable() {
387 this.toggleable = false;
388 return this;
389 }
390
391 /**
392 * Overwrites the default "Don't show again" text of the toggle checkbox
393 * if you want to give more information. Only has an effect if
394 * <code>toggleEnable</code> is set.
395 * @param text
396 */
397 public ExtendedDialog setToggleCheckboxText(String text) {
398 this.toggleCheckboxText = text;
399 return this;
400 }
401
402 /**
403 * Sets the button that will react to ENTER.
404 */
405 public ExtendedDialog setDefaultButton(int defaultButtonIdx) {
406 this.defaultButtonIdx = defaultButtonIdx;
407 return this;
408 }
409
410 /**
411 * Used in combination with toggle:
412 * If the user presses 'cancel' the toggle settings are ignored and not saved to the pref
413 * @param cancelButton index of the button that stands for cancel
414 */
415 public ExtendedDialog setCancelButton(int cancelButtonIdx) {
416 this.cancelButtonIdx = cancelButtonIdx;
417 return this;
418 }
419
420 /**
421 * This function returns true if the dialog has been set to "do not show again"
422 * @return true if dialog should not be shown again
423 */
424 private boolean toggleCheckState(String togglePref) {
425 toggleable = togglePref != null && !togglePref.equals("");
426
427 toggleValue = Main.pref.getInteger("message."+togglePref+".value", -1);
428 // No identifier given, so return false (= show the dialog)
429 if(!toggleable || toggleValue == -1)
430 return false;
431 this.togglePref = togglePref;
432 // The pref is true, if the dialog should be shown.
433 return !(Main.pref.getBoolean("message."+ togglePref, true));
434 }
435
436 /**
437 * This function checks the state of the "Do not show again" checkbox and
438 * writes the corresponding pref
439 */
440 private void toggleSaveState() {
441 if(!toggleable || toggleCheckbox == null || result == cancelButtonIdx || result == ExtendedDialog.DialogClosedOtherwise)
442 return;
443 Main.pref.put("message."+ togglePref, !toggleCheckbox.isSelected());
444 Main.pref.putInteger("message."+togglePref+".value", result);
445 }
446
447 /**
448 * Convenience function that converts a given string into a JMultilineLabel
449 * @param msg
450 * @return JMultilineLabel
451 */
452 private static JMultilineLabel string2label(String msg) {
453 JMultilineLabel lbl = new JMultilineLabel(msg);
454 // Make it not wider than 1/2 of the screen
455 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
456 lbl.setMaxWidth(screenSize.width/2);
457 return lbl;
458 }
459
460 /**
461 * Configures how this dialog support for context sensitive help.
462 * <ul>
463 * <li>if helpTopic is null, the dialog doesn't provide context sensitive help</li>
464 * <li>if helpTopic != null, the dialog redirect user to the help page for this helpTopic when
465 * the user clicks F1 in the dialog</li>
466 * <li>if showHelpButton is true, the dialog displays "Help" button (rightmost button in
467 * the button row)</li>
468 * </ul>
469 *
470 * @param helpTopic the help topic
471 * @param showHelpButton true, if the dialog displays a help button
472 */
473 public ExtendedDialog configureContextsensitiveHelp(String helpTopic, boolean showHelpButton) {
474 this.helpTopic = helpTopic;
475 this.showHelpButton = showHelpButton;
476 return this;
477 }
478
479 class HelpAction extends AbstractAction {
480 public HelpAction() {
481 putValue(SHORT_DESCRIPTION, tr("Show help information"));
482 putValue(NAME, tr("Help"));
483 putValue(SMALL_ICON, ImageProvider.get("help"));
484 }
485
486 public void actionPerformed(ActionEvent e) {
487 HelpBrowser.setUrlForHelpTopic(helpTopic);
488 }
489 }
490}
Note: See TracBrowser for help on using the repository browser.