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

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

fix many checkstyle violations

  • 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 @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 transient 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,
226 Class<? extends PreferenceSetting> prefClass) {
227 super(new BorderLayout());
228 this.preferencePrefix = iconName;
229 this.name = name;
230 this.preferenceClass = prefClass;
231
232 /** Use the full width of the parent element */
233 setPreferredSize(new Dimension(0, preferredHeight));
234 /** Override any minimum sizes of child elements so the user can resize freely */
235 setMinimumSize(new Dimension(0,0));
236 this.preferredHeight = preferredHeight;
237 toggleAction = new ToggleDialogAction(name, "dialogs/"+iconName, tooltip, shortcut);
238 String helpId = "Dialog/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
239 toggleAction.putValue("help", helpId.substring(0, helpId.length()-6));
240
241 isShowing = Main.pref.getBoolean(preferencePrefix+".visible", defShow);
242 isDocked = Main.pref.getBoolean(preferencePrefix+".docked", true);
243 isCollapsed = Main.pref.getBoolean(preferencePrefix+".minimized", false);
244 buttonHiding = propButtonHiding.get();
245
246 /** show the minimize button */
247 titleBar = new TitleBar(name, iconName);
248 add(titleBar, BorderLayout.NORTH);
249
250 setBorder(BorderFactory.createEtchedBorder());
251
252 Main.redirectToMainContentPane(this);
253 Main.pref.addPreferenceChangeListener(this);
254
255 windowMenuItem = MainMenu.addWithCheckbox(Main.main.menu.windowMenu,
256 (JosmAction) getToggleAction(),
257 MainMenu.WINDOW_MENU_GROUP.TOGGLE_DIALOG);
258 }
259
260 /**
261 * The action to toggle the visibility state of this toggle dialog.
262 *
263 * Emits {@link PropertyChangeEvent}s for the property <tt>selected</tt>:
264 * <ul>
265 * <li>true, if the dialog is currently visible</li>
266 * <li>false, if the dialog is currently invisible</li>
267 * </ul>
268 *
269 */
270 public final class ToggleDialogAction extends JosmAction {
271
272 private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut) {
273 super(name, iconName, tooltip, shortcut, false);
274 }
275
276 @Override
277 public void actionPerformed(ActionEvent e) {
278 toggleButtonHook();
279 if (getValue("toolbarbutton") instanceof JButton) {
280 ((JButton) getValue("toolbarbutton")).setSelected(!isShowing);
281 }
282 if (isShowing) {
283 hideDialog();
284 if (dialogsPanel != null) {
285 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
286 }
287 hideNotify();
288 } else {
289 showDialog();
290 if (isDocked && isCollapsed) {
291 expand();
292 }
293 if (isDocked && dialogsPanel != null) {
294 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
295 }
296 showNotify();
297 }
298 }
299 }
300
301 /**
302 * Shows the dialog
303 */
304 public void showDialog() {
305 setIsShowing(true);
306 if (!isDocked) {
307 detach();
308 } else {
309 dock();
310 this.setVisible(true);
311 }
312 // toggling the selected value in order to enforce PropertyChangeEvents
313 setIsShowing(true);
314 windowMenuItem.setState(true);
315 toggleAction.putValue("selected", Boolean.FALSE);
316 toggleAction.putValue("selected", Boolean.TRUE);
317 }
318
319 /**
320 * Changes the state of the dialog such that the user can see the content.
321 * (takes care of the panel reconstruction)
322 */
323 public void unfurlDialog() {
324 if (isDialogInDefaultView())
325 return;
326 if (isDialogInCollapsedView()) {
327 expand();
328 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, this);
329 } else if (!isDialogShowing()) {
330 showDialog();
331 if (isDocked && isCollapsed) {
332 expand();
333 }
334 if (isDocked) {
335 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, this);
336 }
337 showNotify();
338 }
339 }
340
341 @Override
342 public void buttonHidden() {
343 if ((Boolean) toggleAction.getValue("selected")) {
344 toggleAction.actionPerformed(null);
345 }
346 }
347
348 @Override
349 public void buttonShown() {
350 unfurlDialog();
351 }
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", 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
457 * in a detached dialog.
458 *
459 */
460 public void closeDetachedDialog() {
461 if (detachedDialog != null) {
462 detachedDialog.setVisible(false);
463 detachedDialog.getContentPane().removeAll();
464 detachedDialog.dispose();
465 }
466 }
467
468 /**
469 * Called when toggle dialog is shown (after it was created or expanded). Descendants may overwrite this
470 * method, it's a good place to register listeners needed to keep dialog updated
471 */
472 public void showNotify() {
473
474 }
475
476 /**
477 * Called when toggle dialog is hidden (collapsed, removed, MapFrame is removed, ...). Good place to unregister
478 * listeners
479 */
480 public void hideNotify() {
481
482 }
483
484 /**
485 * The title bar displayed in docked mode
486 *
487 */
488 protected class TitleBar extends JPanel {
489 /** the label which shows whether the toggle dialog is expanded or collapsed */
490 private final JLabel lblMinimized;
491 /** the label which displays the dialog's title **/
492 private final JLabel lblTitle;
493 private final JComponent lblTitleWeak;
494 /** the button which shows whether buttons are dynamic or not */
495 private final JButton buttonsHide;
496 /** the contextual menu **/
497 private DialogPopupMenu popupMenu;
498
499 public TitleBar(String toggleDialogName, String iconName) {
500 setLayout(new GridBagLayout());
501
502 lblMinimized = new JLabel(ImageProvider.get("misc", "normal"));
503 add(lblMinimized);
504
505 // scale down the dialog icon
506 lblTitle = new JLabel("", new ImageProvider("dialogs", iconName).setWidth(16).get(), JLabel.TRAILING);
507 lblTitle.setIconTextGap(8);
508
509 JPanel conceal = new JPanel();
510 conceal.add(lblTitle);
511 conceal.setVisible(false);
512 add(conceal, GBC.std());
513
514 // Cannot add the label directly since it would displace other elements on resize
515 lblTitleWeak = new JComponent() {
516 @Override
517 public void paintComponent(Graphics g) {
518 lblTitle.paint(g);
519 }
520 };
521 lblTitleWeak.setPreferredSize(new Dimension(Integer.MAX_VALUE,20));
522 lblTitleWeak.setMinimumSize(new Dimension(0,20));
523 add(lblTitleWeak, GBC.std().fill(GBC.HORIZONTAL));
524
525 buttonsHide = new JButton(ImageProvider.get("misc", buttonHiding != ButtonHidingType.ALWAYS_SHOWN
526 ? /* ICON(misc/)*/ "buttonhide" : /* ICON(misc/)*/ "buttonshow"));
527 buttonsHide.setToolTipText(tr("Toggle dynamic buttons"));
528 buttonsHide.setBorder(BorderFactory.createEmptyBorder());
529 buttonsHide.addActionListener(
530 new ActionListener() {
531 @Override
532 public void actionPerformed(ActionEvent e) {
533 JRadioButtonMenuItem item = (buttonHiding == ButtonHidingType.DYNAMIC) ? alwaysShown : dynamic;
534 item.setSelected(true);
535 item.getAction().actionPerformed(null);
536 }
537 }
538 );
539 add(buttonsHide);
540
541 // show the pref button if applicable
542 if (preferenceClass != null) {
543 JButton pref = new JButton(new ImageProvider("preference").setWidth(16).get());
544 pref.setToolTipText(tr("Open preferences for this panel"));
545 pref.setBorder(BorderFactory.createEmptyBorder());
546 pref.addActionListener(
547 new ActionListener(){
548 @Override
549 @SuppressWarnings("unchecked")
550 public void actionPerformed(ActionEvent e) {
551 final PreferenceDialog p = new PreferenceDialog(Main.parent);
552 if (TabPreferenceSetting.class.isAssignableFrom(preferenceClass)) {
553 p.selectPreferencesTabByClass((Class<? extends TabPreferenceSetting>) preferenceClass);
554 } else if (SubPreferenceSetting.class.isAssignableFrom(preferenceClass)) {
555 p.selectSubPreferencesTabByClass((Class<? extends SubPreferenceSetting>) preferenceClass);
556 }
557 p.setVisible(true);
558 }
559 }
560 );
561 add(pref);
562 }
563
564 // show the sticky button
565 JButton sticky = new JButton(ImageProvider.get("misc", "sticky"));
566 sticky.setToolTipText(tr("Undock the panel"));
567 sticky.setBorder(BorderFactory.createEmptyBorder());
568 sticky.addActionListener(
569 new ActionListener(){
570 @Override
571 public void actionPerformed(ActionEvent e) {
572 detach();
573 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
574 }
575 }
576 );
577 add(sticky);
578
579 // show the close button
580 JButton close = new JButton(ImageProvider.get("misc", "close"));
581 close.setToolTipText(tr("Close this panel. You can reopen it with the buttons in the left toolbar."));
582 close.setBorder(BorderFactory.createEmptyBorder());
583 close.addActionListener(
584 new ActionListener(){
585 @Override
586 public void actionPerformed(ActionEvent e) {
587 hideDialog();
588 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
589 hideNotify();
590 }
591 }
592 );
593 add(close);
594 setToolTipText(tr("Click to minimize/maximize the panel content"));
595 setTitle(toggleDialogName);
596 }
597
598 public void setTitle(String title) {
599 lblTitle.setText(title);
600 lblTitleWeak.repaint();
601 }
602
603 public String getTitle() {
604 return lblTitle.getText();
605 }
606
607 public class DialogPopupMenu extends JPopupMenu {
608
609 /**
610 * Constructs a new {@code DialogPopupMenu}.
611 */
612 public DialogPopupMenu() {
613 alwaysShown.setSelected(buttonHiding == ButtonHidingType.ALWAYS_SHOWN);
614 dynamic.setSelected(buttonHiding == ButtonHidingType.DYNAMIC);
615 alwaysHidden.setSelected(buttonHiding == ButtonHidingType.ALWAYS_HIDDEN);
616 ButtonGroup buttonHidingGroup = new ButtonGroup();
617 JMenu buttonHidingMenu = new JMenu(tr("Side buttons"));
618 for (JRadioButtonMenuItem rb : new JRadioButtonMenuItem[]{alwaysShown, dynamic, alwaysHidden}) {
619 buttonHidingGroup.add(rb);
620 buttonHidingMenu.add(rb);
621 }
622 add(buttonHidingMenu);
623 for (javax.swing.Action action: buttonActions) {
624 add(action);
625 }
626 }
627 }
628
629 public final void registerMouseListener() {
630 popupMenu = new DialogPopupMenu();
631 addMouseListener(new MouseEventHandler());
632 }
633
634 class MouseEventHandler extends PopupMenuLauncher {
635 public MouseEventHandler() {
636 super(popupMenu);
637 }
638 @Override
639 public void mouseClicked(MouseEvent e) {
640 if (SwingUtilities.isLeftMouseButton(e)) {
641 if (isCollapsed) {
642 expand();
643 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, ToggleDialog.this);
644 } else {
645 collapse();
646 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
647 }
648 }
649 }
650 }
651 }
652
653 /**
654 * The dialog class used to display toggle dialogs in a detached window.
655 *
656 */
657 private class DetachedDialog extends JDialog {
658 public DetachedDialog() {
659 super(JOptionPane.getFrameForComponent(Main.parent));
660 getContentPane().add(ToggleDialog.this);
661 addWindowListener(new WindowAdapter(){
662 @Override public void windowClosing(WindowEvent e) {
663 rememberGeometry();
664 getContentPane().removeAll();
665 dispose();
666 if (dockWhenClosingDetachedDlg()) {
667 dock();
668 if (isDialogInCollapsedView()) {
669 expand();
670 }
671 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
672 } else {
673 hideDialog();
674 hideNotify();
675 }
676 }
677 });
678 addComponentListener(new ComponentAdapter() {
679 @Override public void componentMoved(ComponentEvent e) {
680 rememberGeometry();
681 }
682 @Override public void componentResized(ComponentEvent e) {
683 rememberGeometry();
684 }
685 });
686
687 try {
688 new WindowGeometry(preferencePrefix+".geometry").applySafe(this);
689 } catch (WindowGeometryException e) {
690 ToggleDialog.this.setPreferredSize(ToggleDialog.this.getDefaultDetachedSize());
691 pack();
692 setLocationRelativeTo(Main.parent);
693 }
694 setTitle(titleBar.getTitle());
695 HelpUtil.setHelpContext(getRootPane(), helpTopic());
696 }
697
698 protected void rememberGeometry() {
699 if (detachedDialog != null) {
700 new WindowGeometry(detachedDialog).remember(preferencePrefix+".geometry");
701 }
702 }
703 }
704
705 /**
706 * Replies the action to toggle the visible state of this toggle dialog
707 *
708 * @return the action to toggle the visible state of this toggle dialog
709 */
710 public AbstractAction getToggleAction() {
711 return toggleAction;
712 }
713
714 /**
715 * Replies the prefix for the preference settings of this dialog.
716 *
717 * @return the prefix for the preference settings of this dialog.
718 */
719 public String getPreferencePrefix() {
720 return preferencePrefix;
721 }
722
723 /**
724 * Sets the dialogsPanel managing all toggle dialogs.
725 * @param dialogsPanel The panel managing all toggle dialogs
726 */
727 public void setDialogsPanel(DialogsPanel dialogsPanel) {
728 this.dialogsPanel = dialogsPanel;
729 }
730
731 /**
732 * Replies the name of this toggle dialog
733 */
734 @Override
735 public String getName() {
736 return "toggleDialog." + preferencePrefix;
737 }
738
739 /**
740 * Sets the title.
741 * @param title The dialog's title
742 */
743 public void setTitle(String title) {
744 titleBar.setTitle(title);
745 if (detachedDialog != null) {
746 detachedDialog.setTitle(title);
747 }
748 }
749
750 protected void setIsShowing(boolean val) {
751 isShowing = val;
752 Main.pref.put(preferencePrefix+".visible", val);
753 stateChanged();
754 }
755
756 protected void setIsDocked(boolean val) {
757 if (buttonsPanel != null) {
758 buttonsPanel.setVisible(val ? buttonHiding != ButtonHidingType.ALWAYS_HIDDEN : true);
759 }
760 isDocked = val;
761 Main.pref.put(preferencePrefix+".docked", val);
762 stateChanged();
763 }
764
765 protected void setIsCollapsed(boolean val) {
766 isCollapsed = val;
767 Main.pref.put(preferencePrefix+".minimized", val);
768 stateChanged();
769 }
770
771 protected void setIsButtonHiding(ButtonHidingType val) {
772 buttonHiding = val;
773 propButtonHiding.put(val);
774 refreshHidingButtons();
775 }
776
777 /**
778 * Returns the preferred height of this dialog.
779 * @return The preferred height if the toggle dialog is expanded
780 */
781 public int getPreferredHeight() {
782 return preferredHeight;
783 }
784
785 @Override
786 public String helpTopic() {
787 String help = getClass().getName();
788 help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
789 return "Dialog/"+help;
790 }
791
792 @Override
793 public String toString() {
794 return name;
795 }
796
797 /**
798 * Determines if this dialog is showing either as docked or as detached dialog.
799 * @return {@code true} if this dialog is showing either as docked or as detached dialog
800 */
801 public boolean isDialogShowing() {
802 return isShowing;
803 }
804
805 /**
806 * Determines if this dialog is docked and expanded.
807 * @return {@code true} if this dialog is docked and expanded
808 */
809 public boolean isDialogInDefaultView() {
810 return isShowing && isDocked && (!isCollapsed);
811 }
812
813 /**
814 * Determines if this dialog is docked and collapsed.
815 * @return {@code true} if this dialog is docked and collapsed
816 */
817 public boolean isDialogInCollapsedView() {
818 return isShowing && isDocked && isCollapsed;
819 }
820
821 public void setButton(JToggleButton button) {
822 this.button = button;
823 }
824
825 public JToggleButton getButton() {
826 return button;
827 }
828
829 /***
830 * The following methods are intended to be overridden, in order to customize
831 * the toggle dialog behavior.
832 **/
833
834 /**
835 * Change the Geometry of the detached dialog to better fit the content.
836 */
837 protected Rectangle getDetachedGeometry(Rectangle last) {
838 return last;
839 }
840
841 /**
842 * Default size of the detached dialog.
843 * Override this method to customize the initial dialog size.
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.