001 // License: GPL. See LICENSE file for details.
002 package org.openstreetmap.josm.gui.dialogs;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.awt.AWTEvent;
007 import java.awt.BorderLayout;
008 import java.awt.Component;
009 import java.awt.Container;
010 import java.awt.Dimension;
011 import java.awt.FlowLayout;
012 import java.awt.Graphics;
013 import java.awt.GridBagLayout;
014 import java.awt.GridLayout;
015 import java.awt.Image;
016 import java.awt.Rectangle;
017 import java.awt.Toolkit;
018 import java.awt.event.AWTEventListener;
019 import java.awt.event.ActionEvent;
020 import java.awt.event.ActionListener;
021 import java.awt.event.ComponentAdapter;
022 import java.awt.event.ComponentEvent;
023 import java.awt.event.MouseAdapter;
024 import java.awt.event.MouseEvent;
025 import java.awt.event.WindowAdapter;
026 import java.awt.event.WindowEvent;
027 import java.beans.PropertyChangeEvent;
028 import java.util.ArrayList;
029 import java.util.Arrays;
030 import java.util.Collection;
031 import java.util.LinkedList;
032 import java.util.List;
033
034 import javax.swing.AbstractAction;
035 import javax.swing.BorderFactory;
036 import javax.swing.ImageIcon;
037 import javax.swing.JButton;
038 import javax.swing.JCheckBoxMenuItem;
039 import javax.swing.JComponent;
040 import javax.swing.JDialog;
041 import javax.swing.JLabel;
042 import javax.swing.JMenu;
043 import javax.swing.JOptionPane;
044 import javax.swing.JPanel;
045 import javax.swing.JPopupMenu;
046 import javax.swing.JRadioButtonMenuItem;
047 import javax.swing.JScrollPane;
048 import javax.swing.JToggleButton;
049
050 import org.openstreetmap.josm.Main;
051 import org.openstreetmap.josm.actions.JosmAction;
052 import org.openstreetmap.josm.data.preferences.ParametrizedEnumProperty;
053 import org.openstreetmap.josm.gui.MainMenu;
054 import org.openstreetmap.josm.gui.ShowHideButtonListener;
055 import org.openstreetmap.josm.gui.SideButton;
056 import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
057 import org.openstreetmap.josm.gui.help.HelpUtil;
058 import org.openstreetmap.josm.gui.help.Helpful;
059 import org.openstreetmap.josm.tools.Destroyable;
060 import org.openstreetmap.josm.tools.GBC;
061 import org.openstreetmap.josm.tools.ImageProvider;
062 import org.openstreetmap.josm.tools.Shortcut;
063 import org.openstreetmap.josm.tools.WindowGeometry;
064 import org.openstreetmap.josm.tools.WindowGeometry.WindowGeometryException;
065
066 /**
067 * This class is a toggle dialog that can be turned on and off.
068 *
069 */
070 public class ToggleDialog extends JPanel implements ShowHideButtonListener, Helpful, AWTEventListener {
071
072 public enum ButtonHiddingType {
073 ALWAYS_SHOWN, ALWAYS_HIDDEN, DYNAMIC
074 }
075
076 private final ParametrizedEnumProperty<ButtonHiddingType> PROP_BUTTON_HIDING = new ParametrizedEnumProperty<ToggleDialog.ButtonHiddingType>(ButtonHiddingType.class, ButtonHiddingType.DYNAMIC) {
077 @Override
078 protected String getKey(String... params) {
079 return preferencePrefix + ".buttonhiding";
080 }
081 @Override
082 protected ButtonHiddingType parse(String s) {
083 try {
084 return super.parse(s);
085 } catch (IllegalArgumentException e) {
086 // Legacy settings
087 return Boolean.parseBoolean(s)?ButtonHiddingType.DYNAMIC:ButtonHiddingType.ALWAYS_HIDDEN;
088 }
089 }
090 };
091
092 /** The action to toggle this dialog */
093 protected ToggleDialogAction toggleAction;
094 protected String preferencePrefix;
095 final protected String name;
096
097 /** DialogsPanel that manages all ToggleDialogs */
098 protected DialogsPanel dialogsPanel;
099
100 protected TitleBar titleBar;
101
102 /**
103 * Indicates whether the dialog is showing or not.
104 */
105 protected boolean isShowing;
106 /**
107 * If isShowing is true, indicates whether the dialog is docked or not, e. g.
108 * shown as part of the main window or as a separate dialog window.
109 */
110 protected boolean isDocked;
111 /**
112 * If isShowing and isDocked are true, indicates whether the dialog is
113 * currently minimized or not.
114 */
115 protected boolean isCollapsed;
116 /**
117 * Indicates whether dynamic button hiding is active or not.
118 */
119 protected ButtonHiddingType buttonHiding;
120
121 /** the preferred height if the toggle dialog is expanded */
122 private int preferredHeight;
123
124 /** the label in the title bar which shows whether the toggle dialog is expanded or collapsed */
125 private JLabel lblMinimized;
126
127 /** the label in the title bar which shows whether buttons are dynamic or not */
128 private JButton buttonsHide = null;
129
130 /** the JDialog displaying the toggle dialog as undocked dialog */
131 protected JDialog detachedDialog;
132
133 protected JToggleButton button;
134 private JPanel buttonsPanel;
135 private List<javax.swing.Action> buttonActions = new ArrayList<javax.swing.Action>();
136
137 /** holds the menu entry in the windows menu. Required to properly
138 * toggle the checkbox on show/hide
139 */
140 protected JCheckBoxMenuItem windowMenuItem;
141
142 /**
143 * Constructor
144 * (see below)
145 */
146 public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight) {
147 this(name, iconName, tooltip, shortcut, preferredHeight, false);
148 }
149 /**
150 * Constructor
151 *
152 * @param name the name of the dialog
153 * @param iconName the name of the icon to be displayed
154 * @param tooltip the tool tip
155 * @param shortcut the shortcut
156 * @param preferredHeight the preferred height for the dialog
157 * @param defShow if the dialog should be shown by default, if there is no preference
158 */
159 public ToggleDialog(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight, boolean defShow) {
160 super(new BorderLayout());
161 this.preferencePrefix = iconName;
162 this.name = name;
163
164 /** Use the full width of the parent element */
165 setPreferredSize(new Dimension(0, preferredHeight));
166 /** Override any minimum sizes of child elements so the user can resize freely */
167 setMinimumSize(new Dimension(0,0));
168 this.preferredHeight = preferredHeight;
169 toggleAction = new ToggleDialogAction(name, "dialogs/"+iconName, tooltip, shortcut, iconName);
170 String helpId = "Dialog/"+getClass().getName().substring(getClass().getName().lastIndexOf('.')+1);
171 toggleAction.putValue("help", helpId.substring(0, helpId.length()-6));
172
173 isShowing = Main.pref.getBoolean(preferencePrefix+".visible", defShow);
174 isDocked = Main.pref.getBoolean(preferencePrefix+".docked", true);
175 isCollapsed = Main.pref.getBoolean(preferencePrefix+".minimized", false);
176 buttonHiding = PROP_BUTTON_HIDING.get();
177
178 /** show the minimize button */
179 titleBar = new TitleBar(name, iconName);
180 add(titleBar, BorderLayout.NORTH);
181
182 setBorder(BorderFactory.createEtchedBorder());
183
184 Main.redirectToMainContentPane(this);
185
186 windowMenuItem = MainMenu.addWithCheckbox(Main.main.menu.windowMenu,
187 (JosmAction) getToggleAction(),
188 MainMenu.WINDOW_MENU_GROUP.TOGGLE_DIALOG);
189 }
190
191 /**
192 * The action to toggle the visibility state of this toggle dialog.
193 *
194 * Emits {@link PropertyChangeEvent}s for the property <tt>selected</tt>:
195 * <ul>
196 * <li>true, if the dialog is currently visible</li>
197 * <li>false, if the dialog is currently invisible</li>
198 * </ul>
199 *
200 */
201 public final class ToggleDialogAction extends JosmAction {
202
203 private ToggleDialogAction(String name, String iconName, String tooltip, Shortcut shortcut, String prefname) {
204 super(name, iconName, tooltip, shortcut, false);
205 }
206
207 public void actionPerformed(ActionEvent e) {
208 toggleButtonHook();
209 if(getValue("toolbarbutton") != null && getValue("toolbarbutton") instanceof JButton) {
210 ((JButton) getValue("toolbarbutton")).setSelected(!isShowing);
211 }
212 if (isShowing) {
213 hideDialog();
214 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
215 hideNotify();
216 } else {
217 showDialog();
218 if (isDocked && isCollapsed) {
219 expand();
220 }
221 if (isDocked) {
222 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
223 }
224 showNotify();
225 }
226 }
227
228 @Override
229 public void destroy() {
230 super.destroy();
231 }
232 }
233
234 /**
235 * Shows the dialog
236 */
237 public void showDialog() {
238 setIsShowing(true);
239 if (!isDocked) {
240 detach();
241 } else {
242 dock();
243 this.setVisible(true);
244 }
245 // toggling the selected value in order to enforce PropertyChangeEvents
246 setIsShowing(true);
247 windowMenuItem.setState(true);
248 toggleAction.putValue("selected", false);
249 toggleAction.putValue("selected", true);
250 }
251
252 /**
253 * Changes the state of the dialog such that the user can see the content.
254 * (takes care of the panel reconstruction)
255 */
256 public void unfurlDialog() {
257 if (isDialogInDefaultView())
258 return;
259 if (isDialogInCollapsedView()) {
260 expand();
261 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, this);
262 } else if (!isDialogShowing()) {
263 showDialog();
264 if (isDocked && isCollapsed) {
265 expand();
266 }
267 if (isDocked) {
268 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, this);
269 }
270 showNotify();
271 }
272 }
273
274 @Override
275 public void buttonHidden() {
276 if ((Boolean) toggleAction.getValue("selected")) {
277 toggleAction.actionPerformed(null);
278 }
279 }
280
281 public void buttonShown() {
282 unfurlDialog();
283 }
284
285
286 /**
287 * Hides the dialog
288 */
289 public void hideDialog() {
290 closeDetachedDialog();
291 this.setVisible(false);
292 windowMenuItem.setState(false);
293 setIsShowing(false);
294 toggleAction.putValue("selected", false);
295 }
296
297 /**
298 * Displays the toggle dialog in the toggle dialog view on the right
299 * of the main map window.
300 *
301 */
302 protected void dock() {
303 detachedDialog = null;
304 titleBar.setVisible(true);
305 setIsDocked(true);
306 }
307
308 /**
309 * Display the dialog in a detached window.
310 *
311 */
312 protected void detach() {
313 setContentVisible(true);
314 this.setVisible(true);
315 titleBar.setVisible(false);
316 detachedDialog = new DetachedDialog();
317 detachedDialog.setVisible(true);
318 setIsShowing(true);
319 setIsDocked(false);
320 }
321
322 /**
323 * Collapses the toggle dialog to the title bar only
324 *
325 */
326 public void collapse() {
327 if (isDialogInDefaultView()) {
328 setContentVisible(false);
329 setIsCollapsed(true);
330 setPreferredSize(new Dimension(0,20));
331 setMaximumSize(new Dimension(Integer.MAX_VALUE,20));
332 setMinimumSize(new Dimension(Integer.MAX_VALUE,20));
333 lblMinimized.setIcon(ImageProvider.get("misc", "minimized"));
334 }
335 else throw new IllegalStateException();
336 }
337
338 /**
339 * Expands the toggle dialog
340 */
341 protected void expand() {
342 if (isDialogInCollapsedView()) {
343 setContentVisible(true);
344 setIsCollapsed(false);
345 setPreferredSize(new Dimension(0,preferredHeight));
346 setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
347 lblMinimized.setIcon(ImageProvider.get("misc", "normal"));
348 }
349 else throw new IllegalStateException();
350 }
351
352 /**
353 * Sets the visibility of all components in this toggle dialog, except the title bar
354 *
355 * @param visible true, if the components should be visible; false otherwise
356 */
357 protected void setContentVisible(boolean visible) {
358 Component comps[] = getComponents();
359 for(int i=0; i<comps.length; i++) {
360 if(comps[i] != titleBar) {
361 comps[i].setVisible(visible);
362 }
363 }
364 }
365
366 public void destroy() {
367 closeDetachedDialog();
368 hideNotify();
369 Main.main.menu.windowMenu.remove(windowMenuItem);
370 Toolkit.getDefaultToolkit().removeAWTEventListener(this);
371 destroyComponents(this);
372 }
373
374 private void destroyComponents(Component component) {
375 if (component instanceof Container) {
376 for (Component c: ((Container)component).getComponents()) {
377 destroyComponents(c);
378 }
379 }
380 if (component instanceof Destroyable) {
381 ((Destroyable) component).destroy();
382 }
383 }
384
385 /**
386 * Closes the detached dialog if this toggle dialog is currently displayed
387 * in a detached dialog.
388 *
389 */
390 public void closeDetachedDialog() {
391 if (detachedDialog != null) {
392 detachedDialog.setVisible(false);
393 detachedDialog.getContentPane().removeAll();
394 detachedDialog.dispose();
395 }
396 }
397
398 /**
399 * Called when toggle dialog is shown (after it was created or expanded). Descendants may overwrite this
400 * method, it's a good place to register listeners needed to keep dialog updated
401 */
402 public void showNotify() {
403
404 }
405
406 /**
407 * Called when toggle dialog is hidden (collapsed, removed, MapFrame is removed, ...). Good place to unregister
408 * listeners
409 */
410 public void hideNotify() {
411
412 }
413
414 /**
415 * The title bar displayed in docked mode
416 *
417 */
418 protected class TitleBar extends JPanel {
419 final private JLabel lblTitle;
420 final private JComponent lblTitle_weak;
421
422 public TitleBar(String toggleDialogName, String iconName) {
423 setLayout(new GridBagLayout());
424
425 lblMinimized = new JLabel(ImageProvider.get("misc", "normal"));
426 add(lblMinimized);
427
428 // scale down the dialog icon
429 ImageIcon inIcon = ImageProvider.get("dialogs", iconName);
430 ImageIcon smallIcon = new ImageIcon(inIcon.getImage().getScaledInstance(16 , 16, Image.SCALE_SMOOTH));
431 lblTitle = new JLabel("",smallIcon, JLabel.TRAILING);
432 lblTitle.setIconTextGap(8);
433
434 JPanel conceal = new JPanel();
435 conceal.add(lblTitle);
436 conceal.setVisible(false);
437 add(conceal, GBC.std());
438
439 // Cannot add the label directly since it would displace other elements on resize
440 lblTitle_weak = new JComponent() {
441 @Override
442 public void paintComponent(Graphics g) {
443 lblTitle.paint(g);
444 }
445 };
446 lblTitle_weak.setPreferredSize(new Dimension(Integer.MAX_VALUE,20));
447 lblTitle_weak.setMinimumSize(new Dimension(0,20));
448 add(lblTitle_weak, GBC.std().fill(GBC.HORIZONTAL));
449
450 addMouseListener(
451 new MouseAdapter() {
452 @Override
453 public void mouseClicked(MouseEvent e) {
454 if (isCollapsed) {
455 expand();
456 dialogsPanel.reconstruct(Action.COLLAPSED_TO_DEFAULT, ToggleDialog.this);
457 } else {
458 collapse();
459 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
460 }
461 }
462
463 private void maybeShowPopup(MouseEvent e) {
464 if (e.isPopupTrigger()) {
465 JPopupMenu menu = new JPopupMenu();
466 JMenu buttonHidingMenu = new JMenu(tr("Side buttons"));
467 JRadioButtonMenuItem alwaysShown = new JRadioButtonMenuItem(new AbstractAction(tr("Always shown")) {
468 @Override
469 public void actionPerformed(ActionEvent e) {
470 setIsButtonHiding(ButtonHiddingType.ALWAYS_SHOWN);
471 }
472 });
473 JRadioButtonMenuItem dynamic = new JRadioButtonMenuItem(new AbstractAction(tr("Dynamic")) {
474 @Override
475 public void actionPerformed(ActionEvent e) {
476 setIsButtonHiding(ButtonHiddingType.DYNAMIC);
477 }
478 });
479 JRadioButtonMenuItem alwaysHidden = new JRadioButtonMenuItem(new AbstractAction(tr("Always hidden")) {
480 @Override
481 public void actionPerformed(ActionEvent e) {
482 setIsButtonHiding(ButtonHiddingType.ALWAYS_HIDDEN);
483 }
484 });
485 alwaysShown.setSelected(buttonHiding == ButtonHiddingType.ALWAYS_SHOWN);
486 dynamic.setSelected(buttonHiding == ButtonHiddingType.DYNAMIC);
487 alwaysHidden.setSelected(buttonHiding == ButtonHiddingType.ALWAYS_HIDDEN);
488 buttonHidingMenu.add(alwaysShown);
489 buttonHidingMenu.add(dynamic);
490 buttonHidingMenu.add(alwaysHidden);
491 menu.add(buttonHidingMenu);
492 for (javax.swing.Action action: buttonActions) {
493 menu.add(action);
494 }
495 menu.show(TitleBar.this, e.getX(), e.getY());
496 }
497 }
498
499 @Override
500 public void mousePressed(MouseEvent e) {
501 maybeShowPopup(e);
502 }
503
504 @Override
505 public void mouseReleased(MouseEvent e) {
506 maybeShowPopup(e);
507 }
508 }
509 );
510
511 if(Main.pref.getBoolean("dialog.dynamic.buttons", true)) {
512 buttonsHide = new JButton(ImageProvider.get("misc", buttonHiding != ButtonHiddingType.ALWAYS_SHOWN ? "buttonhide" : "buttonshow"));
513 buttonsHide.setToolTipText(tr("Toggle dynamic buttons"));
514 buttonsHide.setBorder(BorderFactory.createEmptyBorder());
515 buttonsHide.addActionListener(
516 new ActionListener(){
517 public void actionPerformed(ActionEvent e) {
518 setIsButtonHiding(buttonHiding == ButtonHiddingType.ALWAYS_SHOWN?ButtonHiddingType.DYNAMIC:ButtonHiddingType.ALWAYS_SHOWN);
519 }
520 }
521 );
522 add(buttonsHide);
523 }
524
525 // show the sticky button
526 JButton sticky = new JButton(ImageProvider.get("misc", "sticky"));
527 sticky.setToolTipText(tr("Undock the panel"));
528 sticky.setBorder(BorderFactory.createEmptyBorder());
529 sticky.addActionListener(
530 new ActionListener(){
531 public void actionPerformed(ActionEvent e) {
532 detach();
533 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
534 }
535 }
536 );
537 add(sticky);
538
539 // show the close button
540 JButton close = new JButton(ImageProvider.get("misc", "close"));
541 close.setToolTipText(tr("Close this panel. You can reopen it with the buttons in the left toolbar."));
542 close.setBorder(BorderFactory.createEmptyBorder());
543 close.addActionListener(
544 new ActionListener(){
545 public void actionPerformed(ActionEvent e) {
546 hideDialog();
547 dialogsPanel.reconstruct(Action.ELEMENT_SHRINKS, null);
548 hideNotify();
549 }
550 }
551 );
552 add(close);
553 setToolTipText(tr("Click to minimize/maximize the panel content"));
554 setTitle(toggleDialogName);
555 }
556
557 public void setTitle(String title) {
558 lblTitle.setText(title);
559 lblTitle_weak.repaint();
560 }
561
562 public String getTitle() {
563 return lblTitle.getText();
564 }
565 }
566
567 /**
568 * The dialog class used to display toggle dialogs in a detached window.
569 *
570 */
571 private class DetachedDialog extends JDialog{
572 public DetachedDialog() {
573 super(JOptionPane.getFrameForComponent(Main.parent));
574 getContentPane().add(ToggleDialog.this);
575 addWindowListener(new WindowAdapter(){
576 @Override public void windowClosing(WindowEvent e) {
577 rememberGeometry();
578 getContentPane().removeAll();
579 dispose();
580 if (dockWhenClosingDetachedDlg()) {
581 dock();
582 if (isDialogInCollapsedView()) {
583 expand();
584 }
585 dialogsPanel.reconstruct(Action.INVISIBLE_TO_DEFAULT, ToggleDialog.this);
586 } else {
587 hideDialog();
588 hideNotify();
589 }
590 }
591 });
592 addComponentListener(new ComponentAdapter() {
593 @Override public void componentMoved(ComponentEvent e) {
594 rememberGeometry();
595 }
596 @Override public void componentResized(ComponentEvent e) {
597 rememberGeometry();
598 }
599 });
600
601 try {
602 new WindowGeometry(preferencePrefix+".geometry").applySafe(this);
603 } catch (WindowGeometryException e) {
604 ToggleDialog.this.setPreferredSize(ToggleDialog.this.getDefaultDetachedSize());
605 pack();
606 setLocationRelativeTo(Main.parent);
607 }
608 setTitle(titleBar.getTitle());
609 HelpUtil.setHelpContext(getRootPane(), helpTopic());
610 }
611
612 protected void rememberGeometry() {
613 if (detachedDialog != null) {
614 new WindowGeometry(detachedDialog).remember(preferencePrefix+".geometry");
615 }
616 }
617 }
618
619 /**
620 * Replies the action to toggle the visible state of this toggle dialog
621 *
622 * @return the action to toggle the visible state of this toggle dialog
623 */
624 public AbstractAction getToggleAction() {
625 return toggleAction;
626 }
627
628 /**
629 * Replies the prefix for the preference settings of this dialog.
630 *
631 * @return the prefix for the preference settings of this dialog.
632 */
633 public String getPreferencePrefix() {
634 return preferencePrefix;
635 }
636
637 /**
638 * Sets the dialogsPanel managing all toggle dialogs
639 */
640 public void setDialogsPanel(DialogsPanel dialogsPanel) {
641 this.dialogsPanel = dialogsPanel;
642 }
643
644 /**
645 * Replies the name of this toggle dialog
646 */
647 @Override
648 public String getName() {
649 return "toggleDialog." + preferencePrefix;
650 }
651
652 /**
653 * Sets the title
654 */
655 public void setTitle(String title) {
656 titleBar.setTitle(title);
657 if (detachedDialog != null) {
658 detachedDialog.setTitle(title);
659 }
660 }
661
662 protected void setIsShowing(boolean val) {
663 isShowing = val;
664 Main.pref.put(preferencePrefix+".visible", val);
665 stateChanged();
666 }
667
668 protected void setIsDocked(boolean val) {
669 if(buttonsPanel != null && buttonsHide != null) {
670 buttonsPanel.setVisible(val ? buttonHiding == ButtonHiddingType.ALWAYS_SHOWN : true);
671 }
672 isDocked = val;
673 Main.pref.put(preferencePrefix+".docked", val);
674 stateChanged();
675 }
676
677 protected void setIsCollapsed(boolean val) {
678 isCollapsed = val;
679 Main.pref.put(preferencePrefix+".minimized", val);
680 stateChanged();
681 }
682
683 protected void setIsButtonHiding(ButtonHiddingType val) {
684 buttonHiding = val;
685 PROP_BUTTON_HIDING.put(val);
686 if (buttonsHide != null) {
687 buttonsHide.setIcon(ImageProvider.get("misc", val != ButtonHiddingType.ALWAYS_SHOWN ? "buttonhide" : "buttonshow"));
688 }
689 if (buttonsPanel != null) {
690 buttonsPanel.setVisible(val != ButtonHiddingType.ALWAYS_HIDDEN);
691 }
692 stateChanged();
693 }
694
695 public int getPreferredHeight() {
696 return preferredHeight;
697 }
698
699 public String helpTopic() {
700 String help = getClass().getName();
701 help = help.substring(help.lastIndexOf('.')+1, help.length()-6);
702 return "Dialog/"+help;
703 }
704
705 @Override
706 public String toString() {
707 return name;
708 }
709
710 /**
711 * Replies true if this dialog is showing either as docked or as detached dialog
712 */
713 public boolean isDialogShowing() {
714 return isShowing;
715 }
716
717 /**
718 * Replies true if this dialog is docked and expanded
719 */
720 public boolean isDialogInDefaultView() {
721 return isShowing && isDocked && (! isCollapsed);
722 }
723
724 /**
725 * Replies true if this dialog is docked and collapsed
726 */
727 public boolean isDialogInCollapsedView() {
728 return isShowing && isDocked && isCollapsed;
729 }
730
731 public void setButton(JToggleButton button) {
732 this.button = button;
733 }
734
735 public JToggleButton getButton() {
736 return button;
737 }
738
739 /***
740 * The following methods are intended to be overridden, in order to customize
741 * the toggle dialog behavior.
742 **/
743
744 /**
745 * Change the Geometry of the detached dialog to better fit the content.
746 */
747 protected Rectangle getDetachedGeometry(Rectangle last) {
748 return last;
749 }
750
751 /**
752 * Default size of the detached dialog.
753 * Override this method to customize the initial dialog size.
754 */
755 protected Dimension getDefaultDetachedSize() {
756 return new Dimension(dialogsPanel.getWidth(), preferredHeight);
757 }
758
759 /**
760 * Do something when the toggleButton is pressed.
761 */
762 protected void toggleButtonHook() {
763 }
764
765 protected boolean dockWhenClosingDetachedDlg() {
766 return true;
767 }
768
769 /**
770 * primitive stateChangedListener for subclasses
771 */
772 protected void stateChanged() {
773 }
774
775 protected Component createLayout(Component data, boolean scroll, Collection<SideButton> buttons) {
776 return createLayout(data, scroll, buttons, (Collection<SideButton>[]) null);
777 }
778
779 protected Component createLayout(Component data, boolean scroll, Collection<SideButton> firstButtons, Collection<SideButton>... nextButtons) {
780 if (scroll) {
781 data = new JScrollPane(data);
782 }
783 LinkedList<Collection<SideButton>> buttons = new LinkedList<Collection<SideButton>>();
784 buttons.addFirst(firstButtons);
785 if (nextButtons != null) {
786 buttons.addAll(Arrays.asList(nextButtons));
787 }
788 add(data, BorderLayout.CENTER);
789 if (buttons.size() > 0 && buttons.get(0) != null && !buttons.get(0).isEmpty()) {
790 buttonsPanel = new JPanel(new GridLayout(buttons.size(), 1));
791 for (Collection<SideButton> buttonRow : buttons) {
792 if (buttonRow == null) {
793 continue;
794 }
795 final JPanel buttonRowPanel = new JPanel(Main.pref.getBoolean("dialog.align.left", false)
796 ? new FlowLayout(FlowLayout.LEFT) : new GridLayout(1, buttonRow.size()));
797 buttonsPanel.add(buttonRowPanel);
798 for (SideButton button : buttonRow) {
799 buttonRowPanel.add(button);
800 javax.swing.Action action = button.getAction();
801 if (action != null) {
802 buttonActions.add(action);
803 } else {
804 System.err.println("Button " + button + " doesn't have action defined");
805 new Exception().printStackTrace();
806 }
807 }
808 }
809 add(buttonsPanel, BorderLayout.SOUTH);
810 if (Main.pref.getBoolean("dialog.dynamic.buttons", true)) {
811 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_MOTION_EVENT_MASK);
812 buttonsPanel.setVisible(buttonHiding == ButtonHiddingType.ALWAYS_SHOWN || !isDocked);
813 }
814 } else if (buttonsHide != null) {
815 buttonsHide.setVisible(false);
816 }
817 return data;
818 }
819
820 @Override
821 public void eventDispatched(AWTEvent event) {
822 if(isShowing() && !isCollapsed && isDocked && buttonHiding == ButtonHiddingType.DYNAMIC) {
823 Rectangle b = this.getBounds();
824 b.setLocation(getLocationOnScreen());
825 if (b.contains(((MouseEvent)event).getLocationOnScreen())) {
826 if(!buttonsPanel.isVisible()) {
827 buttonsPanel.setVisible(true);
828 }
829 } else if (buttonsPanel.isVisible()) {
830 buttonsPanel.setVisible(false);
831 }
832 }
833 }
834 }