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

Last change on this file since 7668 was 7668, checked in by stoecker, 10 years ago

cleanup icons, mark undetected icons, set proper mimetype, delete unused icons, update geticons script

  • Property svn:eol-style set to native
File size: 33.4 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 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 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
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 lblTitle_weak.repaint();
600 }
601
602 public String getTitle() {
603 return lblTitle.getText();
604 }
605
606 public class DialogPopupMenu extends JPopupMenu {
607 private final ButtonGroup buttonHidingGroup = new ButtonGroup();
608 private final JMenu buttonHidingMenu = new JMenu(tr("Side buttons"));
609
610 public DialogPopupMenu() {
611 alwaysShown.setSelected(buttonHiding == ButtonHidingType.ALWAYS_SHOWN);
612 dynamic.setSelected(buttonHiding == ButtonHidingType.DYNAMIC);
613 alwaysHidden.setSelected(buttonHiding == ButtonHidingType.ALWAYS_HIDDEN);
614 for (JRadioButtonMenuItem rb : new JRadioButtonMenuItem[]{alwaysShown, dynamic, alwaysHidden}) {
615 buttonHidingGroup.add(rb);
616 buttonHidingMenu.add(rb);
617 }
618 add(buttonHidingMenu);
619 for (javax.swing.Action action: buttonActions) {
620 add(action);
621 }
622 }
623 }
624
625 public final void registerMouseListener() {
626 popupMenu = new DialogPopupMenu();
627 addMouseListener(new MouseEventHandler());
628 }
629
630 class MouseEventHandler extends PopupMenuLauncher {
631 public MouseEventHandler() {
632 super(popupMenu);
633 }
634 @Override
635 public void mouseClicked(MouseEvent e) {
636 if (SwingUtilities.isLeftMouseButton(e)) {
637 if (isCollapsed) {
638 expand();
639 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, ToggleDialog.this);
640 } else {
641 collapse();
642 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
643 }
644 }
645 }
646 }
647 }
648
649 /**
650 * The dialog class used to display toggle dialogs in a detached window.
651 *
652 */
653 private class DetachedDialog extends JDialog {
654 public DetachedDialog() {
655 super(JOptionPane.getFrameForComponent(Main.parent));
656 getContentPane().add(ToggleDialog.this);
657 addWindowListener(new WindowAdapter(){
658 @Override public void windowClosing(WindowEvent e) {
659 rememberGeometry();
660 getContentPane().removeAll();
661 dispose();
662 if (dockWhenClosingDetachedDlg()) {
663 dock();
664 if (isDialogInCollapsedView()) {
665 expand();
666 }
667 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
668 } else {
669 hideDialog();
670 hideNotify();
671 }
672 }
673 });
674 addComponentListener(new ComponentAdapter() {
675 @Override public void componentMoved(ComponentEvent e) {
676 rememberGeometry();
677 }
678 @Override public void componentResized(ComponentEvent e) {
679 rememberGeometry();
680 }
681 });
682
683 try {
684 new WindowGeometry(preferencePrefix+".geometry").applySafe(this);
685 } catch (WindowGeometryException e) {
686 ToggleDialog.this.setPreferredSize(ToggleDialog.this.getDefaultDetachedSize());
687 pack();
688 setLocationRelativeTo(Main.parent);
689 }
690 setTitle(titleBar.getTitle());
691 HelpUtil.setHelpContext(getRootPane(), helpTopic());
692 }
693
694 protected void rememberGeometry() {
695 if (detachedDialog != null) {
696 new WindowGeometry(detachedDialog).remember(preferencePrefix+".geometry");
697 }
698 }
699 }
700
701 /**
702 * Replies the action to toggle the visible state of this toggle dialog
703 *
704 * @return the action to toggle the visible state of this toggle dialog
705 */
706 public AbstractAction getToggleAction() {
707 return toggleAction;
708 }
709
710 /**
711 * Replies the prefix for the preference settings of this dialog.
712 *
713 * @return the prefix for the preference settings of this dialog.
714 */
715 public String getPreferencePrefix() {
716 return preferencePrefix;
717 }
718
719 /**
720 * Sets the dialogsPanel managing all toggle dialogs.
721 * @param dialogsPanel The panel managing all toggle dialogs
722 */
723 public void setDialogsPanel(DialogsPanel dialogsPanel) {
724 this.dialogsPanel = dialogsPanel;
725 }
726
727 /**
728 * Replies the name of this toggle dialog
729 */
730 @Override
731 public String getName() {
732 return "toggleDialog." + preferencePrefix;
733 }
734
735 /**
736 * Sets the title.
737 * @param title The dialog's title
738 */
739 public void setTitle(String title) {
740 titleBar.setTitle(title);
741 if (detachedDialog != null) {
742 detachedDialog.setTitle(title);
743 }
744 }
745
746 protected void setIsShowing(boolean val) {
747 isShowing = val;
748 Main.pref.put(preferencePrefix+".visible", val);
749 stateChanged();
750 }
751
752 protected void setIsDocked(boolean val) {
753 if (buttonsPanel != null) {
754 buttonsPanel.setVisible(val ? buttonHiding != ButtonHidingType.ALWAYS_HIDDEN : true);
755 }
756 isDocked = val;
757 Main.pref.put(preferencePrefix+".docked", val);
758 stateChanged();
759 }
760
761 protected void setIsCollapsed(boolean val) {
762 isCollapsed = val;
763 Main.pref.put(preferencePrefix+".minimized", val);
764 stateChanged();
765 }
766
767 protected void setIsButtonHiding(ButtonHidingType val) {
768 buttonHiding = val;
769 PROP_BUTTON_HIDING.put(val);
770 refreshHidingButtons();
771 }
772
773 /**
774 * Returns the preferred height of this dialog.
775 * @return The preferred height if the toggle dialog is expanded
776 */
777 public int getPreferredHeight() {
778 return preferredHeight;
779 }
780
781 @Override
782 public String helpTopic() {
783 String help = getClass().getName();
784 help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
785 return "Dialog/"+help;
786 }
787
788 @Override
789 public String toString() {
790 return name;
791 }
792
793 /**
794 * Determines if this dialog is showing either as docked or as detached dialog.
795 * @return {@code true} if this dialog is showing either as docked or as detached dialog
796 */
797 public boolean isDialogShowing() {
798 return isShowing;
799 }
800
801 /**
802 * Determines if this dialog is docked and expanded.
803 * @return {@code true} if this dialog is docked and expanded
804 */
805 public boolean isDialogInDefaultView() {
806 return isShowing && isDocked && (! isCollapsed);
807 }
808
809 /**
810 * Determines if this dialog is docked and collapsed.
811 * @return {@code true} if this dialog is docked and collapsed
812 */
813 public boolean isDialogInCollapsedView() {
814 return isShowing && isDocked && isCollapsed;
815 }
816
817 public void setButton(JToggleButton button) {
818 this.button = button;
819 }
820
821 public JToggleButton getButton() {
822 return button;
823 }
824
825 /***
826 * The following methods are intended to be overridden, in order to customize
827 * the toggle dialog behavior.
828 **/
829
830 /**
831 * Change the Geometry of the detached dialog to better fit the content.
832 */
833 protected Rectangle getDetachedGeometry(Rectangle last) {
834 return last;
835 }
836
837 /**
838 * Default size of the detached dialog.
839 * Override this method to customize the initial dialog size.
840 */
841 protected Dimension getDefaultDetachedSize() {
842 return new Dimension(dialogsPanel.getWidth(), preferredHeight);
843 }
844
845 /**
846 * Do something when the toggleButton is pressed.
847 */
848 protected void toggleButtonHook() {
849 }
850
851 protected boolean dockWhenClosingDetachedDlg() {
852 return true;
853 }
854
855 /**
856 * primitive stateChangedListener for subclasses
857 */
858 protected void stateChanged() {
859 }
860
861 protected Component createLayout(Component data, boolean scroll, Collection<SideButton> buttons) {
862 return createLayout(data, scroll, buttons, (Collection<SideButton>[]) null);
863 }
864
865 @SafeVarargs
866 protected final Component createLayout(Component data, boolean scroll, Collection<SideButton> firstButtons, Collection<SideButton>... nextButtons) {
867 if (scroll) {
868 data = new JScrollPane(data);
869 }
870 LinkedList<Collection<SideButton>> buttons = new LinkedList<>();
871 buttons.addFirst(firstButtons);
872 if (nextButtons != null) {
873 buttons.addAll(Arrays.asList(nextButtons));
874 }
875 add(data, BorderLayout.CENTER);
876 if (!buttons.isEmpty() && buttons.get(0) != null && !buttons.get(0).isEmpty()) {
877 buttonsPanel = new JPanel(new GridLayout(buttons.size(), 1));
878 for (Collection<SideButton> buttonRow : buttons) {
879 if (buttonRow == null) {
880 continue;
881 }
882 final JPanel buttonRowPanel = new JPanel(Main.pref.getBoolean("dialog.align.left", false)
883 ? new FlowLayout(FlowLayout.LEFT) : new GridLayout(1, buttonRow.size()));
884 buttonsPanel.add(buttonRowPanel);
885 for (SideButton button : buttonRow) {
886 buttonRowPanel.add(button);
887 javax.swing.Action action = button.getAction();
888 if (action != null) {
889 buttonActions.add(action);
890 } else {
891 Main.warn("Button " + button + " doesn't have action defined");
892 Main.error(new Exception());
893 }
894 }
895 }
896 add(buttonsPanel, BorderLayout.SOUTH);
897 dynamicButtonsPropertyChanged();
898 } else {
899 titleBar.buttonsHide.setVisible(false);
900 }
901
902 // Register title bar mouse listener only after buttonActions has been initialized to have a complete popup menu
903 titleBar.registerMouseListener();
904
905 return data;
906 }
907
908 @Override
909 public void eventDispatched(AWTEvent event) {
910 if(isShowing() && !isCollapsed && isDocked && buttonHiding == ButtonHidingType.DYNAMIC) {
911 if (buttonsPanel != null) {
912 Rectangle b = this.getBounds();
913 b.setLocation(getLocationOnScreen());
914 if (b.contains(((MouseEvent)event).getLocationOnScreen())) {
915 if(!buttonsPanel.isVisible()) {
916 buttonsPanel.setVisible(true);
917 }
918 } else if (buttonsPanel.isVisible()) {
919 buttonsPanel.setVisible(false);
920 }
921 }
922 }
923 }
924
925 @Override
926 public void preferenceChanged(PreferenceChangeEvent e) {
927 if (e.getKey().equals(PROP_DYNAMIC_BUTTONS.getKey())) {
928 dynamicButtonsPropertyChanged();
929 }
930 }
931
932 private void dynamicButtonsPropertyChanged() {
933 boolean propEnabled = PROP_DYNAMIC_BUTTONS.get();
934 if (propEnabled) {
935 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_MOTION_EVENT_MASK);
936 } else {
937 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
938 }
939 titleBar.buttonsHide.setVisible(propEnabled);
940 refreshHidingButtons();
941 }
942
943 private void refreshHidingButtons() {
944 titleBar.buttonsHide.setIcon(ImageProvider.get("misc", buttonHiding != ButtonHidingType.ALWAYS_SHOWN
945 ? /* ICON(misc/)*/ "buttonhide" : /* ICON(misc/)*/ "buttonshow"));
946 titleBar.buttonsHide.setEnabled(buttonHiding != ButtonHidingType.ALWAYS_HIDDEN);
947 if (buttonsPanel != null) {
948 buttonsPanel.setVisible(buttonHiding != ButtonHidingType.ALWAYS_HIDDEN || !isDocked);
949 }
950 stateChanged();
951 }
952}
Note: See TracBrowser for help on using the repository browser.