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

Last change on this file since 2474 was 2374, checked in by stoecker, 14 years ago

removed deprecated stuff

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