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

Last change on this file since 2543 was 2512, checked in by stoecker, 15 years ago

i18n updated, fixed files to reduce problems when applying patches, fix #4017

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