/* * Copyright 2007-2010 Enrico Boldrini, Lorenzo Bigagli This file is part of * CheckboxTree. CheckboxTree 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. CheckboxTree 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 CheckboxTree; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA */ package it.cnr.imaa.essi.lablib.gui.checkboxtree; import java.awt.Rectangle; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; import javax.swing.JFrame; import javax.swing.JTree; import javax.swing.WindowConstants; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; /* * questa รจ la classe del francese (ticket #5 ). Non mi funziona. Da togliere * appena possibile. */ /** * A tree whose nodes may be checked (e.g. the widget usually found in software * installers, that allows to select which features to install/uninstall). If a * node has some child of different checking status is greyed. You can use the * same constructors of JTree to instantiate a new CheckboxTree Example from a * TreeNode: * *
* DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
* root.add(new DefaultMutableTreeNode("child A"));
* root.add(new DefaultMutableTreeNode("child B"));
* CheckboxTree CheckboxTree = new CheckboxTree(root);
*
*
* Example from a TreeModel:
*
* * DefaultTreeModel dtm = new DefaultTreeModel(root); * * CheckboxTree CheckboxTree = new CheckboxTree(root); ** * Default constructor (useful for gui builders): * *
* CheckboxTree CheckboxTree = new CheckboxTree(); ** * Then you can set the checking propagation style: * *
* CheckboxTree.getCheckingModel().setCheckingMode(TreeCheckingModel.CheckingMode.SIMPLE); * CheckboxTree.getCheckingModel().setCheckingMode(TreeCheckingModel.CheckingMode.PROPAGATE); * CheckboxTree.getCheckingModel().setCheckingMode(TreeCheckingModel.CheckingMode.PROPAGATE_PRESERVING_CHECK); * CheckboxTree.getCheckingModel().setCheckingMode(TreeCheckingModel.CheckingMode.PROPAGATE_PRESERVING_UNCHECK); ** * You can also set the model at a later time using: * *
* CheckboxTree.setModel(aTreeModel); ** * There are two methods that return the paths that are in the checking: * *
* TreePath[] tp = CheckboxTree.getCheckingPaths(); * * TreePath[] tp = CheckboxTree.getCheckingRoots(); ** * You can also add/remove a listener of a TreeCheckingEvent in this way: * *
* CheckboxTree.addTreeCheckingListener(new TreeCheckingListener() {
* public void valueChanged(TreeCheckingEvent e) {
* System.out.println("Checked paths changed: user clicked on " + (e.getLeadingPath().getLastPathComponent()));
* }
* });
*
*
* @author Enrico Boldrini
* @author Lorenzo Bigagli
*/
public class CheckboxTreeOlivier extends JTree {
private class NodeCheckListener extends MouseAdapter {
@Override
public void mousePressed(MouseEvent e) {
if (!isEnabled()) {
return;
}
// we use mousePressed instead of mouseClicked for performance
int x = e.getX();
int y = e.getY();
int row = getRowForLocation(x, y);
if (row == -1) {
// click outside any node
return;
}
Rectangle rect = getRowBounds(row);
if (rect == null) {
// clic on an invalid node
return;
}
if (((CheckboxTreeCellRenderer) getCellRenderer()).isOnHotspot(x - rect.x, y - rect.y)) {
// NEW
TreePath clickedPath = getPathForRow(row);
getCheckingModel().toggleCheckingPath(clickedPath);
// if the clicked node was selected with another ones, set (or
// unset) all of them
TreePath[] selectionPaths = getSelectionPaths();
if (selectionPaths != null && Arrays.asList(selectionPaths).contains(clickedPath)) {
if (getCheckingModel().isPathChecked(getPathForRow(row))) {
getCheckingModel().addCheckingPaths(selectionPaths);
} else {
getCheckingModel().removeCheckingPaths(selectionPaths);
}
}
}
}
};
/*
* Temporary solution for enabling spacebar checking. Should make use of
* InputMaps?
*/
private class SpaceListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
if (!isEnabled()) {
return;
}
TreePath path = CheckboxTreeOlivier.this.getSelectionPath();
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
if (path != null) {
TreeCheckingModel cm = CheckboxTreeOlivier.this.getCheckingModel();
cm.toggleCheckingPath(path);
}
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
private TreeCheckingModel checkingModel;
/**
* For GUI builders. It returns a CheckboxTree with a default tree model to
* show something interesting. Creates a CheckboxTree with visible handles,
* a default CheckboxTreeCellRenderer and a default TreeCheckingModel.
*/
public CheckboxTreeOlivier() {
super(getDefaultTreeModel());
initialize();
}
/**
* Creates a CheckboxTree with visible handles, a default
* CheckboxTreeCellRenderer and a default TreeCheckingModel. The tree is
* based on the specified tree model.
*/
public CheckboxTreeOlivier(TreeModel treemodel) {
super(treemodel);
initialize();
}
/**
* Creates a CheckboxTree with visible handles, a default
* CheckboxTreeCellRenderer and a default TreeCheckingModel. The tree root
* is the specified tree node.
*
* @param root the root of the tree
*/
public CheckboxTreeOlivier(TreeNode root) {
super(root);
initialize();
}
/**
* Add a path in the checking.
*/
public void addCheckingPath(TreePath path) {
getCheckingModel().addCheckingPath(path);
}
/**
* Add paths in the checking.
*/
public void addCheckingPaths(TreePath[] paths) {
getCheckingModel().addCheckingPaths(paths);
}
/**
* Adds a listener for TreeChecking events.
*
* @param tsl the TreeCheckingListener that will be notified
* when a node is checked
*/
public void addTreeCheckingListener(TreeCheckingListener tsl) {
this.checkingModel.addTreeCheckingListener(tsl);
}
/**
* Clears the checking.
*/
public void clearChecking() {
getCheckingModel().clearChecking();
}
/**
* Expand completely a tree
*/
public void expandAll() {
expandSubTree(getPathForRow(0));
}
private void expandSubTree(TreePath path) {
expandPath(path);
Object node = path.getLastPathComponent();
int childrenNumber = getModel().getChildCount(node);
TreePath[] childrenPath = new TreePath[childrenNumber];
for (int childIndex = 0; childIndex < childrenNumber; childIndex++) {
childrenPath[childIndex] = path.pathByAddingChild(getModel().getChild(node, childIndex));
expandSubTree(childrenPath[childIndex]);
}
}
/**
* @return Returns the TreeCheckingModel.
*/
public TreeCheckingModel getCheckingModel() {
return this.checkingModel;
}
/**
* Return paths that are in the checking.
*/
public TreePath[] getCheckingPaths() {
return getCheckingModel().getCheckingPaths();
}
/**
* @return Returns the paths that are in the checking set and are the
* (upper) roots of checked trees.
*/
public TreePath[] getCheckingRoots() {
return getCheckingModel().getCheckingRoots();
}
/**
* @return Returns the paths that are in the greying.
*/
public TreePath[] getGreyingPaths() {
return getCheckingModel().getGreyingPaths();
}
/**
* Convenience initialization method. NEW
*/
private void initialize() {
setCheckingModel(new DefaultTreeCheckingModel(this.treeModel));
setCellRenderer(new DefaultCheckboxTreeCellRenderer());
MouseListener[] listener = getMouseListeners();
for (MouseListener mouseListener : listener)
removeMouseListener(mouseListener);
addMouseListener(new NodeCheckListener());
for (MouseListener mouseListener : listener)
addMouseListener(mouseListener);
addKeyListener(new SpaceListener());
this.selectionModel.setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
setShowsRootHandles(true);
putClientProperty("JTree.lineStyle", "Angled");// for Metal L&F
}
/**
* Returns true if the item identified by the path is currently checked.
*
* @param path a TreePath identifying a node
* @return true if the node is checked
*/
public boolean isPathChecked(TreePath path) {
return getCheckingModel().isPathChecked(path);
}
/**
* Remove a path from the checking.
*/
public void removeCheckingPath(TreePath path) {
getCheckingModel().removeCheckingPath(path);
}
/**
* Remove paths from the checking.
*/
public void removeCheckingPaths(TreePath[] paths) {
getCheckingModel().removeCheckingPaths(paths);
}
/**
* Removes a TreeChecking listener.
*
* @param tsl the TreeChckingListener to remove
*/
public void removeTreeCheckingListener(TreeCheckingListener tsl) {
this.checkingModel.removeTreeCheckingListener(tsl);
}
/**
* Sets the CheckboxTreeCellRenderer that will be used to draw
* each cell.
*
* @param x the TreeCellRenderer that is to render each cell
*/
public void setCellRenderer(CheckboxTreeCellRenderer x) {
super.setCellRenderer(x);
}
/**
* Set the checking model of this CheckboxTree.
*
* @param newCheckingModel The new TreeCheckingModel.
*/
public void setCheckingModel(TreeCheckingModel newCheckingModel) {
/*
* in case we are dealing with DefaultTreeCheckingModel, we link/unlink
* it from the model of this tree
*/
TreeCheckingModel oldCheckingModel = this.checkingModel;
if (oldCheckingModel != null && oldCheckingModel instanceof DefaultTreeCheckingModel) {
// null the model to avoid dangling pointers
((DefaultTreeCheckingModel) oldCheckingModel).setTreeModel(null);
}
// TODO: what if newCheckingModel == null ?
this.checkingModel = newCheckingModel;
if (newCheckingModel != null) {
if (newCheckingModel instanceof DefaultTreeCheckingModel) {
((DefaultTreeCheckingModel) newCheckingModel).setTreeModel(getModel());
}
// add a treeCheckingListener to repaint upon checking modifications
newCheckingModel.addTreeCheckingListener(new TreeCheckingListener() {
public void valueChanged(TreeCheckingEvent e) {
repaint();
}
});
}
}
/**
* Set path in the checking.
*/
public void setCheckingPath(TreePath path) {
getCheckingModel().setCheckingPath(path);
}
/**
* Set paths that are in the checking.
*/
public void setCheckingPaths(TreePath[] paths) {
getCheckingModel().setCheckingPaths(paths);
}
/**
* Sets the TreeModel and links it to the existing checkingModel.
*/
@Override
public void setModel(TreeModel newModel) {
super.setModel(newModel);
if (checkingModel != null && checkingModel instanceof DefaultTreeCheckingModel) {
((DefaultTreeCheckingModel) checkingModel).setTreeModel(newModel);
}
}
/**
* @return a string representation of the tree, including the checking,
* enabling and greying sets.
*/
@Override
public String toString() {
String retVal = super.toString();
TreeCheckingModel tcm = getCheckingModel();
if (tcm != null) {
return retVal + "\n" + tcm.toString();
}
return retVal;
}
/**
* Test function. NEW
*
* @param args
*/
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
DefaultMutableTreeNode root = new DefaultMutableTreeNode("root");
Random rand = new Random();
java.util.List