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

Last change on this file since 2185 was 2185, checked in by stoecker, 15 years ago

see #3550 - fixed resizable side views - patch by bastiK

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