/*
* org.openmicroscopy.shoola.util.ui.clsf.TreeCheck
*
*------------------------------------------------------------------------------
* Copyright (C) 2006 University of Dundee. All rights reserved.
*
*
* This program 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 2 of the License, or
* (at your option) any later version.
* This program 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.util.ui.clsf;
//Java imports
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JRadioButton;
import javax.swing.JTree;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
//Third-party libraries
//Application-internal dependencies
/**
* Displays an hierarchical data. Each node added to the structure
* is an instance of {@link TreeCheckNode} i.e. a selection box to select the
* node is added to the node if the parameter <code>leafOnly</code> is set to
* <code>false</code>. If the parameter <code>leafOnly</code> is set to
* <code>true</code>, only the leaf nodes can be selected.
*
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @version 2.2
* <small>
* (<b>Internal version:</b> $Revision$ $Date$)
* </small>
* @since OME2.2
*/
public class TreeCheck
extends JTree
{
/** Bounds property to indicate a node is selected or deselected. */
public static final String NODE_SELECTED_PROPERTY = "nodeSelected";
/**
* Flag to indicate that only one child can be selected in a given parent.
* If <code>true</code> only one child can be selected at a time.
* If <code>false</code> multiple children can be selected.
*/
private boolean singleSelectionInParent;
/** Fires a property change with the numbers of nodes selected. */
private void fireNodeSelection()
{
DefaultTreeModel dtm = (DefaultTreeModel) getModel();
TreeCheckNode root = (TreeCheckNode) dtm.getRoot();
Enumeration nodes = root.breadthFirstEnumeration();
TreeCheckNode node;
int index = 0;
//Determine the number of selected nodes.
while (nodes.hasMoreElements()) {
node = (TreeCheckNode) nodes.nextElement();
if (node.isSelected()) index++;
}
firePropertyChange(NODE_SELECTED_PROPERTY, Integer.valueOf(-1),
Integer.valueOf(index));
}
/**
* Selects the specified node and deselects the other siblings.
*
* @param node The node to select.
*/
private void handleSingleSelection(TreeCheckNode node)
{
TreeCheckNode parent = node.getParentDisplay();
if (parent == null) return;
if (node.isSelected()) return; //node already selected.
Set nodes = parent.getChildrenDisplay();
Iterator i = nodes.iterator();
TreeCheckNode child;
while (i.hasNext()) {
child = (TreeCheckNode) i.next();
child.setSelected(child.equals(node));
}
fireNodeSelection();
}
/**
* Initializes the tree i.e. set the model, the selection model, etc.
*
* @param root The root node of the tree.
* @param leafOnly Passed <code>true</code> to allow leaves selection only
* <code>false</code> otherwise.
*/
private void initialize(TreeCheckNode root, boolean leafOnly)
{
setCellRenderer(new TreeCheckRenderer(leafOnly));
getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION);
putClientProperty("JTree.lineStyle", "Angled");
setShowsRootHandles(true);
super.setModel(new TreeCheckModel(root));
addMouseListener(new MouseAdapter() {
/** Handles the node selection. */
public void mouseClicked(MouseEvent me) {
int row = getRowForLocation(me.getX(), me.getY());
TreePath path = getPathForRow(row);
if (path == null) return;
Object o = path.getLastPathComponent();
if (!(o instanceof TreeCheckNode)) return;
TreeCheckNode node = (TreeCheckNode) o;
if (singleSelectionInParent) handleSingleSelection(node);
else {
node.setSelected(!node.isSelected());
fireNodeSelection();
}
((DefaultTreeModel) getModel()).nodeChanged(node);
if (row == 0) {
revalidate();
repaint();
}
}
});
}
/**
* Creates a new instance.
*
* @param rootObject The object hosted by the root node.
* @param rootIcon The icon of the root node.
*/
public TreeCheck(Object rootObject, Icon rootIcon)
{
initialize(new TreeCheckNode(rootObject, rootIcon), true);
}
/**
* Creates a new instance.
*
* @param root The root node.
*/
public TreeCheck(TreeCheckNode root)
{
initialize(root, true);
}
/**
* Creates a new instance.
*
* @param rootObject The object hosted by the root node.
* @param rootIcon The icon of the root node.
* @param leafOnly Passed <code>true</code> to allow leaves selection
* only, <code>false</code> otherwise.
*/
public TreeCheck(Object rootObject, Icon rootIcon, boolean leafOnly)
{
initialize(new TreeCheckNode(rootObject, rootIcon), leafOnly);
}
/**
* Sets the {@link #singleSelectionInParent} value.
*
* @param b The value to set.
*/
public void setSingleSelectionInParent(boolean b)
{
singleSelectionInParent = b;
TreeCheckRenderer rnd = (TreeCheckRenderer) getCellRenderer();
if (singleSelectionInParent) rnd.initToggleButton(JRadioButton.class);
else rnd.initToggleButton(JCheckBox.class);
}
/**
* Returns <code>true</code> if for a given parent, only one child
* can be selected at a time, <code>false</code> otherwise.
*
* @return See above.
*/
public boolean isSingleSelectionInParent()
{
return singleSelectionInParent;
}
/**
* Returns a collection of selected {@link TreeCheckNode}s.
*
* @return See above.
*/
public Set getSelectedNodes()
{
HashSet set = new HashSet();
DefaultTreeModel dtm = (DefaultTreeModel) getModel();
TreeCheckNode root = (TreeCheckNode) dtm.getRoot();
Enumeration nodes = root.breadthFirstEnumeration();
TreeCheckNode node;
while (nodes.hasMoreElements()) {
node = (TreeCheckNode) nodes.nextElement();
if (node.isSelected()) set.add(node);
}
return Collections.unmodifiableSet(set);
}
/** Selects all nodes. */
public void selectAllNodes()
{
DefaultTreeModel dtm = (DefaultTreeModel) getModel();
TreeCheckNode root = (TreeCheckNode) dtm.getRoot();
Enumeration nodes = root.breadthFirstEnumeration();
TreeCheckNode node;
while (nodes.hasMoreElements()) {
node = (TreeCheckNode) nodes.nextElement();
node.setSelected(true);
}
fireNodeSelection();
repaint();
}
/** Deselects all the nodes. */
public void deselectAllNodes()
{
DefaultTreeModel dtm = (DefaultTreeModel) getModel();
TreeCheckNode root = (TreeCheckNode) dtm.getRoot();
Enumeration nodes = root.breadthFirstEnumeration();
TreeCheckNode node;
while (nodes.hasMoreElements()) {
node = (TreeCheckNode) nodes.nextElement();
node.setSelected(false);
}
fireNodeSelection();
repaint();
}
/**
* Overridden to make sure that the root node is a {@link TreeCheckNode}.
* @see JTree#setModel(TreeModel)
*/
public void setModel(TreeModel newModel) {}
}