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

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

see #3550 - patch by bastik - resize dialogs on right side

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