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

Last change on this file since 7005 was 7005, checked in by Don-vip, 10 years ago

see #8465 - use diamond operator where applicable

  • Property svn:eol-style set to native
File size: 33.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.AWTEvent;
7import java.awt.BorderLayout;
8import java.awt.Component;
9import java.awt.Container;
10import java.awt.Dimension;
11import java.awt.FlowLayout;
12import java.awt.Graphics;
13import java.awt.GridBagLayout;
14import java.awt.GridLayout;
15import java.awt.Rectangle;
16import java.awt.Toolkit;
17import java.awt.event.AWTEventListener;
18import java.awt.event.ActionEvent;
19import java.awt.event.ActionListener;
20import java.awt.event.ComponentAdapter;
21import java.awt.event.ComponentEvent;
22import java.awt.event.MouseEvent;
23import java.awt.event.WindowAdapter;
24import java.awt.event.WindowEvent;
25import java.beans.PropertyChangeEvent;
26import java.util.ArrayList;
27import java.util.Arrays;
28import java.util.Collection;
29import java.util.LinkedList;
30import java.util.List;
31
32import javax.swing.AbstractAction;
33import javax.swing.BorderFactory;
34import javax.swing.ButtonGroup;
35import javax.swing.JButton;
36import javax.swing.JCheckBoxMenuItem;
37import javax.swing.JComponent;
38import javax.swing.JDialog;
39import javax.swing.JLabel;
40import javax.swing.JMenu;
41import javax.swing.JOptionPane;
42import javax.swing.JPanel;
43import javax.swing.JPopupMenu;
44import javax.swing.JRadioButtonMenuItem;
45import javax.swing.JScrollPane;
46import javax.swing.JToggleButton;
47import javax.swing.SwingUtilities;
48
49import org.openstreetmap.josm.Main;
50import org.openstreetmap.josm.actions.JosmAction;
51import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
52import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
53import org.openstreetmap.josm.data.preferences.BooleanProperty;
54import org.openstreetmap.josm.data.preferences.ParametrizedEnumProperty;
55import org.openstreetmap.josm.gui.MainMenu;
56import org.openstreetmap.josm.gui.ShowHideButtonListener;
57import org.openstreetmap.josm.gui.SideButton;
58import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
59import org.openstreetmap.josm.gui.help.HelpUtil;
60import org.openstreetmap.josm.gui.help.Helpful;
61import org.openstreetmap.josm.gui.preferences.PreferenceDialog;
62import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
63import org.openstreetmap.josm.gui.preferences.SubPreferenceSetting;
64import org.openstreetmap.josm.gui.preferences.TabPreferenceSetting;
65import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
66import org.openstreetmap.josm.tools.Destroyable;
67import org.openstreetmap.josm.tools.GBC;
68import org.openstreetmap.josm.tools.ImageProvider;
69import org.openstreetmap.josm.tools.Shortcut;
70import org.openstreetmap.josm.tools.WindowGeometry;
71import org.openstreetmap.josm.tools.WindowGeometry.WindowGeometryException;
72
73/**
74 * This class is a toggle dialog that can be turned on and off.
75 *
76 */
77public class ToggleDialog extends JPanel implements ShowHideButtonListener, Helpful, AWTEventListener, Destroyable, PreferenceChangedListener {
78
79 /**
80 * The button-hiding strategy in toggler dialogs.
81 */
82 public enum ButtonHidingType {
83 /** Buttons are always shown (default) **/
84 ALWAYS_SHOWN,
85 /** Buttons are always hidden **/
86 ALWAYS_HIDDEN,
87 /** Buttons are dynamically hidden, i.e. only shown when mouse cursor is in dialog */
88 DYNAMIC
89 }
90
91 /**
92 * Property to enable dyanmic buttons globally.
93 * @since 6752
94 */
95 public static final BooleanProperty PROP_DYNAMIC_BUTTONS = new BooleanProperty("dialog.dynamic.buttons", false);
96
97 private final ParametrizedEnumProperty<ButtonHidingType> PROP_BUTTON_HIDING = new ParametrizedEnumProperty<ToggleDialog.ButtonHidingType>(
98 ButtonHidingType.class, ButtonHidingType.DYNAMIC) {
99 @Override
100 protected String getKey(String... params) {
101 return preferencePrefix + ".buttonhiding";
102 }
103 @Override
104 protected ButtonHidingType parse(String s) {
105 try {
106 return super.parse(s);
107 } catch (IllegalArgumentException e) {
108 // Legacy settings
109 return Boolean.parseBoolean(s)?ButtonHidingType.DYNAMIC:ButtonHidingType.ALWAYS_SHOWN;
110 }
111 }
112 };
113
114 /** The action to toggle this dialog */
115 protected final ToggleDialogAction toggleAction;
116 protected String preferencePrefix;
117 protected final String name;
118
119 /** DialogsPanel that manages all ToggleDialogs */
120 protected DialogsPanel dialogsPanel;
121
122 protected TitleBar titleBar;
123
124 /**
125 * Indicates whether the dialog is showing or not.
126 */
127 protected boolean isShowing;
128
129 /**
130 * If isShowing is true, indicates whether the dialog is docked or not, e. g.
131 * shown as part of the main window or as a separate dialog window.
132 */
133 protected boolean isDocked;
134
135 /**
136 * If isShowing and isDocked are true, indicates whether the dialog is
137 * currently minimized or not.
138 */
139 protected boolean isCollapsed;
140
141 /**
142 * Indicates whether dynamic button hiding is active or not.
143 */
144 protected ButtonHidingType buttonHiding;
145
146 /** the preferred height if the toggle dialog is expanded */
147 private int preferredHeight;
148
149 /** the JDialog displaying the toggle dialog as undocked dialog */
150 protected JDialog detachedDialog;
151
152 protected JToggleButton button;
153 private JPanel buttonsPanel;
154 private List<javax.swing.Action> buttonActions = new ArrayList<>();
155
156 /** holds the menu entry in the windows menu. Required to properly
157 * toggle the checkbox on show/hide
158 */
159 protected JCheckBoxMenuItem windowMenuItem;
160
161 private final JRadioButtonMenuItem alwaysShown = new JRadioButtonMenuItem(new AbstractAction(tr("Always shown")) {
162 @Override
163 public void actionPerformed(ActionEvent e) {
164 setIsButtonHiding(ButtonHidingType.ALWAYS_SHOWN);
165 }
166 });
167
168 private final JRadioButtonMenuItem dynamic = new JRadioButtonMenuItem(new AbstractAction(tr("Dynamic")) {
169 @Override
170 public void actionPerformed(ActionEvent e) {
171 setIsButtonHiding(ButtonHidingType.DYNAMIC);
172 }
173 });
174
175 private final JRadioButtonMenuItem alwaysHidden = new JRadioButtonMenuItem(new AbstractAction(tr("Always hidden")) {
176 @Override
177 public void actionPerformed(ActionEvent e) {
178 setIsButtonHiding(ButtonHidingType.ALWAYS_HIDDEN);
179 }
180 });
181
182 /**
183 * The linked preferences class (optional). If set, accessible from the title bar with a dedicated button
184 */
185 protected Class<? extends PreferenceSetting> preferenceClass;
186
187 /**
188 * Constructor
189 *
190 * @param name the name of the dialog
191 * @param iconName the name of the icon to be displayed
192 * @param tooltip the tool tip
193 * @param shortcut the shortcut
194 * @param preferredHeight the preferred height for the dialog
195 */
196 public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
197 this(name, iconName, tooltip, shortcut, preferredHeight, false);
198 }
199
200 /**
201 * Constructor
202
203 * @param name the name of the dialog
204 * @param iconName the name of the icon to be displayed
205 * @param tooltip the tool tip
206 * @param shortcut the shortcut
207 * @param preferredHeight the preferred height for the dialog
208 * @param defShow if the dialog should be shown by default, if there is no preference
209 */
210 public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight, boolean defShow) {
211 this(name, iconName, tooltip, shortcut, preferredHeight, defShow, null);
212 }
213
214 /**
215 * Constructor
216 *
217 * @param name the name of the dialog
218 * @param iconName the name of the icon to be displayed
219 * @param tooltip the tool tip
220 * @param shortcut the shortcut
221 * @param preferredHeight the preferred height for the dialog
222 * @param defShow if the dialog should be shown by default, if there is no preference
223 * @param prefClass the preferences settings class, or null if not applicable
224 */
225 public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight, boolean defShow, Class<? extends PreferenceSetting> prefClass) {
226 super(new BorderLayout());
227 this.preferencePrefix = iconName;
228 this.name = name;
229 this.preferenceClass = prefClass;
230
231 /** Use the full width of the parent element */
232 setPreferredSize(new Dimension(0, preferredHeight));
233 /** Override any minimum sizes of child elements so the user can resize freely */
234 setMinimumSize(new Dimension(0,0));
235 this.preferredHeight = preferredHeight;
236 toggleAction = new ToggleDialogAction(name, "dialogs/"+iconName, tooltip, shortcut);
237 String helpId = "Dialog/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
238 toggleAction.putValue("help", helpId.substring(0, helpId.length()-6));
239
240 isShowing = Main.pref.getBoolean(preferencePrefix+".visible", defShow);
241 isDocked = Main.pref.getBoolean(preferencePrefix+".docked", true);
242 isCollapsed = Main.pref.getBoolean(preferencePrefix+".minimized", false);
243 buttonHiding = PROP_BUTTON_HIDING.get();
244
245 /** show the minimize button */
246 titleBar = new TitleBar(name, iconName);
247 add(titleBar, BorderLayout.NORTH);
248
249 setBorder(BorderFactory.createEtchedBorder());
250
251 Main.redirectToMainContentPane(this);
252 Main.pref.addPreferenceChangeListener(this);
253
254 windowMenuItem = MainMenu.addWithCheckbox(Main.main.menu.windowMenu,
255 (JosmAction) getToggleAction(),
256 MainMenu.WINDOW_MENU_GROUP.TOGGLE_DIALOG);
257 }
258
259 /**
260 * The action to toggle the visibility state of this toggle dialog.
261 *
262 * Emits {@link PropertyChangeEvent}s for the property <tt>selected</tt>:
263 * <ul>
264 * <li>true, if the dialog is currently visible</li>
265 * <li>false, if the dialog is currently invisible</li>
266 * </ul>
267 *
268 */
269 public final class ToggleDialogAction extends JosmAction {
270
271 private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut) {
272 super(name, iconName, tooltip, shortcut, false);
273 }
274
275 @Override
276 public void actionPerformed(ActionEvent e) {
277 toggleButtonHook();
278 if (getValue("toolbarbutton") instanceof JButton) {
279 ((JButton) getValue("toolbarbutton")).setSelected(!isShowing);
280 }
281 if (isShowing) {
282 hideDialog();
283 if (dialogsPanel != null) {
284 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
285 }
286 hideNotify();
287 } else {
288 showDialog();
289 if (isDocked && isCollapsed) {
290 expand();
291 }
292 if (isDocked && dialogsPanel != null) {
293 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
294 }
295 showNotify();
296 }
297 }
298 }
299
300 /**
301 * Shows the dialog
302 */
303 public void showDialog() {
304 setIsShowing(true);
305 if (!isDocked) {
306 detach();
307 } else {
308 dock();
309 this.setVisible(true);
310 }
311 // toggling the selected value in order to enforce PropertyChangeEvents
312 setIsShowing(true);
313 windowMenuItem.setState(true);
314 toggleAction.putValue("selected", false);
315 toggleAction.putValue("selected", true);
316 }
317
318 /**
319 * Changes the state of the dialog such that the user can see the content.
320 * (takes care of the panel reconstruction)
321 */
322 public void unfurlDialog() {
323 if (isDialogInDefaultView())
324 return;
325 if (isDialogInCollapsedView()) {
326 expand();
327 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, this);
328 } else if (!isDialogShowing()) {
329 showDialog();
330 if (isDocked && isCollapsed) {
331 expand();
332 }
333 if (isDocked) {
334 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, this);
335 }
336 showNotify();
337 }
338 }
339
340 @Override
341 public void buttonHidden() {
342 if ((Boolean) toggleAction.getValue("selected")) {
343 toggleAction.actionPerformed(null);
344 }
345 }
346
347 @Override
348 public void buttonShown() {
349 unfurlDialog();
350 }
351
352
353 /**
354 * Hides the dialog
355 */
356 public void hideDialog() {
357 closeDetachedDialog();
358 this.setVisible(false);
359 windowMenuItem.setState(false);
360 setIsShowing(false);
361 toggleAction.putValue("selected", false);
362 }
363
364 /**
365 * Displays the toggle dialog in the toggle dialog view on the right
366 * of the main map window.
367 *
368 */
369 protected void dock() {
370 detachedDialog = null;
371 titleBar.setVisible(true);
372 setIsDocked(true);
373 }
374
375 /**
376 * Display the dialog in a detached window.
377 *
378 */
379 protected void detach() {
380 setContentVisible(true);
381 this.setVisible(true);
382 titleBar.setVisible(false);
383 detachedDialog = new DetachedDialog();
384 detachedDialog.setVisible(true);
385 setIsShowing(true);
386 setIsDocked(false);
387 }
388
389 /**
390 * Collapses the toggle dialog to the title bar only
391 *
392 */
393 public void collapse() {
394 if (isDialogInDefaultView()) {
395 setContentVisible(false);
396 setIsCollapsed(true);
397 setPreferredSize(new Dimension(0,20));
398 setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
399 setMinimumSize(new Dimension(Integer.MAX_VALUE,20));
400 titleBar.lblMinimized.setIcon(ImageProvider.get("misc", "minimized"));
401 }
402 else throw new IllegalStateException();
403 }
404
405 /**
406 * Expands the toggle dialog
407 */
408 protected void expand() {
409 if (isDialogInCollapsedView()) {
410 setContentVisible(true);
411 setIsCollapsed(false);
412 setPreferredSize(new Dimension(0,preferredHeight));
413 setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
414 titleBar.lblMinimized.setIcon(ImageProvider.get("misc", "normal"));
415 }
416 else throw new IllegalStateException();
417 }
418
419 /**
420 * Sets the visibility of all components in this toggle dialog, except the title bar
421 *
422 * @param visible true, if the components should be visible; false otherwise
423 */
424 protected void setContentVisible(boolean visible) {
425 Component[] comps = getComponents();
426 for (Component comp : comps) {
427 if (comp != titleBar && (!visible || comp != buttonsPanel || buttonHiding != ButtonHidingType.ALWAYS_HIDDEN)) {
428 comp.setVisible(visible);
429 }
430 }
431 }
432
433 @Override
434 public void destroy() {
435 closeDetachedDialog();
436 hideNotify();
437 Main.main.menu.windowMenu.remove(windowMenuItem);
438 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
439 Main.pref.removePreferenceChangeListener(this);
440 destroyComponents(this, false);
441 }
442
443 private void destroyComponents(Component component, boolean destroyItself) {
444 if (component instanceof Container) {
445 for (Component c: ((Container)component).getComponents()) {
446 destroyComponents(c, true);
447 }
448 }
449 if (destroyItself && component instanceof Destroyable) {
450 ((Destroyable) component).destroy();
451 }
452 }
453
454 /**
455 * Closes the detached dialog if this toggle dialog is currently displayed
456 * in a detached dialog.
457 *
458 */
459 public void closeDetachedDialog() {
460 if (detachedDialog != null) {
461 detachedDialog.setVisible(false);
462 detachedDialog.getContentPane().removeAll();
463 detachedDialog.dispose();
464 }
465 }
466
467 /**
468 * Called when toggle dialog is shown (after it was created or expanded). Descendants may overwrite this
469 * method, it's a good place to register listeners needed to keep dialog updated
470 */
471 public void showNotify() {
472
473 }
474
475 /**
476 * Called when toggle dialog is hidden (collapsed, removed, MapFrame is removed, ...). Good place to unregister
477 * listeners
478 */
479 public void hideNotify() {
480
481 }
482
483 /**
484 * The title bar displayed in docked mode
485 *
486 */
487 protected class TitleBar extends JPanel {
488 /** the label which shows whether the toggle dialog is expanded or collapsed */
489 private final JLabel lblMinimized;
490 /** the label which displays the dialog's title **/
491 private final JLabel lblTitle;
492 private final JComponent lblTitle_weak;
493 /** the button which shows whether buttons are dynamic or not */
494 private final JButton buttonsHide;
495 /** the contextual menu **/
496 private DialogPopupMenu popupMenu;
497
498 public TitleBar(String toggleDialogName, String iconName) {
499 setLayout(new GridBagLayout());
500
501 lblMinimized = new JLabel(ImageProvider.get("misc", "normal"));
502 add(lblMinimized);
503
504 // scale down the dialog icon
505 lblTitle = new JLabel("", new ImageProvider("dialogs", iconName).setWidth(16).get(), JLabel.TRAILING);
506 lblTitle.setIconTextGap(8);
507
508 JPanel conceal = new JPanel();
509 conceal.add(lblTitle);
510 conceal.setVisible(false);
511 add(conceal, GBC.std());
512
513 // Cannot add the label directly since it would displace other elements on resize
514 lblTitle_weak = new JComponent() {
515 @Override
516 public void paintComponent(Graphics g) {
517 lblTitle.paint(g);
518 }
519 };
520 lblTitle_weak.setPreferredSize(new Dimension(Integer.MAX_VALUE,20));
521 lblTitle_weak.setMinimumSize(new Dimension(0,20));
522 add(lblTitle_weak, GBC.std().fill(GBC.HORIZONTAL));
523
524 buttonsHide = new JButton(ImageProvider.get("misc", buttonHiding != ButtonHidingType.ALWAYS_SHOWN ? "buttonhide" : "buttonshow"));
525 buttonsHide.setToolTipText(tr("Toggle dynamic buttons"));
526 buttonsHide.setBorder(BorderFactory.createEmptyBorder());
527 buttonsHide.addActionListener(
528 new ActionListener() {
529 @Override
530 public void actionPerformed(ActionEvent e) {
531 JRadioButtonMenuItem item = (buttonHiding == ButtonHidingType.DYNAMIC) ? alwaysShown : dynamic;
532 item.setSelected(true);
533 item.getAction().actionPerformed(null);
534 }
535 }
536 );
537 add(buttonsHide);
538
539 // show the pref button if applicable
540 if (preferenceClass != null) {
541 JButton pref = new JButton(new ImageProvider("preference").setWidth(16).get());
542 pref.setToolTipText(tr("Open preferences for this panel"));
543 pref.setBorder(BorderFactory.createEmptyBorder());
544 pref.addActionListener(
545 new ActionListener(){
546 @Override
547 @SuppressWarnings("unchecked")
548 public void actionPerformed(ActionEvent e) {
549 final PreferenceDialog p = new PreferenceDialog(Main.parent);
550 if (TabPreferenceSetting.class.isAssignableFrom(preferenceClass)) {
551 p.selectPreferencesTabByClass((Class<? extends TabPreferenceSetting>) preferenceClass);
552 } else if (SubPreferenceSetting.class.isAssignableFrom(preferenceClass)) {
553 p.selectSubPreferencesTabByClass((Class<? extends SubPreferenceSetting>) preferenceClass);
554 }
555 p.setVisible(true);
556 }
557 }
558 );
559 add(pref);
560 }
561
562 // show the sticky button
563 JButton sticky = new JButton(ImageProvider.get("misc", "sticky"));
564 sticky.setToolTipText(tr("Undock the panel"));
565 sticky.setBorder(BorderFactory.createEmptyBorder());
566 sticky.addActionListener(
567 new ActionListener(){
568 @Override
569 public void actionPerformed(ActionEvent e) {
570 detach();
571 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
572 }
573 }
574 );
575 add(sticky);
576
577 // show the close button
578 JButton close = new JButton(ImageProvider.get("misc", "close"));
579 close.setToolTipText(tr("Close this panel. You can reopen it with the buttons in the left toolbar."));
580 close.setBorder(BorderFactory.createEmptyBorder());
581 close.addActionListener(
582 new ActionListener(){
583 @Override
584 public void actionPerformed(ActionEvent e) {
585 hideDialog();
586 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
587 hideNotify();
588 }
589 }
590 );
591 add(close);
592 setToolTipText(tr("Click to minimize/maximize the panel content"));
593 setTitle(toggleDialogName);
594 }
595
596 public void setTitle(String title) {
597 lblTitle.setText(title);
598 lblTitle_weak.repaint();
599 }
600
601 public String getTitle() {
602 return lblTitle.getText();
603 }
604
605 public class DialogPopupMenu extends JPopupMenu {
606 private final ButtonGroup buttonHidingGroup = new ButtonGroup();
607 private final JMenu buttonHidingMenu = new JMenu(tr("Side buttons"));
608
609 public DialogPopupMenu() {
610 alwaysShown.setSelected(buttonHiding == ButtonHidingType.ALWAYS_SHOWN);
611 dynamic.setSelected(buttonHiding == ButtonHidingType.DYNAMIC);
612 alwaysHidden.setSelected(buttonHiding == ButtonHidingType.ALWAYS_HIDDEN);
613 for (JRadioButtonMenuItem rb : new JRadioButtonMenuItem[]{alwaysShown, dynamic, alwaysHidden}) {
614 buttonHidingGroup.add(rb);
615 buttonHidingMenu.add(rb);
616 }
617 add(buttonHidingMenu);
618 for (javax.swing.Action action: buttonActions) {
619 add(action);
620 }
621 }
622 }
623
624 public final void registerMouseListener() {
625 popupMenu = new DialogPopupMenu();
626 addMouseListener(new MouseEventHandler());
627 }
628
629 class MouseEventHandler extends PopupMenuLauncher {
630 public MouseEventHandler() {
631 super(popupMenu);
632 }
633 @Override
634 public void mouseClicked(MouseEvent e) {
635 if (SwingUtilities.isLeftMouseButton(e)) {
636 if (isCollapsed) {
637 expand();
638 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, ToggleDialog.this);
639 } else {
640 collapse();
641 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
642 }
643 }
644 }
645 }
646 }
647
648 /**
649 * The dialog class used to display toggle dialogs in a detached window.
650 *
651 */
652 private class DetachedDialog extends JDialog {
653 public DetachedDialog() {
654 super(JOptionPane.getFrameForComponent(Main.parent));
655 getContentPane().add(ToggleDialog.this);
656 addWindowListener(new WindowAdapter(){
657 @Override public void windowClosing(WindowEvent e) {
658 rememberGeometry();
659 getContentPane().removeAll();
660 dispose();
661 if (dockWhenClosingDetachedDlg()) {
662 dock();
663 if (isDialogInCollapsedView()) {
664 expand();
665 }
666 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
667 } else {
668 hideDialog();
669 hideNotify();
670 }
671 }
672 });
673 addComponentListener(new ComponentAdapter() {
674 @Override public void componentMoved(ComponentEvent e) {
675 rememberGeometry();
676 }
677 @Override public void componentResized(ComponentEvent e) {
678 rememberGeometry();
679 }
680 });
681
682 try {
683 new WindowGeometry(preferencePrefix+".geometry").applySafe(this);
684 } catch (WindowGeometryException e) {
685 ToggleDialog.this.setPreferredSize(ToggleDialog.this.getDefaultDetachedSize());
686 pack();
687 setLocationRelativeTo(Main.parent);
688 }
689 setTitle(titleBar.getTitle());
690 HelpUtil.setHelpContext(getRootPane(), helpTopic());
691 }
692
693 protected void rememberGeometry() {
694 if (detachedDialog != null) {
695 new WindowGeometry(detachedDialog).remember(preferencePrefix+".geometry");
696 }
697 }
698 }
699
700 /**
701 * Replies the action to toggle the visible state of this toggle dialog
702 *
703 * @return the action to toggle the visible state of this toggle dialog
704 */
705 public AbstractAction getToggleAction() {
706 return toggleAction;
707 }
708
709 /**
710 * Replies the prefix for the preference settings of this dialog.
711 *
712 * @return the prefix for the preference settings of this dialog.
713 */
714 public String getPreferencePrefix() {
715 return preferencePrefix;
716 }
717
718 /**
719 * Sets the dialogsPanel managing all toggle dialogs.
720 * @param dialogsPanel The panel managing all toggle dialogs
721 */
722 public void setDialogsPanel(DialogsPanel dialogsPanel) {
723 this.dialogsPanel = dialogsPanel;
724 }
725
726 /**
727 * Replies the name of this toggle dialog
728 */
729 @Override
730 public String getName() {
731 return "toggleDialog." + preferencePrefix;
732 }
733
734 /**
735 * Sets the title.
736 * @param title The dialog's title
737 */
738 public void setTitle(String title) {
739 titleBar.setTitle(title);
740 if (detachedDialog != null) {
741 detachedDialog.setTitle(title);
742 }
743 }
744
745 protected void setIsShowing(boolean val) {
746 isShowing = val;
747 Main.pref.put(preferencePrefix+".visible", val);
748 stateChanged();
749 }
750
751 protected void setIsDocked(boolean val) {
752 if (buttonsPanel != null) {
753 buttonsPanel.setVisible(val ? buttonHiding != ButtonHidingType.ALWAYS_HIDDEN : true);
754 }
755 isDocked = val;
756 Main.pref.put(preferencePrefix+".docked", val);
757 stateChanged();
758 }
759
760 protected void setIsCollapsed(boolean val) {
761 isCollapsed = val;
762 Main.pref.put(preferencePrefix+".minimized", val);
763 stateChanged();
764 }
765
766 protected void setIsButtonHiding(ButtonHidingType val) {
767 buttonHiding = val;
768 PROP_BUTTON_HIDING.put(val);
769 refreshHidingButtons();
770 }
771
772 /**
773 * Returns the preferred height of this dialog.
774 * @return The preferred height if the toggle dialog is expanded
775 */
776 public int getPreferredHeight() {
777 return preferredHeight;
778 }
779
780 @Override
781 public String helpTopic() {
782 String help = getClass().getName();
783 help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
784 return "Dialog/"+help;
785 }
786
787 @Override
788 public String toString() {
789 return name;
790 }
791
792 /**
793 * Determines if this dialog is showing either as docked or as detached dialog.
794 * @return {@code true} if this dialog is showing either as docked or as detached dialog
795 */
796 public boolean isDialogShowing() {
797 return isShowing;
798 }
799
800 /**
801 * Determines if this dialog is docked and expanded.
802 * @return {@code true} if this dialog is docked and expanded
803 */
804 public boolean isDialogInDefaultView() {
805 return isShowing && isDocked && (! isCollapsed);
806 }
807
808 /**
809 * Determines if this dialog is docked and collapsed.
810 * @return {@code true} if this dialog is docked and collapsed
811 */
812 public boolean isDialogInCollapsedView() {
813 return isShowing && isDocked && isCollapsed;
814 }
815
816 public void setButton(JToggleButton button) {
817 this.button = button;
818 }
819
820 public JToggleButton getButton() {
821 return button;
822 }
823
824 /***
825 * The following methods are intended to be overridden, in order to customize
826 * the toggle dialog behavior.
827 **/
828
829 /**
830 * Change the Geometry of the detached dialog to better fit the content.
831 */
832 protected Rectangle getDetachedGeometry(Rectangle last) {
833 return last;
834 }
835
836 /**
837 * Default size of the detached dialog.
838 * Override this method to customize the initial dialog size.
839 */
840 protected Dimension getDefaultDetachedSize() {
841 return new Dimension(dialogsPanel.getWidth(), preferredHeight);
842 }
843
844 /**
845 * Do something when the toggleButton is pressed.
846 */
847 protected void toggleButtonHook() {
848 }
849
850 protected boolean dockWhenClosingDetachedDlg() {
851 return true;
852 }
853
854 /**
855 * primitive stateChangedListener for subclasses
856 */
857 protected void stateChanged() {
858 }
859
860 protected Component createLayout(Component data, boolean scroll, Collection<SideButton> buttons) {
861 return createLayout(data, scroll, buttons, (Collection<SideButton>[]) null);
862 }
863
864 protected Component createLayout(Component data, boolean scroll, Collection<SideButton> firstButtons, Collection<SideButton>... nextButtons) {
865 if (scroll) {
866 data = new JScrollPane(data);
867 }
868 LinkedList<Collection<SideButton>> buttons = new LinkedList<>();
869 buttons.addFirst(firstButtons);
870 if (nextButtons != null) {
871 buttons.addAll(Arrays.asList(nextButtons));
872 }
873 add(data, BorderLayout.CENTER);
874 if (!buttons.isEmpty() && buttons.get(0) != null && !buttons.get(0).isEmpty()) {
875 buttonsPanel = new JPanel(new GridLayout(buttons.size(), 1));
876 for (Collection<SideButton> buttonRow : buttons) {
877 if (buttonRow == null) {
878 continue;
879 }
880 final JPanel buttonRowPanel = new JPanel(Main.pref.getBoolean("dialog.align.left", false)
881 ? new FlowLayout(FlowLayout.LEFT) : new GridLayout(1, buttonRow.size()));
882 buttonsPanel.add(buttonRowPanel);
883 for (SideButton button : buttonRow) {
884 buttonRowPanel.add(button);
885 javax.swing.Action action = button.getAction();
886 if (action != null) {
887 buttonActions.add(action);
888 } else {
889 Main.warn("Button " + button + " doesn't have action defined");
890 Main.error(new Exception());
891 }
892 }
893 }
894 add(buttonsPanel, BorderLayout.SOUTH);
895 dynamicButtonsPropertyChanged();
896 } else {
897 titleBar.buttonsHide.setVisible(false);
898 }
899
900 // Register title bar mouse listener only after buttonActions has been initialized to have a complete popup menu
901 titleBar.registerMouseListener();
902
903 return data;
904 }
905
906 @Override
907 public void eventDispatched(AWTEvent event) {
908 if(isShowing() && !isCollapsed && isDocked && buttonHiding == ButtonHidingType.DYNAMIC) {
909 if (buttonsPanel != null) {
910 Rectangle b = this.getBounds();
911 b.setLocation(getLocationOnScreen());
912 if (b.contains(((MouseEvent)event).getLocationOnScreen())) {
913 if(!buttonsPanel.isVisible()) {
914 buttonsPanel.setVisible(true);
915 }
916 } else if (buttonsPanel.isVisible()) {
917 buttonsPanel.setVisible(false);
918 }
919 }
920 }
921 }
922
923 @Override
924 public void preferenceChanged(PreferenceChangeEvent e) {
925 if (e.getKey().equals(PROP_DYNAMIC_BUTTONS.getKey())) {
926 dynamicButtonsPropertyChanged();
927 }
928 }
929
930 private void dynamicButtonsPropertyChanged() {
931 boolean propEnabled = PROP_DYNAMIC_BUTTONS.get();
932 if (propEnabled) {
933 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_MOTION_EVENT_MASK);
934 } else {
935 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
936 }
937 titleBar.buttonsHide.setVisible(propEnabled);
938 refreshHidingButtons();
939 }
940
941 private void refreshHidingButtons() {
942 titleBar.buttonsHide.setIcon(ImageProvider.get("misc", buttonHiding != ButtonHidingType.ALWAYS_SHOWN ? "buttonhide" : "buttonshow"));
943 titleBar.buttonsHide.setEnabled(buttonHiding != ButtonHidingType.ALWAYS_HIDDEN);
944 if (buttonsPanel != null) {
945 buttonsPanel.setVisible(buttonHiding != ButtonHidingType.ALWAYS_HIDDEN || !isDocked);
946 }
947 stateChanged();
948 }
949}
Note: See TracBrowser for help on using the repository browser.