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

Last change on this file since 9438 was 9078, checked in by Don-vip, 8 years ago

sonar - Immutable Field

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