/* * Copyright (c) 2010, SQL Power Group Inc. * * This file is part of SQL Power Library. * * SQL Power Library is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * SQL Power Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ca.sqlpower.swingui; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.FocusListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JTree; import javax.swing.Popup; import javax.swing.SwingUtilities; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; /** * This {@link Action} creates a {@link Popup} with a {@link JTree} embedded * inside of it. */ public class PopupJTreeAction extends AbstractAction { /** * The {@link JPanel} the generated {@link Popup} should appear on. */ private final JPanel panel; /** * The {@link JTree} that should be embedded inside the generated * {@link Popup}. */ private final JTree tree; /** * The {@link JButton} that performs this {@link Action} to produce the * {@link Popup}. */ private final JButton button; /** * The {@link List} of valid {@link Class} types that a selected * {@link TreeNode} can be. */ private final List<Class<?>> validSelectionClasses; /** * The {@link PopupListenerHandler} */ private PopupListenerHandler popupListenerHandler; /** * This {@link TreeSelectionListener} determines if a tree selection is * valid. If it is valid, the popup is cleaned up. */ private final TreeSelectionListener treeListener = new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { TreePath path = e.getNewLeadSelectionPath(); if (path != null) { Object node = path.getLastPathComponent(); for (Class<?> c : validSelectionClasses) { if (c.isAssignableFrom(node.getClass())) { popupCleanup(); break; } } } } }; /** * Creates a new {@link PopupJTreeAction} given the {@link JPanel} the * created {@link Popup} should appear on, the {@link JTree} the * {@link Popup} should embed, and the {@link JButton} that performs this * action. * * @param panel * The {@link JPanel} this {@link Popup} should appear on. * @param tree * The {@link JTree} the {@link Popup} should embed. * @param button * The {@link JButton} that should perform this action. * @param validSelectionClass * The only one valid tree node type that can be selected. If * this is null, then no selections are allowed. */ public PopupJTreeAction(JPanel panel, JTree tree, JButton button, Class<?> validSelectionClass) { this(panel, tree, button, (validSelectionClass == null)? Collections.<Class<?>>emptyList() : Collections.<Class<?>>singletonList(validSelectionClass)); } /** * Creates a new {@link PopupJTreeAction} given the {@link JPanel} the * created {@link Popup} should appear on, the {@link JTree} the * {@link Popup} should embed, and the {@link JButton} that performs this * action. * * @param panel * The {@link JPanel} this {@link Popup} should appear on. * @param tree * The {@link JTree} the {@link Popup} should embed. * @param button * The {@link JButton} that should perform this action. * @param validSelectionClasses * The {@link List} of {@link Class}es that are valid tree * selections. A unmodifiable copy of this {@link List} is made * to use when validating the selection. If this is an empty * {@link List}, no selections are allowed. */ public PopupJTreeAction(JPanel panel, JTree tree, JButton button, List<Class<?>> validSelectionClasses) { super(); this.panel = panel; this.tree = tree; this.button = button; this.validSelectionClasses = Collections.unmodifiableList(new ArrayList<Class<?>>(validSelectionClasses)); } /** * Creates a {@link Popup} if it is not visible, and connects the * appropriate listeners to check for when to hide the {@link Popup}. * Otherwise, it hides the {@link Popup} and disconnects the listeners. */ public void actionPerformed(ActionEvent e) { if (popupListenerHandler != null && popupListenerHandler.isPopupVisible()) { popupCleanup(); } else { Point windowLocation = new Point(0, 0); SwingUtilities.convertPointToScreen(windowLocation, button); windowLocation.y += button.getHeight(); // Popup the JTree and attach the popup listener handler to the tree popupListenerHandler = SPSUtils.popupComponent(panel, tree, windowLocation); popupConnect(); } } /** * Displays the {@link Popup} and attaches a {@link FocusListener} and * {@link TreeSelectionListener} on the tree that is embedded. */ private void popupConnect() { if (popupListenerHandler != null) { popupListenerHandler.connect(); } if (tree != null) { tree.addTreeSelectionListener(treeListener); } } /** * Hides the {@link Popup} and removes all of the * {@link TreeSelectionListener}s and {@link FocusListener}s on all of the * related components that were hooked up using {@link #popupConnect()}. */ private void popupCleanup() { if (popupListenerHandler != null && popupListenerHandler.isPopupVisible()) { popupListenerHandler.cleanup(); } if (tree != null) { tree.removeTreeSelectionListener(treeListener); } } }