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

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

fixed #4502 - Photomapping pannel open just one time;
make tiger highlight color a little transparent (see #2381)

  • Property svn:eol-style set to native
File size: 18.9 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 * Hides the dialog
190 */
191 public void hideDialog() {
192 closeDetachedDialog();
193 this.setVisible(false);
194 setIsShowing(false);
195 toggleAction.putValue("selected", false);
196 hideNotify();
197 }
198
199 /**
200 * Displays the toggle dialog in the toggle dialog view on the right
201 * of the main map window.
202 *
203 */
204 protected void dock() {
205 detachedDialog = null;
206 titleBar.setVisible(true);
207 setIsDocked(true);
208 }
209
210 /**
211 * Display the dialog in a detached window.
212 *
213 */
214 protected void detach() {
215 setContentVisible(true);
216 this.setVisible(true);
217 titleBar.setVisible(false);
218 detachedDialog = new DetachedDialog();
219 detachedDialog.setVisible(true);
220 setIsShowing(true);
221 setIsDocked(false);
222 }
223
224 /**
225 * Collapses the toggle dialog to the title bar only
226 *
227 */
228 public void collapse() {
229 // if (isShowing && isDocked && !isCollapsed) {
230 if (isDialogInDefaultView()) {
231 setContentVisible(false);
232 setIsCollapsed(true);
233 setPreferredSize(new Dimension(0,20));
234 setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
235 setMinimumSize(new Dimension(Integer.MAX_VALUE,20));
236 lblMinimized.setIcon(ImageProvider.get("misc", "minimized"));
237 hideNotify();
238 }
239 else throw new IllegalStateException();
240 }
241
242 /**
243 * Expands the toggle dialog
244 */
245 protected void expand() {
246 // if (isShowing && isDocked && isCollapsed) {
247 if (isDialogInCollapsedView()) {
248 setContentVisible(true);
249 setIsCollapsed(false);
250 setPreferredSize(new Dimension(0,preferredHeight));
251 setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
252 lblMinimized.setIcon(ImageProvider.get("misc", "normal"));
253 showNotify();
254 }
255 else throw new IllegalStateException();
256 }
257
258 /**
259 * Sets the visibility of all components in this toggle dialog, except the title bar
260 *
261 * @param visible true, if the components should be visible; false otherwise
262 */
263 protected void setContentVisible(boolean visible) {
264 Component comps[] = getComponents();
265 for(int i=0; i<comps.length; i++) {
266 if(comps[i] != titleBar) {
267 comps[i].setVisible(visible);
268 }
269 }
270 }
271
272 public void destroy() {
273 closeDetachedDialog();
274 hideNotify();
275 }
276
277 /**
278 * Closes the detached dialog if this toggle dialog is currently displayed
279 * in a detached dialog.
280 *
281 */
282 public void closeDetachedDialog() {
283 if (detachedDialog != null) {
284 detachedDialog.setVisible(false);
285 detachedDialog.getContentPane().removeAll();
286 detachedDialog.dispose();
287 }
288 }
289
290 /**
291 * Called when toggle dialog is shown (after it was created or expanded). Descendants may overwrite this
292 * method, it's a good place to register listeners needed to keep dialog updated
293 */
294 public void showNotify() {
295
296 }
297
298 /**
299 * Called when toggle dialog is hidden (collapsed, removed, MapFrame is removed, ...). Good place to unregister
300 * listeners
301 */
302 public void hideNotify() {
303
304 }
305
306 /**
307 * The title bar displayed in docked mode
308 *
309 */
310 protected class TitleBar extends JPanel {
311 final private JLabel lblTitle;
312 final private JComponent lblTitle_weak;
313
314 public TitleBar(String toggleDialogName, String iconName) {
315 setLayout(new GridBagLayout());
316 lblMinimized = new JLabel(ImageProvider.get("misc", "normal"));
317 add(lblMinimized);
318
319 // scale down the dialog icon
320 ImageIcon inIcon = ImageProvider.get("dialogs", iconName);
321 ImageIcon smallIcon = new ImageIcon(inIcon.getImage().getScaledInstance(16 , 16, Image.SCALE_SMOOTH));
322 lblTitle = new JLabel("",smallIcon, JLabel.TRAILING);
323 lblTitle.setIconTextGap(8);
324
325 JPanel conceal = new JPanel();
326 conceal.add(lblTitle);
327 conceal.setVisible(false);
328 add(conceal, GBC.std());
329
330 // Cannot add the label directly since it would displace other elements on resize
331 lblTitle_weak = new JComponent() {
332 @Override
333 public void paintComponent(Graphics g) {
334 lblTitle.paint(g);
335 }
336 };
337 lblTitle_weak.setPreferredSize(new Dimension(Integer.MAX_VALUE,20));
338 lblTitle_weak.setMinimumSize(new Dimension(0,20));
339 add(lblTitle_weak, GBC.std().fill(GBC.HORIZONTAL));
340
341 addMouseListener(
342 new MouseAdapter() {
343 @Override
344 public void mouseClicked(MouseEvent e) {
345 // toggleExpandedState();
346 if (isCollapsed) {
347 expand();
348 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, ToggleDialog.this);
349 } else {
350 collapse();
351 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
352 }
353 }
354 }
355 );
356
357 // show the sticky button
358 JButton sticky = new JButton(ImageProvider.get("misc", "sticky"));
359 sticky.setToolTipText(tr("Undock the panel"));
360 sticky.setBorder(BorderFactory.createEmptyBorder());
361 sticky.addActionListener(
362 new ActionListener(){
363 public void actionPerformed(ActionEvent e) {
364 detach();
365 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
366 }
367 }
368 );
369 add(sticky);
370
371 // show the close button
372 JButton close = new JButton(ImageProvider.get("misc", "close"));
373 close.setToolTipText(tr("Close this panel. You can reopen it with the buttons in the left toolbar."));
374 close.setBorder(BorderFactory.createEmptyBorder());
375 close.addActionListener(
376 new ActionListener(){
377 public void actionPerformed(ActionEvent e) {
378 hideDialog();
379 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
380 }
381 }
382 );
383 add(close);
384 setToolTipText(tr("Click to minimize/maximize the panel content"));
385 setTitle(toggleDialogName);
386 }
387
388 public void setTitle(String title) {
389 lblTitle.setText(title);
390 lblTitle_weak.repaint();
391 }
392
393 public String getTitle() {
394 return lblTitle.getText();
395 }
396 }
397
398 /**
399 * The dialog class used to display toggle dialogs in a detached window.
400 *
401 */
402 private class DetachedDialog extends JDialog{
403 public DetachedDialog() {
404 super(JOptionPane.getFrameForComponent(Main.parent));
405 getContentPane().add(ToggleDialog.this);
406 addWindowListener(new WindowAdapter(){
407 @Override public void windowClosing(WindowEvent e) {
408 rememberGeometry();
409 getContentPane().removeAll();
410 dispose();
411 if (dockWhenClosingDetachedDlg()) {
412 dock();
413 if (isDialogInCollapsedView()) {
414 expand();
415 }
416 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
417 } else {
418 hideDialog();
419 }
420 }
421 });
422 addComponentListener(new ComponentAdapter() {
423 @Override public void componentMoved(ComponentEvent e) {
424 rememberGeometry();
425 }
426 @Override public void componentResized(ComponentEvent e) {
427 rememberGeometry();
428 }
429 });
430
431 String bounds = Main.pref.get(preferencePrefix+".bounds",null);
432 if (bounds != null) {
433 String[] b = bounds.split(",");
434 setBounds(getDetachedGeometry(new Rectangle(
435 Integer.parseInt(b[0]),Integer.parseInt(b[1]),Integer.parseInt(b[2]),Integer.parseInt(b[3]))));
436 } else {
437 ToggleDialog.this.setPreferredSize(ToggleDialog.this.getDefaultDetachedSize());
438 pack();
439 setLocationRelativeTo(Main.parent);
440 }
441 setTitle(titleBar.getTitle());
442 HelpUtil.setHelpContext(getRootPane(), helpTopic());
443 }
444
445 protected void rememberGeometry() {
446 Main.pref.put(preferencePrefix+".bounds", detachedDialog.getX()+","+detachedDialog.getY()+","+detachedDialog.getWidth()+","+detachedDialog.getHeight());
447 }
448 }
449
450 /**
451 * Replies the action to toggle the visible state of this toggle dialog
452 *
453 * @return the action to toggle the visible state of this toggle dialog
454 */
455 public AbstractAction getToggleAction() {
456 return toggleAction;
457 }
458
459 /**
460 * Replies the prefix for the preference settings of this dialog.
461 *
462 * @return the prefix for the preference settings of this dialog.
463 */
464 public String getPreferencePrefix() {
465 return preferencePrefix;
466 }
467
468 /**
469 * Sets the dialogsPanel managing all toggle dialogs
470 */
471 public void setDialogsPanel(DialogsPanel dialogsPanel) {
472 this.dialogsPanel = dialogsPanel;
473 }
474
475 /**
476 * Replies the name of this toggle dialog
477 */
478 @Override
479 public String getName() {
480 return "toggleDialog." + preferencePrefix;
481 }
482
483 /**
484 * Sets the title
485 */
486 public void setTitle(String title) {
487 titleBar.setTitle(title);
488 if (detachedDialog != null) {
489 detachedDialog.setTitle(title);
490 }
491 }
492
493 protected void setIsShowing(boolean val) {
494 isShowing = val;
495 Main.pref.put(preferencePrefix+".visible", val);
496 stateChanged();
497 }
498
499 protected void setIsDocked(boolean val) {
500 isDocked = val;
501 Main.pref.put(preferencePrefix+".docked", val);
502 stateChanged();
503 }
504
505 protected void setIsCollapsed(boolean val) {
506 isCollapsed = val;
507 Main.pref.put(preferencePrefix+".minimized", val);
508 stateChanged();
509 }
510
511 public int getPreferredHeight() {
512 return preferredHeight;
513 }
514
515 public String helpTopic() {
516 String help = getClass().getName();
517 help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
518 return "Dialog/"+help;
519 }
520
521 @Override
522 public String toString() {
523 return name;
524 }
525
526 /**
527 * Replies true if this dialog is showing either as docked or as detached dialog
528 */
529 public boolean isDialogShowing() {
530 return isShowing;
531 }
532
533 /**
534 * Replies true if this dialog is docked and expanded
535 */
536 public boolean isDialogInDefaultView() {
537 return isShowing && isDocked && (! isCollapsed);
538 }
539
540 /**
541 * Replies true if this dialog is docked and collapsed
542 */
543 public boolean isDialogInCollapsedView() {
544 return isShowing && isDocked && isCollapsed;
545 }
546
547 /***
548 * The following methods are intended to be overridden, in order to customize
549 * the toggle dialog behavior.
550 **/
551
552 /**
553 * Change the Geometry of the detached dialog to better fit the content.
554 */
555 protected Rectangle getDetachedGeometry(Rectangle last) {
556 return last;
557 }
558
559 /**
560 * Default size of the detached dialog.
561 * Override this method to customize the initial dialog size.
562 */
563 protected Dimension getDefaultDetachedSize() {
564 return new Dimension(dialogsPanel.getWidth(), preferredHeight);
565 }
566
567 /**
568 * Do something when the toggleButton is pressed.
569 */
570 protected void toggleButtonHook() {
571 }
572
573 protected boolean dockWhenClosingDetachedDlg() {
574 return true;
575 }
576
577 /**
578 * primitive stateChangedListener for subclasses
579 */
580 protected void stateChanged() {
581 }
582
583 protected JPanel getButtonPanel(int columns) {
584 JPanel pnl = new JPanel();
585 pnl.setLayout(Main.pref.getBoolean("dialog.align.left", false)
586 ? new FlowLayout(FlowLayout.LEFT) : new GridLayout(1,columns));
587 return pnl;
588 }
589}
Note: See TracBrowser for help on using the repository browser.