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

Last change on this file since 2602 was 2602, checked in by bastiK, 15 years ago

some minor things:

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