/* * Copyright (c) 2011, grossmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the jo-widgets.org nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH * DAMAGE. */ package org.jowidgets.spi.impl.swing.common.widgets; import java.awt.Component; import java.awt.Point; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.swing.BorderFactory; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.jowidgets.common.color.IColorConstant; import org.jowidgets.common.types.Position; import org.jowidgets.common.types.SelectionPolicy; import org.jowidgets.logging.api.ILogger; import org.jowidgets.logging.api.LoggerProvider; import org.jowidgets.spi.impl.controller.TreeSelectionObservableSpi; import org.jowidgets.spi.impl.swing.common.dnd.IDropSelectionProvider; import org.jowidgets.spi.impl.swing.common.options.SwingOptions; import org.jowidgets.spi.impl.swing.common.util.ColorConvert; import org.jowidgets.spi.impl.swing.common.util.PositionConvert; import org.jowidgets.spi.impl.swing.common.widgets.base.JoTreeNode; import org.jowidgets.spi.impl.swing.common.widgets.base.JoTreeNodeRenderer; import org.jowidgets.spi.widgets.ITreeNodeSpi; import org.jowidgets.spi.widgets.ITreeSpi; import org.jowidgets.spi.widgets.controller.ITreeSelectionListenerSpi; import org.jowidgets.spi.widgets.setup.ITreeSetupSpi; import org.jowidgets.util.Assert; import org.jowidgets.util.Tuple; public class TreeImpl extends SwingControl implements ITreeSpi, IDropSelectionProvider { private static final ILogger LOGGER = LoggerProvider.get(TreeImpl.class); private final Map<JoTreeNode, TreeNodeImpl> nodes; private final TreeSelectionObservableSpi treeObservable; private final JTree tree; private final DefaultMutableTreeNode mutableRootNode; private final DefaultTreeModel treeModel; private final ITreeNodeSpi rootNode; private List<JoTreeNode> lastSelection; public TreeImpl(final ITreeSetupSpi setup) { super(createComponent(setup)); if (setup.isChecked()) { LOGGER.warn("Checked Tree is not jet implemented for swing"); } this.nodes = new HashMap<JoTreeNode, TreeNodeImpl>(); this.treeObservable = new TreeSelectionObservableSpi(); this.lastSelection = new LinkedList<JoTreeNode>(); if (getUiReference() instanceof JScrollPane) { this.tree = (JTree) ((JScrollPane) getUiReference()).getViewport().getView(); } else { this.tree = (JTree) getUiReference(); } this.treeModel = (DefaultTreeModel) tree.getModel(); this.mutableRootNode = (DefaultMutableTreeNode) treeModel.getRoot(); tree.setCellRenderer(new JoTreeNodeRenderer()); tree.addTreeExpansionListener(new TreeExpansionListener() { @Override public void treeExpanded(final TreeExpansionEvent event) { final TreePath path = event.getPath(); if (path.getLastPathComponent() != mutableRootNode) { final JoTreeNode node = (JoTreeNode) path.getLastPathComponent(); final TreeNodeImpl treeNodeImpl = nodes.get(node); if (treeNodeImpl != null) { treeNodeImpl.fireExpandedChanged(true); } } } @Override public void treeCollapsed(final TreeExpansionEvent event) { final TreePath path = event.getPath(); if (path.getLastPathComponent() != mutableRootNode) { final JoTreeNode node = (JoTreeNode) path.getLastPathComponent(); final TreeNodeImpl treeNodeImpl = nodes.get(node); if (treeNodeImpl != null) { treeNodeImpl.fireExpandedChanged(false); } } } }); tree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(final TreeSelectionEvent e) { fireSelectionChange(); } }); setMouseListener(new MouseAdapter() {}); tree.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(final MouseEvent e) { trigger(e); } @Override public void mousePressed(final MouseEvent e) { trigger(e); } private void trigger(final MouseEvent e) { if (e.isPopupTrigger()) { final TreePath path = tree.getPathForLocation(e.getX(), e.getY()); //This is necessary when tree is inside a viewport final Point popupPosition = new Point(e.getLocationOnScreen()); SwingUtilities.convertPointFromScreen(popupPosition, getUiReference()); final Position position = PositionConvert.convert(popupPosition); if (path != null) { final JoTreeNode node = (JoTreeNode) path.getLastPathComponent(); if (!e.isShiftDown() && !e.isControlDown()) { tree.setSelectionPath(new TreePath(node.getPath())); } nodes.get(node).firePopupDetected(position); } else { getPopupDetectionObservable().firePopupDetected(position); } } } }); if (!SwingOptions.isDefaultTreeTransferHandler()) { tree.setTransferHandler(null); } this.rootNode = new TreeNodeImpl(this, mutableRootNode); } @Override public ITreeNodeSpi getRootNode() { return rootNode; } @Override public Object getDropSelection(final Object dropLocation) { if (dropLocation instanceof JTree.DropLocation) { final JTree.DropLocation treeDropLocation = (JTree.DropLocation) dropLocation; final Object lastNode = treeDropLocation.getPath().getLastPathComponent(); if (lastNode instanceof JoTreeNode) { return nodes.get(lastNode); } } return null; } @Override public ITreeNodeSpi getNodeAt(final Position position) { Assert.paramNotNull(position, "position"); final TreePath treePath = tree.getPathForLocation(position.getX(), position.getY()); if (treePath != null) { final Object lastNode = treePath.getLastPathComponent(); if (lastNode instanceof JoTreeNode) { return nodes.get(lastNode); } } return null; } @Override public List<ITreeNodeSpi> getSelectedNodes() { final List<ITreeNodeSpi> result = new LinkedList<ITreeNodeSpi>(); final TreePath[] selectionPaths = tree.getSelectionPaths(); if (selectionPaths != null) { for (final TreePath selectionPath : selectionPaths) { final Object selectedNode = selectionPath.getLastPathComponent(); if (selectedNode instanceof JoTreeNode) { result.add(nodes.get(selectedNode)); } } } return result; } @Override public void setForegroundColor(final IColorConstant colorValue) { tree.setForeground(ColorConvert.convert(colorValue)); super.setForegroundColor(colorValue); } @Override public void setBackgroundColor(final IColorConstant colorValue) { tree.setBackground(ColorConvert.convert(colorValue)); super.setBackgroundColor(colorValue); } @Override public IColorConstant getForegroundColor() { return ColorConvert.convert(tree.getForeground()); } @Override public IColorConstant getBackgroundColor() { return ColorConvert.convert(tree.getBackground()); } @Override public void addTreeSelectionListener(final ITreeSelectionListenerSpi listener) { treeObservable.addTreeSelectionListener(listener); } @Override public void removeTreeSelectionListener(final ITreeSelectionListenerSpi listener) { treeObservable.removeTreeSelectionListener(listener); } protected void registerNode(final JoTreeNode joTreeNode, final TreeNodeImpl nodeImpl) { nodes.put(joTreeNode, nodeImpl); } protected void unRegisterNode(final JoTreeNode joTreeNode) { for (int i = 0; i < joTreeNode.getChildCount(); i++) { unRegisterNode((JoTreeNode) joTreeNode.getChildAt(i)); } nodes.remove(joTreeNode); lastSelection.remove(joTreeNode); } protected DefaultMutableTreeNode getMutableRootNode() { return mutableRootNode; } protected JTree getTree() { return tree; } protected DefaultTreeModel getTreeModel() { return treeModel; } private void fireSelectionChange() { final List<JoTreeNode> newSelection = new LinkedList<JoTreeNode>(); final TreePath[] selectionPaths = tree.getSelectionPaths(); if (selectionPaths != null) { for (final TreePath selectionPath : selectionPaths) { final Object selectedNode = selectionPath.getLastPathComponent(); if (selectedNode instanceof JoTreeNode) { newSelection.add((JoTreeNode) selectedNode); } } } for (final JoTreeNode wasSelected : lastSelection) { if (!newSelection.contains(wasSelected)) { final TreeNodeImpl treeNode = nodes.get(wasSelected); if (treeNode != null) { treeNode.fireSelectionChanged(false); } } } for (final JoTreeNode isSelected : newSelection) { if (!lastSelection.contains(isSelected)) { final TreeNodeImpl treeNode = nodes.get(isSelected); if (treeNode != null) { treeNode.fireSelectionChanged(true); } } } lastSelection = newSelection; treeObservable.fireSelectionChanged(); } private static Tuple<Component, Component> createComponent(final ITreeSetupSpi setup) { final DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(); final DefaultTreeModel treeModel = new DefaultTreeModel(rootNode); final JTree tree = new JTree(rootNode); tree.setModel(treeModel); rootNode.setAllowsChildren(true); tree.setShowsRootHandles(true); tree.setDoubleBuffered(true); tree.setRootVisible(false); tree.setBorder(BorderFactory.createEmptyBorder()); ToolTipManager.sharedInstance().registerComponent(tree); if (SelectionPolicy.MULTI_SELECTION == setup.getSelectionPolicy()) { tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); } else if (SelectionPolicy.SINGLE_SELECTION == setup.getSelectionPolicy()) { tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); } else { throw new IllegalArgumentException("SelectionPolicy '" + setup.getSelectionPolicy() + "' is not known"); } if (setup.isContentScrolled()) { final JScrollPane result = new JScrollPane(tree); result.setBorder(BorderFactory.createEmptyBorder()); result.setViewportBorder(BorderFactory.createEmptyBorder()); return new Tuple<Component, Component>(result, tree); } else { return new Tuple<Component, Component>(tree, tree); } } }