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

Last change on this file since 2657 was 2613, checked in by Gubaer, 14 years ago

new: global in-memory cache for downloaded changesets
new: toggle dialog for changesets
new: downloading of changesets (currently without changeset content, will follow later)

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