source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/ToggleDialog.java@ 3016

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

pop up the conflict dialog when a conflict has been detected

  • Property svn:eol-style set to native
File size: 19.5 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.FlowLayout;
10import java.awt.Graphics;
11import java.awt.GridBagLayout;
12import java.awt.GridLayout;
13import java.awt.Image;
14import java.awt.Rectangle;
15import java.awt.event.ActionEvent;
16import java.awt.event.ActionListener;
17import java.awt.event.ComponentAdapter;
18import java.awt.event.ComponentEvent;
19import java.awt.event.MouseAdapter;
20import java.awt.event.MouseEvent;
21import java.awt.event.WindowAdapter;
22import java.awt.event.WindowEvent;
23
24import javax.swing.AbstractAction;
25import javax.swing.BorderFactory;
26import javax.swing.ImageIcon;
27import javax.swing.JButton;
28import javax.swing.JComponent;
29import javax.swing.JDialog;
30import javax.swing.JLabel;
31import javax.swing.JOptionPane;
32import javax.swing.JPanel;
33
34import org.openstreetmap.josm.Main;
35import org.openstreetmap.josm.actions.JosmAction;
36import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
37import org.openstreetmap.josm.gui.help.HelpUtil;
38import org.openstreetmap.josm.gui.help.Helpful;
39import org.openstreetmap.josm.tools.GBC;
40import org.openstreetmap.josm.tools.ImageProvider;
41import org.openstreetmap.josm.tools.Shortcut;
42
43/**
44 * This class is a toggle dialog that can be turned on and off.
45 *
46 *
47 */
48public class ToggleDialog extends JPanel implements Helpful {
49 /** The action to toggle this dialog */
50 protected ToggleDialogAction toggleAction;
51 protected String preferencePrefix;
52 final protected String name;
53
54 /** DialogsPanel that manages all ToggleDialogs */
55 protected DialogsPanel dialogsPanel;
56
57 protected TitleBar titleBar;
58
59 /**
60 * Indicates whether the dialog is showing or not.
61 */
62 protected boolean isShowing;
63 /**
64 * If isShowing is true, indicates whether the dialog is docked or not, e. g.
65 * shown as part of the main window or as a separate dialog window.
66 */
67 protected boolean isDocked;
68 /**
69 * If isShowing and isDocked are true, indicates whether the dialog is
70 * currently minimized or not.
71 */
72 protected boolean isCollapsed;
73
74 /** the preferred height if the toggle dialog is expanded */
75 private int preferredHeight;
76
77 /** the label in the title bar which shows whether the toggle dialog is expanded or collapsed */
78 private JLabel lblMinimized;
79
80 /** the JDialog displaying the toggle dialog as undocked dialog */
81 protected JDialog detachedDialog;
82
83 /**
84 * Constructor
85 * (see below)
86 */
87 public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
88 this(name, iconName, tooltip, shortcut, preferredHeight, false);
89 }
90 /**
91 * Constructor
92 *
93 * @param name the name of the dialog
94 * @param iconName the name of the icon to be displayed
95 * @param tooltip the tool tip
96 * @param shortcut the shortcut
97 * @param preferredHeight the preferred height for the dialog
98 * @param defShow if the dialog should be shown by default, if there is no preference
99 */
100 public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight, boolean defShow) {
101 super(new BorderLayout());
102 this.preferencePrefix = iconName;
103 this.name = name;
104
105 /** Use the full width of the parent element */
106 setPreferredSize(new Dimension(0, preferredHeight));
107 /** Override any minimum sizes of child elements so the user can resize freely */
108 setMinimumSize(new Dimension(0,0));
109 this.preferredHeight = preferredHeight;
110 toggleAction = new ToggleDialogAction(this, name, "dialogs/"+iconName, tooltip, shortcut, iconName);
111 String helpId = "Dialog/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
112 toggleAction.putValue("help", helpId.substring(0, helpId.length()-6));
113
114 setLayout(new BorderLayout());
115
116 /** show the minimize button */
117 lblMinimized = new JLabel(ImageProvider.get("misc", "normal"));
118 titleBar = new TitleBar(name, iconName);
119 add(titleBar, BorderLayout.NORTH);
120
121 setBorder(BorderFactory.createEtchedBorder());
122
123 isShowing = Main.pref.getBoolean(preferencePrefix+".visible", defShow);
124 isDocked = Main.pref.getBoolean(preferencePrefix+".docked", true);
125 isCollapsed = Main.pref.getBoolean(preferencePrefix+".minimized", false);
126 }
127
128 /**
129 * The action to toggle the visibility state of this toggle dialog.
130 *
131 * Emits {@see PropertyChangeEvent}s for the property <tt>selected</tt>:
132 * <ul>
133 * <li>true, if the dialog is currently visible</li>
134 * <li>false, if the dialog is currently invisible</li>
135 * </ul>
136 *
137 */
138 public final static class ToggleDialogAction extends JosmAction {
139
140 private ToggleDialog dialog;
141
142 private ToggleDialogAction(ToggleDialog dialog, String name, String iconName, String tooltip, Shortcut shortcut, String prefname) {
143 super(name, iconName, tooltip, shortcut, false);
144 this.dialog = dialog;
145 }
146
147 public void actionPerformed(ActionEvent e) {
148 dialog.toggleButtonHook();
149 if (dialog.isShowing) {
150 dialog.hideDialog();
151 dialog.dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
152 } else {
153 dialog.showDialog();
154 if (dialog.isDocked && dialog.isCollapsed) {
155 dialog.expand();
156 }
157 if (dialog.isDocked) {
158 dialog.dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, dialog);
159 }
160 }
161 }
162
163 @Override
164 public void destroy() {
165 super.destroy();
166 dialog = null;
167 }
168 }
169
170 /**
171 * Shows the dialog
172 */
173 public void showDialog() {
174 setIsShowing(true);
175 if (!isDocked) {
176 detach();
177 } else {
178 dock();
179 this.setVisible(true);
180 }
181 // toggling the selected value in order to enforce PropertyChangeEvents
182 setIsShowing(true);
183 toggleAction.putValue("selected", false);
184 toggleAction.putValue("selected", true);
185 showNotify();
186 }
187
188 /**
189 * Changes the state of the dialog such that the user can see the content
190 * and takes care of the panel reconstruction.
191 */
192 public void unfurlDialog()
193 {
194 if (isDialogInDefaultView())
195 return;
196 if (isDialogInCollapsedView()) {
197 expand();
198 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, this);
199 } else if (!isDialogShowing()) {
200 showDialog();
201 if (isDocked && isCollapsed) {
202 expand();
203 }
204 if (isDocked) {
205 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, this);
206 }
207 }
208 }
209
210 /**
211 * Hides the dialog
212 */
213 public void hideDialog() {
214 closeDetachedDialog();
215 this.setVisible(false);
216 setIsShowing(false);
217 toggleAction.putValue("selected", false);
218 hideNotify();
219 }
220
221 /**
222 * Displays the toggle dialog in the toggle dialog view on the right
223 * of the main map window.
224 *
225 */
226 protected void dock() {
227 detachedDialog = null;
228 titleBar.setVisible(true);
229 setIsDocked(true);
230 }
231
232 /**
233 * Display the dialog in a detached window.
234 *
235 */
236 protected void detach() {
237 setContentVisible(true);
238 this.setVisible(true);
239 titleBar.setVisible(false);
240 detachedDialog = new DetachedDialog();
241 detachedDialog.setVisible(true);
242 setIsShowing(true);
243 setIsDocked(false);
244 }
245
246 /**
247 * Collapses the toggle dialog to the title bar only
248 *
249 */
250 public void collapse() {
251 // if (isShowing && isDocked && !isCollapsed) {
252 if (isDialogInDefaultView()) {
253 setContentVisible(false);
254 setIsCollapsed(true);
255 setPreferredSize(new Dimension(0,20));
256 setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
257 setMinimumSize(new Dimension(Integer.MAX_VALUE,20));
258 lblMinimized.setIcon(ImageProvider.get("misc", "minimized"));
259 hideNotify();
260 }
261 else throw new IllegalStateException();
262 }
263
264 /**
265 * Expands the toggle dialog
266 */
267 protected void expand() {
268 // if (isShowing && isDocked && isCollapsed) {
269 if (isDialogInCollapsedView()) {
270 setContentVisible(true);
271 setIsCollapsed(false);
272 setPreferredSize(new Dimension(0,preferredHeight));
273 setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
274 lblMinimized.setIcon(ImageProvider.get("misc", "normal"));
275 showNotify();
276 }
277 else throw new IllegalStateException();
278 }
279
280 /**
281 * Sets the visibility of all components in this toggle dialog, except the title bar
282 *
283 * @param visible true, if the components should be visible; false otherwise
284 */
285 protected void setContentVisible(boolean visible) {
286 Component comps[] = getComponents();
287 for(int i=0; i<comps.length; i++) {
288 if(comps[i] != titleBar) {
289 comps[i].setVisible(visible);
290 }
291 }
292 }
293
294 public void destroy() {
295 closeDetachedDialog();
296 hideNotify();
297 }
298
299 /**
300 * Closes the detached dialog if this toggle dialog is currently displayed
301 * in a detached dialog.
302 *
303 */
304 public void closeDetachedDialog() {
305 if (detachedDialog != null) {
306 detachedDialog.setVisible(false);
307 detachedDialog.getContentPane().removeAll();
308 detachedDialog.dispose();
309 }
310 }
311
312 /**
313 * Called when toggle dialog is shown (after it was created or expanded). Descendants may overwrite this
314 * method, it's a good place to register listeners needed to keep dialog updated
315 */
316 public void showNotify() {
317
318 }
319
320 /**
321 * Called when toggle dialog is hidden (collapsed, removed, MapFrame is removed, ...). Good place to unregister
322 * listeners
323 */
324 public void hideNotify() {
325
326 }
327
328 /**
329 * The title bar displayed in docked mode
330 *
331 */
332 protected class TitleBar extends JPanel {
333 final private JLabel lblTitle;
334 final private JComponent lblTitle_weak;
335
336 public TitleBar(String toggleDialogName, String iconName) {
337 setLayout(new GridBagLayout());
338 lblMinimized = new JLabel(ImageProvider.get("misc", "normal"));
339 add(lblMinimized);
340
341 // scale down the dialog icon
342 ImageIcon inIcon = ImageProvider.get("dialogs", iconName);
343 ImageIcon smallIcon = new ImageIcon(inIcon.getImage().getScaledInstance(16 , 16, Image.SCALE_SMOOTH));
344 lblTitle = new JLabel("",smallIcon, JLabel.TRAILING);
345 lblTitle.setIconTextGap(8);
346
347 JPanel conceal = new JPanel();
348 conceal.add(lblTitle);
349 conceal.setVisible(false);
350 add(conceal, GBC.std());
351
352 // Cannot add the label directly since it would displace other elements on resize
353 lblTitle_weak = new JComponent() {
354 @Override
355 public void paintComponent(Graphics g) {
356 lblTitle.paint(g);
357 }
358 };
359 lblTitle_weak.setPreferredSize(new Dimension(Integer.MAX_VALUE,20));
360 lblTitle_weak.setMinimumSize(new Dimension(0,20));
361 add(lblTitle_weak, GBC.std().fill(GBC.HORIZONTAL));
362
363 addMouseListener(
364 new MouseAdapter() {
365 @Override
366 public void mouseClicked(MouseEvent e) {
367 // toggleExpandedState
368 if (isCollapsed) {
369 expand();
370 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, ToggleDialog.this);
371 } else {
372 collapse();
373 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
374 }
375 }
376 }
377 );
378
379 // show the sticky button
380 JButton sticky = new JButton(ImageProvider.get("misc", "sticky"));
381 sticky.setToolTipText(tr("Undock the panel"));
382 sticky.setBorder(BorderFactory.createEmptyBorder());
383 sticky.addActionListener(
384 new ActionListener(){
385 public void actionPerformed(ActionEvent e) {
386 detach();
387 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
388 }
389 }
390 );
391 add(sticky);
392
393 // show the close button
394 JButton close = new JButton(ImageProvider.get("misc", "close"));
395 close.setToolTipText(tr("Close this panel. You can reopen it with the buttons in the left toolbar."));
396 close.setBorder(BorderFactory.createEmptyBorder());
397 close.addActionListener(
398 new ActionListener(){
399 public void actionPerformed(ActionEvent e) {
400 hideDialog();
401 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
402 }
403 }
404 );
405 add(close);
406 setToolTipText(tr("Click to minimize/maximize the panel content"));
407 setTitle(toggleDialogName);
408 }
409
410 public void setTitle(String title) {
411 lblTitle.setText(title);
412 lblTitle_weak.repaint();
413 }
414
415 public String getTitle() {
416 return lblTitle.getText();
417 }
418 }
419
420 /**
421 * The dialog class used to display toggle dialogs in a detached window.
422 *
423 */
424 private class DetachedDialog extends JDialog{
425 public DetachedDialog() {
426 super(JOptionPane.getFrameForComponent(Main.parent));
427 getContentPane().add(ToggleDialog.this);
428 addWindowListener(new WindowAdapter(){
429 @Override public void windowClosing(WindowEvent e) {
430 rememberGeometry();
431 getContentPane().removeAll();
432 dispose();
433 if (dockWhenClosingDetachedDlg()) {
434 dock();
435 if (isDialogInCollapsedView()) {
436 expand();
437 }
438 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
439 } else {
440 hideDialog();
441 }
442 }
443 });
444 addComponentListener(new ComponentAdapter() {
445 @Override public void componentMoved(ComponentEvent e) {
446 rememberGeometry();
447 }
448 @Override public void componentResized(ComponentEvent e) {
449 rememberGeometry();
450 }
451 });
452
453 String bounds = Main.pref.get(preferencePrefix+".bounds",null);
454 if (bounds != null) {
455 String[] b = bounds.split(",");
456 setBounds(getDetachedGeometry(new Rectangle(
457 Integer.parseInt(b[0]),Integer.parseInt(b[1]),Integer.parseInt(b[2]),Integer.parseInt(b[3]))));
458 } else {
459 ToggleDialog.this.setPreferredSize(ToggleDialog.this.getDefaultDetachedSize());
460 pack();
461 setLocationRelativeTo(Main.parent);
462 }
463 setTitle(titleBar.getTitle());
464 HelpUtil.setHelpContext(getRootPane(), helpTopic());
465 }
466
467 protected void rememberGeometry() {
468 Main.pref.put(preferencePrefix+".bounds", detachedDialog.getX()+","+detachedDialog.getY()+","+detachedDialog.getWidth()+","+detachedDialog.getHeight());
469 }
470 }
471
472 /**
473 * Replies the action to toggle the visible state of this toggle dialog
474 *
475 * @return the action to toggle the visible state of this toggle dialog
476 */
477 public AbstractAction getToggleAction() {
478 return toggleAction;
479 }
480
481 /**
482 * Replies the prefix for the preference settings of this dialog.
483 *
484 * @return the prefix for the preference settings of this dialog.
485 */
486 public String getPreferencePrefix() {
487 return preferencePrefix;
488 }
489
490 /**
491 * Sets the dialogsPanel managing all toggle dialogs
492 */
493 public void setDialogsPanel(DialogsPanel dialogsPanel) {
494 this.dialogsPanel = dialogsPanel;
495 }
496
497 /**
498 * Replies the name of this toggle dialog
499 */
500 @Override
501 public String getName() {
502 return "toggleDialog." + preferencePrefix;
503 }
504
505 /**
506 * Sets the title
507 */
508 public void setTitle(String title) {
509 titleBar.setTitle(title);
510 if (detachedDialog != null) {
511 detachedDialog.setTitle(title);
512 }
513 }
514
515 protected void setIsShowing(boolean val) {
516 isShowing = val;
517 Main.pref.put(preferencePrefix+".visible", val);
518 stateChanged();
519 }
520
521 protected void setIsDocked(boolean val) {
522 isDocked = val;
523 Main.pref.put(preferencePrefix+".docked", val);
524 stateChanged();
525 }
526
527 protected void setIsCollapsed(boolean val) {
528 isCollapsed = val;
529 Main.pref.put(preferencePrefix+".minimized", val);
530 stateChanged();
531 }
532
533 public int getPreferredHeight() {
534 return preferredHeight;
535 }
536
537 public String helpTopic() {
538 String help = getClass().getName();
539 help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
540 return "Dialog/"+help;
541 }
542
543 @Override
544 public String toString() {
545 return name;
546 }
547
548 /**
549 * Replies true if this dialog is showing either as docked or as detached dialog
550 */
551 public boolean isDialogShowing() {
552 return isShowing;
553 }
554
555 /**
556 * Replies true if this dialog is docked and expanded
557 */
558 public boolean isDialogInDefaultView() {
559 return isShowing && isDocked && (! isCollapsed);
560 }
561
562 /**
563 * Replies true if this dialog is docked and collapsed
564 */
565 public boolean isDialogInCollapsedView() {
566 return isShowing && isDocked && isCollapsed;
567 }
568
569 /***
570 * The following methods are intended to be overridden, in order to customize
571 * the toggle dialog behavior.
572 **/
573
574 /**
575 * Change the Geometry of the detached dialog to better fit the content.
576 */
577 protected Rectangle getDetachedGeometry(Rectangle last) {
578 return last;
579 }
580
581 /**
582 * Default size of the detached dialog.
583 * Override this method to customize the initial dialog size.
584 */
585 protected Dimension getDefaultDetachedSize() {
586 return new Dimension(dialogsPanel.getWidth(), preferredHeight);
587 }
588
589 /**
590 * Do something when the toggleButton is pressed.
591 */
592 protected void toggleButtonHook() {
593 }
594
595 protected boolean dockWhenClosingDetachedDlg() {
596 return true;
597 }
598
599 /**
600 * primitive stateChangedListener for subclasses
601 */
602 protected void stateChanged() {
603 }
604
605 protected JPanel getButtonPanel(int columns) {
606 JPanel pnl = new JPanel();
607 pnl.setLayout(Main.pref.getBoolean("dialog.align.left", false)
608 ? new FlowLayout(FlowLayout.LEFT) : new GridLayout(1,columns));
609 return pnl;
610 }
611}
Note: See TracBrowser for help on using the repository browser.