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

Last change on this file since 2662 was 2662, checked in by bastiK, 14 years ago

geoimage: reworked image correlation dialog. Might still have some quirks here and there. New: displays and updates the number of matched images in the status bar while you type.

File size: 17.0 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.HelpBrowserProxy;
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 DialogNotShown = -99;
34 public static final int DialogClosedOtherwise = 0;
35 private boolean toggleable = false;
36 private String rememberSizePref = "";
37 private WindowGeometry defaultWindowGeometry = null;
38 private String togglePref = "";
39 private String toggleCheckboxText = tr("Do not show again");
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 = ExtendedDialog.DialogNotShown;
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 * * A return value of ExtendedDialog.DialogNotShown means the
188 * dialog has been toggled off in the past
189 */
190 public int getValue() {
191 return result;
192 }
193
194 private boolean setupDone = false;
195
196 /**
197 * This is called by showDialog().
198 * Only invoke from outside if you need to modify the contentPane
199 */
200 public void setupDialog() {
201 if (setupDone)
202 return;
203 setupDone = true;
204
205 setupEscListener();
206
207 JButton button;
208 JPanel buttonsPanel = new JPanel(new GridBagLayout());
209
210 for(int i=0; i < bTexts.length; i++) {
211 Action action = new AbstractAction(bTexts[i]) {
212 public void actionPerformed(ActionEvent evt) {
213 buttonAction(evt);
214 }
215 };
216
217 button = new JButton(action);
218 if (i == defaultButtonIdx-1) {
219 defaultButton = button;
220 }
221 if(bIcons != null && bIcons[i] != null) {
222 button.setIcon(ImageProvider.get(bIcons[i]));
223 }
224 if (bToolTipTexts != null && i < bToolTipTexts.length && bToolTipTexts[i] != null) {
225 button.setToolTipText(bToolTipTexts[i]);
226 }
227
228 if(i == 0) {
229 rootPane.setDefaultButton(button);
230 }
231 buttonsPanel.add(button, GBC.std().insets(2,2,2,2));
232 buttons.add(button);
233 }
234 if (showHelpButton) {
235 buttonsPanel.add(new JButton(new HelpAction()), GBC.std().insets(2,2,2,2));
236 HelpUtil.setHelpContext(getRootPane(),helpTopic);
237 }
238
239 JPanel cp = new JPanel(new GridBagLayout());
240 cp.add(content, contentConstraints);
241
242 if(toggleable) {
243 toggleCheckbox = new JCheckBox(toggleCheckboxText);
244 boolean showDialog = Main.pref.getBoolean("message."+ togglePref, true);
245 toggleCheckbox.setSelected(!showDialog);
246 cp.add(toggleCheckbox, GBC.eol().anchor(GBC.LINE_START).insets(5,5,5,5));
247 }
248
249 cp.add(buttonsPanel, GBC.eol().anchor(GBC.CENTER).insets(5,5,5,5));
250 if (placeContentInScrollPane) {
251 JScrollPane pane = new JScrollPane(cp);
252 pane.setBorder(null);
253 setContentPane(pane);
254 } else {
255 setContentPane(cp);
256 }
257 pack();
258
259 // Try to make it not larger than the parent window or at least not larger than 2/3 of the screen
260 Dimension d = getSize();
261 Dimension x = findMaxDialogSize();
262
263 boolean limitedInWidth = d.width > x.width;
264 boolean limitedInHeight = d.height > x.height;
265
266 if(x.width > 0 && d.width > x.width) {
267 d.width = x.width;
268 }
269 if(x.height > 0 && d.height > x.height) {
270 d.height = x.height;
271 }
272
273 // We have a vertical scrollbar and enough space to prevent a horizontal one
274 if(!limitedInWidth && limitedInHeight) {
275 d.width += new JScrollBar().getPreferredSize().width;
276 }
277
278 setSize(d);
279 setLocationRelativeTo(parent);
280 }
281
282 /**
283 * This gets performed whenever a button is clicked or activated
284 * @param evt the button event
285 */
286 protected void buttonAction(ActionEvent evt) {
287 String a = evt.getActionCommand();
288 for(int i=0; i < bTexts.length; i++)
289 if(bTexts[i].equals(a)) {
290 result = i+1;
291 break;
292 }
293
294 setVisible(false);
295 }
296
297 /**
298 * Tries to find a good value of how large the dialog should be
299 * @return Dimension Size of the parent Component or 2/3 of screen size if not available
300 */
301 protected Dimension findMaxDialogSize() {
302 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
303 Dimension x = new Dimension(screenSize.width*2/3, screenSize.height*2/3);
304 try {
305 if(parent != null) {
306 x = JOptionPane.getFrameForComponent(parent).getSize();
307 }
308 } catch(NullPointerException e) { }
309 return x;
310 }
311
312 /**
313 * Makes the dialog listen to ESC keypressed
314 */
315 private void setupEscListener() {
316 Action actionListener = new AbstractAction() {
317 public void actionPerformed(ActionEvent actionEvent) {
318 // 0 means that the dialog has been closed otherwise.
319 // We need to set it to zero again, in case the dialog has been re-used
320 // and the result differs from its default value
321 result = ExtendedDialog.DialogClosedOtherwise;
322 setVisible(false);
323 }
324 };
325
326 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
327 .put(KeyStroke.getKeyStroke("ESCAPE"), "ESCAPE");
328 getRootPane().getActionMap().put("ESCAPE", actionListener);
329 }
330
331 /**
332 * Override setVisible to be able to save the window geometry if required
333 */
334 @Override
335 public void setVisible(boolean visible) {
336 if (visible) {
337 repaint();
338 }
339
340 // Ensure all required variables are available
341 if(rememberSizePref.length() != 0 && defaultWindowGeometry != null) {
342 if(visible) {
343 new WindowGeometry(rememberSizePref,
344 defaultWindowGeometry).apply(this);
345 } else {
346 new WindowGeometry(this).remember(rememberSizePref);
347 }
348 }
349 super.setVisible(visible);
350 }
351
352 /**
353 * Call this if you want the dialog to remember the size set by the user.
354 * Set the pref to <code>null</code> or to an empty string to disable again.
355 * By default, it's disabled.
356 *
357 * Note: If you want to set the width of this dialog directly use the usual
358 * setSize, setPreferredSize, setMaxSize, setMinSize
359 *
360 * @param pref The preference to save the dimension to
361 * @param wg The default window geometry that should be used if no
362 * existing preference is found (only takes effect if
363 * <code>pref</code> is not null or empty
364 *
365 */
366 public ExtendedDialog setRememberWindowGeometry(String pref, WindowGeometry wg) {
367 rememberSizePref = pref == null ? "" : pref;
368 defaultWindowGeometry = wg;
369 return this;
370 }
371
372 /**
373 * Calling this will offer the user a "Do not show again" checkbox for the
374 * dialog. Default is to not offer the choice; the dialog will be shown
375 * every time. If the dialog is not shown due to the previous choice of the
376 * user, the result <code>ExtendedDialog.DialogNotShown</code> is returned
377 * @param togglePref The preference to save the checkbox state to
378 */
379 public ExtendedDialog toggleEnable(String togglePref) {
380 this.toggleable = true;
381 this.togglePref = togglePref;
382 return this;
383 }
384
385 /**
386 * Call this if you "accidentally" called toggleEnable. This doesn't need
387 * to be called for every dialog, as it's the default anyway.
388 */
389 public ExtendedDialog toggleDisable() {
390 this.toggleable = false;
391 return this;
392 }
393
394 /**
395 * Overwrites the default "Don't show again" text of the toggle checkbox
396 * if you want to give more information. Only has an effect if
397 * <code>toggleEnable</code> is set.
398 * @param text
399 */
400 public ExtendedDialog setToggleCheckboxText(String text) {
401 this.toggleCheckboxText = text;
402 return this;
403 }
404
405 /**
406 * Sets the button that will react to ENTER.
407 */
408 public ExtendedDialog setDefaultButton(int defaultButtonIdx) {
409 this.defaultButtonIdx = defaultButtonIdx;
410 return this;
411 }
412
413 /**
414 * Used in combination with toggle:
415 * If the user presses 'cancel' the toggle settings are ignored and not saved to the pref
416 * @param cancelButton index of the button that stands for cancel
417 */
418 public ExtendedDialog setCancelButton(int cancelButtonIdx) {
419 this.cancelButtonIdx = cancelButtonIdx;
420 return this;
421 }
422
423 /**
424 * This function returns true if the dialog has been set to "do not show again"
425 * @return true if dialog should not be shown again
426 */
427 private boolean toggleCheckState(String togglePref) {
428 toggleable = togglePref != null && !togglePref.equals("");
429
430 // No identifier given, so return false (= show the dialog)
431 if(!toggleable)
432 return false;
433 this.togglePref = togglePref;
434 // The pref is true, if the dialog should be shown.
435 return !(Main.pref.getBoolean("message."+ togglePref, true));
436 }
437
438 /**
439 * This function checks the state of the "Do not show again" checkbox and
440 * writes the corresponding pref
441 */
442 private void toggleSaveState() {
443 if(!toggleable || toggleCheckbox == null || result == cancelButtonIdx || result == ExtendedDialog.DialogClosedOtherwise)
444 return;
445 Main.pref.put("message."+ togglePref, !toggleCheckbox.isSelected());
446 }
447
448 /**
449 * Convenience function that converts a given string into a JMultilineLabel
450 * @param msg
451 * @return JMultilineLabel
452 */
453 private static JMultilineLabel string2label(String msg) {
454 JMultilineLabel lbl = new JMultilineLabel(msg);
455 // Make it not wider than 1/2 of the screen
456 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
457 lbl.setMaxWidth(screenSize.width/2);
458 return lbl;
459 }
460
461 /**
462 * Configures how this dialog support for context sensitive help.
463 * <ul>
464 * <li>if helpTopic is null, the dialog doesn't provide context sensitive help</li>
465 * <li>if helpTopic != null, the dialog redirect user to the help page for this helpTopic when
466 * the user clicks F1 in the dialog</li>
467 * <li>if showHelpButton is true, the dialog displays "Help" button (rightmost button in
468 * the button row)</li>
469 * </ul>
470 *
471 * @param helpTopic the help topic
472 * @param showHelpButton true, if the dialog displays a help button
473 */
474 public ExtendedDialog configureContextsensitiveHelp(String helpTopic, boolean showHelpButton) {
475 this.helpTopic = helpTopic;
476 this.showHelpButton = showHelpButton;
477 return this;
478 }
479
480 class HelpAction extends AbstractAction {
481 public HelpAction() {
482 putValue(SHORT_DESCRIPTION, tr("Show help information"));
483 putValue(NAME, tr("Help"));
484 putValue(SMALL_ICON, ImageProvider.get("help"));
485 }
486
487 public void actionPerformed(ActionEvent e) {
488 HelpBrowserProxy.getInstance().setUrlForHelpTopic(helpTopic);
489 }
490 }
491}
Note: See TracBrowser for help on using the repository browser.