I want to create a tree with given nodes and children. The required behavior is as follows:
"when a node's checkbox is toggled, it's new value (checked/unchecked) should be reflected to all of it's descendants"
Following is the full code, which is not giving expected behavior:
public class CheckBoxTree {
Map<String, DefaultMutableTreeNode> nodes = new HashMap<String, DefaultMutableTreeNode>();
public static void main(final String args[]) {
final CheckBoxTree cbt = new CheckBoxTree();
DefaultMutableTreeNode root = cbt.addNodeWithoutCheckbox(null, "Select divisions");
DefaultMutableTreeNode nodeSTD1 = cbt.addNodeWithCheckbox(root, "STD1", false);
cbt.nodes.put("STD1", nodeSTD1);
DefaultMutableTreeNode nodeDiv1 = cbt.addNodeWithCheckbox(nodeSTD1, "DIV1", false);
cbt.nodes.put("DIV1", nodeDiv1);
DefaultMutableTreeNode nodeDiv2 = cbt.addNodeWithCheckbox(nodeSTD1, "DIV2", false);
cbt.nodes.put("DIV2", nodeDiv2);
root.add(nodeSTD1);
DefaultMutableTreeNode nodeSTD2 = cbt.addNodeWithCheckbox(root, "STD2", false);
cbt.nodes.put("STD2", nodeSTD2);
DefaultMutableTreeNode nodeDiv3 = cbt.addNodeWithCheckbox(nodeSTD2, "DIV3", false);
cbt.nodes.put("DIV3", nodeDiv3);
DefaultMutableTreeNode nodeDiv4 = cbt.addNodeWithCheckbox(nodeSTD2, "DIV4", false);
cbt.nodes.put("DIV4", nodeDiv4);
root.add(nodeSTD2);
final JTree tree = cbt.createCheckBoxTree(root);
// show the tree onscreen
final JFrame frame = new JFrame("CheckBox Tree");
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(tree, BorderLayout.CENTER);
JButton button = new JButton("Submit");
panel.add(button, BorderLayout.SOUTH);
final JScrollPane scrollPane = new JScrollPane(panel);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 150);
frame.setVisible(true);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
/*TreePath[] selectionPaths = tree.getSelectionModel().getSelectionPaths();
// TreePath[] selectionPaths = tree.getSelectionPaths();
if (selectionPaths != null) {
System.out.println("Selected paths:");
for (TreePath tp : selectionPaths) {
System.out.println(tp);
}
}*/
System.out.println("Selected paths:");
for (DefaultMutableTreeNode n : cbt.nodes.values()) {
Object obj = n.getUserObject();
if (obj instanceof CheckBoxNode) {
if (((CheckBoxNode) obj).isSelected()) {
System.out.println(n.toString());
}
}
}
}
});
}
private JTree createCheckBoxTree(final DefaultMutableTreeNode root) {
final DefaultTreeModel treeModel = new DefaultTreeModel(root);
final JTree tree = new JTree(treeModel);
final CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
tree.setCellRenderer(renderer);
final CheckBoxNodeEditor editor = new CheckBoxNodeEditor(tree);
tree.setCellEditor(editor);
tree.setEditable(true);
// listen for changes in the model (including check box toggles)
treeModel.addTreeModelListener(new TreeModelListener() {
#Override
public void treeNodesChanged(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes changed");
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null) {
return;
}
changeSubTreeSelections(node, null);
}
#Override
public void treeNodesInserted(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes inserted");
}
#Override
public void treeNodesRemoved(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes removed");
}
#Override
public void treeStructureChanged(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": structure changed");
}
});
// listen for changes in the selection
tree.addTreeSelectionListener(new TreeSelectionListener() {
#Override
public void valueChanged(TreeSelectionEvent arg0) {
System.out.println(System.currentTimeMillis() + ": value changed");
/*DefaultMutableTreeNode node =
(DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null) {
return;
}
changeSubTreeSelections(node, null);*/
}
});
return tree;
}
protected void changeSubTreeSelections(DefaultMutableTreeNode node, Boolean selected) {
/*change all subtree selection if applicable*/
Object obj = node.getUserObject();
Boolean isSelected = selected;
if (obj instanceof CheckBoxNode) {
CheckBoxNode cbn = (CheckBoxNode) obj;
if (isSelected == null) {
isSelected = cbn.isSelected();
} else {
cbn.setSelected(isSelected);
}
} else {
return;
}
int count = node.getChildCount();
for (int i = 0; i < count; i++) {
DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
changeSubTreeSelections(child, isSelected);
}
}
public DefaultMutableTreeNode addNodeWithCheckbox(
final DefaultMutableTreeNode parent, final String text, final boolean checked) {
final CheckBoxNode data = new CheckBoxNode(text, checked);
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(data);
if (parent != null) {
parent.add(node);
}
return node;
}
public DefaultMutableTreeNode addNodeWithoutCheckbox(
final DefaultMutableTreeNode parent, final String text) {
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(text);
if (parent != null) {
parent.add(node);
}
return node;
}
/*private static DefaultMutableTreeNode add(
final DefaultMutableTreeNode parent, final String text, final boolean checked) {
final CheckBoxNode data = new CheckBoxNode(text, checked);
final DefaultMutableTreeNode node = new DefaultMutableTreeNode(data);
parent.add(node);
nodes.put(text, node);
return node;
}*/
}
class CheckBoxNodeRenderer implements TreeCellRenderer {
private JCheckBox checkBoxNodeRenderer = new JCheckBox();
private DefaultTreeCellRenderer nonCheckBoxNodeRenderer = new DefaultTreeCellRenderer();
private Color selectionBorderColor, selectionForegroundColor, selectionBackgroundColor,
textForegroundColor, textBackgroundColor;
protected JCheckBox getLeafCheckBox() {
return checkBoxNodeRenderer;
}
public CheckBoxNodeRenderer() {
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
checkBoxNodeRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
checkBoxNodeRenderer.setFocusPainted((booleanValue != null)
&& (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForegroundColor = UIManager.getColor("Tree.selectionForeground");
selectionBackgroundColor = UIManager.getColor("Tree.selectionBackground");
textForegroundColor = UIManager.getColor("Tree.textForeground");
textBackgroundColor = UIManager.getColor("Tree.textBackground");
}
#Override
public Component getTreeCellRendererComponent(
JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
Component returnValue;
/*check if it is checkbox object*/
boolean isCheckboxObject = false;
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof CheckBoxNode) {
isCheckboxObject = true;
CheckBoxNode node = (CheckBoxNode) userObject;
checkBoxNodeRenderer.setText(node.getText());
checkBoxNodeRenderer.setSelected(node.isSelected());
}
} else {
String stringValue =
tree.convertValueToText(value, selected, expanded, leaf, row, false);
checkBoxNodeRenderer.setText(stringValue);
checkBoxNodeRenderer.setSelected(false);
}
if (isCheckboxObject) {
checkBoxNodeRenderer.setEnabled(tree.isEnabled());
if (selected) {
checkBoxNodeRenderer.setForeground(selectionForegroundColor);
checkBoxNodeRenderer.setBackground(selectionBackgroundColor);
} else {
checkBoxNodeRenderer.setForeground(textForegroundColor);
checkBoxNodeRenderer.setBackground(textBackgroundColor);
}
returnValue = checkBoxNodeRenderer;
} else {
returnValue =
nonCheckBoxNodeRenderer.getTreeCellRendererComponent(tree, value, selected,
expanded, leaf, row, hasFocus);
}
return returnValue;
}
}
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
private static final long serialVersionUID = 1L;
private CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
private ChangeEvent changeEvent = null;
private JTree tree;
public CheckBoxNodeEditor(JTree tree) {
this.tree = tree;
}
#Override
public Object getCellEditorValue() {
JCheckBox checkbox = renderer.getLeafCheckBox();
CheckBoxNode checkBoxNode = new CheckBoxNode(checkbox.getText(), checkbox.isSelected());
return checkBoxNode;
}
#Override
public boolean isCellEditable(EventObject event) {
/*uncomment following code to make all nodes editable*/
// return true;
// make all nodes, which are instance of CheckBoxNode, editable
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
returnValue = (userObject instanceof CheckBoxNode);
// returnValue = ((treeNode.isLeaf()) && (userObject
// instanceof CheckBoxNode));
}
}
}
return returnValue;
}
#Override
public Component getTreeCellEditorComponent(
JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
Component editor =
renderer.getTreeCellRendererComponent(tree, value, false, expanded, leaf, row, true);
// editor always selected / focused
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
}
};
if (editor instanceof JCheckBox) {
((JCheckBox) editor).addItemListener(itemListener);
}
return editor;
}
}
class CheckBoxNode {
private String text;
private boolean selected;
public CheckBoxNode(String text, boolean selected) {
this.text = text;
this.selected = selected;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean newValue) {
selected = newValue;
}
public String getText() {
return text;
}
public void setText(String newValue) {
text = newValue;
}
/*#Override
public String toString() {
return getClass().getName() + "[" + text + "/" + selected + "]";
}*/
#Override
public String toString() {
return text;
}
}
class NamedVector extends Vector<Object> {
private static final long serialVersionUID = 1L;
private String name;
public NamedVector(String name) {
this.name = name;
}
public NamedVector(String name, Object elements[]) {
this.name = name;
for (int i = 0, n = elements.length; i < n; i++) {
add(elements[i]);
}
}
#Override
public String toString() {
return "[" + name + "]";
}
}
The toggling of any parent nodes' checkbox not reflecting correctly in children nodes.
Please help me to understand where the problem is.
add treeDidChange to repaint the tree.
#Override
public void treeNodesChanged(final TreeModelEvent e) {
System.out.println(System.currentTimeMillis() + ": nodes changed");
DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent();
if (node == null) {
return;
}
changeSubTreeSelections(node, null);
tree.treeDidChange();
}
What's more, I found that in the function changeSubTreeSelections, this line isSelected = !(cbn.isSelected()); is incorrect. You shouldn't use ! to get an opposite value.
And maybe it's better that you expand the whole descendants' path, if the parent node is selected.
Related
I'm trying to build a JTree with TestObjectCheckBoxNode leafs where each leaf holds a TestObject, everything appears to work except when I check the box in the leaf it is changing the node from a TestObjectCheckBoxNode to the CheckBoxNode superclass. I know this is happening in the TreeCellEditor implementation, specifically CheckBoxNodeEditor:getCellEditorValue(), because it is creating the updated TreeCellRenderer from the UI of that node.
CheckBoxNode checkBoxNode =
new CheckBoxNode(checkBoxPanel.label.getText(),
checkBoxPanel.checkBox.isSelected());
return checkBoxNode;
I'm at a complete loss for how would I do this in a way where I have access to the TestObject for the selected node in CheckBoxNodeEditor, so I could do something like this:
TestObjectCheckBoxNode testObjectCheckBoxNode =
new TestObjectCheckBoxNode(testObject,
checkBoxPanel.checkBox.isSelected());
return testObjectCheckBoxNode;
Here is the complete code below:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.font.TextAttribute;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventObject;
import java.util.Vector;
import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
public class JCheckBoxTreeTest
{
private TestObjectCheckBoxTree tree;
public static void main(String... s)
{
new JCheckBoxTreeTest();
}
public JCheckBoxTreeTest()
{
JFrame frame = new JFrame("JCheckBoxTreeTest Tree");
Vector rootVector = new Category("Root", new Object[]
{
new Category("POI",
new TestObjectCheckBoxNode[] {
new TestObjectCheckBoxNode(new TestObject("TestObject 1"),true),
new TestObjectCheckBoxNode(new TestObject("TestObject 2"),true),
}),
});
tree = new TestObjectCheckBoxTree(rootVector);
tree.addTreeSelectionListener(new TreeSelectionListener()
{
public void valueChanged(TreeSelectionEvent e)
{
DefaultMutableTreeNode node = (DefaultMutableTreeNode)tree.getLastSelectedPathComponent();
if (node == null) return;
Object userObject = ((DefaultMutableTreeNode) node).getUserObject();
System.err.println("node: " + node.toString());
System.err.println("userObject: " + userObject.toString());
}
});
tree.expandAll();
JScrollPane scrollPane = new JScrollPane(tree);
frame.getContentPane().add(scrollPane, BorderLayout.NORTH);
frame.setSize(360, 600);
frame.setVisible(true);
}
class TestObject
{
String name;
public TestObject(String inStr)
{
name = inStr;
}
}
public class CheckBoxNode
{
String text;
boolean selected;
public CheckBoxNode(String text, boolean selected)
{
this.text = text;
this.selected = selected;
}
public boolean isSelected() { return selected; }
public void setSelected(boolean newValue) { selected = newValue; }
public String getText() { return text; }
public void setText(String newValue) { text = newValue; }
public String toString() { return getClass().getName() + "[" + text + "/" + selected + "]"; }
}
public class TestObjectCheckBoxNode extends CheckBoxNode
{
TestObject testObject;
public TestObjectCheckBoxNode(TestObject testObject, boolean selected)
{
super(testObject.name, selected);
this.testObject = testObject;
}
}
public class CheckBoxTree extends JTree
{
public CheckBoxTree(Vector rootVector)
{
super(rootVector);
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
setCellRenderer(renderer);
setCellEditor(new CheckBoxNodeEditor());
setEditable(true);
}
public void expandAll()
{
expandAll(this, new TreePath(((DefaultMutableTreeNode)this.treeModel.getRoot()).getPath()), true);
}
private void expandAll(JTree tree, TreePath path, boolean expand) {
TreeNode node = (TreeNode) path.getLastPathComponent();
if (node.getChildCount() >= 0) {
Enumeration<? extends TreeNode> enumeration = node.children();
while (enumeration.hasMoreElements()) {
TreeNode treeNode = enumeration.nextElement();
TreePath treePath = path.pathByAddingChild(treeNode);
expandAll(tree, treePath, expand);
}
}
if (expand) {
tree.expandPath(path);
} else {
tree.collapsePath(path);
}
}
}
class CheckBoxPanel extends JPanel
{
public JCheckBox checkBox;
public JLabel label;
public CheckBoxPanel()
{
super();
checkBox = new JCheckBox();
label = new JLabel();
checkBox.setBorder(new EmptyBorder(0, 0, 0, 0));
add(checkBox);
add(label);
}
}
class CheckBoxNodeRenderer implements TreeCellRenderer
{
private CheckBoxPanel leafRenderer = new CheckBoxPanel();
private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();
Color selectionBorderColor, selectionForeground, selectionBackground,
textForeground, textBackground;
protected CheckBoxPanel getLeafRenderer() {
return leafRenderer;
}
public CheckBoxNodeRenderer() {
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null)
{
leafRenderer.checkBox.setFont(fontValue);
leafRenderer.label.setFont(fontValue);
//set the nonLeaf text to bold
nonLeafRenderer.setFont(fontValue.deriveFont(Collections.singletonMap(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD)));
}
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
leafRenderer.checkBox.setFocusPainted((booleanValue != null) && (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
Component returnValue;
if (leaf)
{
String stringValue = tree.convertValueToText(value, selected,
expanded, leaf, row, false);
leafRenderer.checkBox.setSelected(false);
leafRenderer.label.setText(stringValue);
leafRenderer.setEnabled(tree.isEnabled());
if (selected) {
leafRenderer.setForeground(selectionForeground);
leafRenderer.setBackground(selectionBackground);
} else {
leafRenderer.setForeground(textForeground);
leafRenderer.setBackground(textBackground);
}
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value)
.getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode) userObject;
leafRenderer.checkBox.setSelected(node.isSelected());
leafRenderer.label.setText(node.getText());
}
}
returnValue = leafRenderer;
}
else
{
returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
}
return returnValue;
}
}
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
TestObject testObject;
ChangeEvent changeEvent = null;
public Object getCellEditorValue()
{
CheckBoxPanel checkBoxPanel = renderer.getLeafRenderer();
if (testObject != null)
{
return new TestObjectCheckBoxNode(testObject, checkBoxPanel.checkBox.isSelected());
}
else
{
return new CheckBoxNode(checkBoxPanel.label.getText(), checkBoxPanel.checkBox.isSelected());
}
}
public boolean isCellEditable(EventObject event) {
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
JTree tree = (JTree)event.getSource();
TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
returnValue = ((treeNode.isLeaf()) && (userObject instanceof CheckBoxNode));
}
}
}
return returnValue;
}
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row) {
Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
if (userObject instanceof TestObjectCheckBoxNode)
{
testObject = ((TestObjectCheckBoxNode)userObject).testObject;
}
else
{
testObject = null;
}
Component editor = renderer.getTreeCellRendererComponent(tree, value,
true, expanded, leaf, row, true);
// editor always selected / focused
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
}
};
if (editor instanceof CheckBoxPanel)
{
((CheckBoxPanel) editor).checkBox.addItemListener(itemListener);
}
return editor;
}
}
class Category extends Vector
{
String name;
public Category(String name)
{
this.name = name;
}
public Category(String name, Object elements[])
{
this.name = name;
for (int i = 0, n = elements.length; i < n; i++)
{
add(elements[i]);
}
}
public String toString()
{
return "[" + name + "]";
}
}
class TestObjectCheckBoxTree extends CheckBoxTree
{
public TestObjectCheckBoxTree(Vector rootVector)
{
super(rootVector);
}
}
}
When editing the tree node Swing calls CheckBoxNodeEditor.getTreeCellEditorComponent() with the current node as the value parameter. Later when you stop editing the node it calls CheckBoxNodeEditor.getCellEditorValue().
What you need to do: if the value in getTreeCellEditorComponent() is a TestObjectCheckBoxNode then store its testObject field into an instance field of the CheckBoxNodeEditor.
Later when getCellEditorValue() is called you can either return a TestObjectCheckBoxNode if the testObject was stored, otherwise return a ChechBoxNode.
The code could look like this (shortened) example:
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
TestObject testObject;
public Object getCellEditorValue() {
CheckBoxPanel checkBoxPanel = renderer.getLeafRenderer();
if (testObject != null) {
return new TestObjectCheckBoxNode(testObject, checkBoxPanel.checkBox.isSelected());
} else {
return new CheckBoxNode(checkBoxPanel.label.getText(), checkBoxPanel.checkBox.isSelected());
}
}
public Component getTreeCellEditorComponent(
JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row
) {
Object realValue = ((DefaultMutableTreeNode) value).getUserObject();
if (realValue instanceof TestObjectCheckBoxNode n) {
testObject = n.testObject;
} else {
testObject = null;
}
// remaining code from your getTreeCellEditorComponent method
}
// other code removed for brevity
}
If you can't use the new pattern matching instanceof operator (because you don't use Java 16 or later) the if statement at the beginning of getTreeCellEditorComponent would need to be written as
public Component getTreeCellEditorComponent(
JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row
) {
Object realValue = ((DefaultMutableTreeNode) value).getUserObject();
if (realValue instanceof TestObjectCheckBoxNode) {
TestObjectCheckBoxNode n = (TestObjectCheckBoxNode) value;
testObject = n.testObject;
} else {
testObject = null;
}
// remaining code from your getTreeCellEditorComponent method
}
Why is the value in getTreeCellEditorComponent a DefaultMutableTreeNode?
The explanation is that JTree exclusively works with TreeNode instances. If you create a JTree with a Vector, a Hashtable or an Object[] it wraps the values that these contain in JTree.DynamicUtilTreeNode instances and sets your objects as userObject property on them.
The JTree.DynamicUtilTreeNode extends from DefaultMutableTreeNode.
I'm exploring How to do the implementation of the Copy & Paste of JTree.
Because, I want Copy from DefaultMutableTreeNode like toString() to paste in another application like Sublime Text.
I was viewing the code in order to view how copy & paste is implemented and how is used drag and drop in JTree.
My first thougth was, Copy and Paste between DefaultMutableTreeNode of JTree must be tested, later how paste from Clipboard to another application, but my code is not working and Don't know why is failing, and I need to solve.
NOTE: Sorry my code is a bit long, because if I put only a snippet code will be non functional. For me all code here is needed to do tests.
Here my code:
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.ActionEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.tree.*;
public class JTreeTransferHandler extends JFrame {
public JTreeTransferHandler() {
initiate();
}
private void initiate() {
setTitle("Copy from JTree");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
setLocation(200,200);
setVisible(true);
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode vegetableNode = new DefaultMutableTreeNode("Vegetables");
vegetableNode.add(new DefaultMutableTreeNode("Capsicum"));
vegetableNode.add(new DefaultMutableTreeNode("Carrot"));
DefaultMutableTreeNode fruitNode = new DefaultMutableTreeNode("Fruits");
fruitNode.add(new DefaultMutableTreeNode("Mango"));
fruitNode.add(new DefaultMutableTreeNode("Apple"));
root.add(vegetableNode);
root.add(fruitNode);
JTree tree = new JTree(root);
TreeTransferHandler treeTransferHandler = new TreeTransferHandler();
tree.setTransferHandler(treeTransferHandler);
Clipboard clipboard = this.getToolkit().getSystemClipboard();
JButton copy = new JButton("Copy");
copy.addActionListener((ActionEvent e) -> {
TransferHandler handler = tree.getTransferHandler();
handler.exportToClipboard(tree, clipboard, TransferHandler.COPY);
});
JButton paste = new JButton("Paste");
paste.addActionListener((ActionEvent e) -> {
Transferable clipData = clipboard.getContents(clipboard);
if (clipData != null) {
if (clipData.isDataFlavorSupported(treeTransferHandler.nodesFlavor)) {
TransferHandler handler = tree.getTransferHandler();
handler.importData(tree, clipData);
}
}
});
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(copy);
buttonsPanel.add(paste);
add(new JScrollPane(tree), BorderLayout.CENTER);
add(buttonsPanel, BorderLayout.SOUTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JTreeTransferHandler jTreeTransferHandler = new JTreeTransferHandler();
});
}
}
class TreeTransferHandler extends TransferHandler {
DataFlavor nodesFlavor;
DataFlavor[] flavors = new DataFlavor[1];
DefaultMutableTreeNode[] nodesToRemove;
public TreeTransferHandler() {
try {
String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" +
DefaultMutableTreeNode[].class.getName() + "\"";
nodesFlavor = new DataFlavor(mimeType);
flavors[0] = nodesFlavor;
} catch(ClassNotFoundException e) {
System.out.println("ClassNotFound: " + e.getMessage());
}
}
//TransferHandler
#Override public int getSourceActions(JComponent c) {
return TransferHandler.COPY;
}
//TransferHandler
#Override public boolean canImport(JComponent comp, DataFlavor flavor[]) {
for (int i = 0, n = flavor.length; i < n; i++) {
for (int j = 0, m = flavors.length; j < m; j++) {
if (flavor[i].equals(flavors[j])) {
return true;
}
}
}
return false;
}
//TransferHandler
#Override protected Transferable createTransferable(JComponent c) {
JTree tree = (JTree) c;
TreePath[] paths = tree.getSelectionPaths();
if (paths != null) {
List<DefaultMutableTreeNode> copies = new ArrayList<>();
List<DefaultMutableTreeNode> toRemove = new ArrayList<>();
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) paths[0].getLastPathComponent();
DefaultMutableTreeNode copy = copy(node);
copies.add(copy);
toRemove.add(node);
for (int i = 1; i < paths.length; i++) {
DefaultMutableTreeNode next =
(DefaultMutableTreeNode) paths[i].getLastPathComponent();
// Do not allow higher level nodes to be added to list.
if (next.getLevel() < node.getLevel()) {
break;
} else if (next.getLevel() > node.getLevel()) { // child node
copy.add(copy(next));
// node already contains child
} else { // sibling
copies.add(copy(next));
toRemove.add(next);
}
}
DefaultMutableTreeNode[] nodes =
copies.toArray(new DefaultMutableTreeNode[copies.size()]);
nodesToRemove =
toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
return new NodesTransferable(nodes);
}
return null;
}
/** Defensive copy used in createTransferable. */
private DefaultMutableTreeNode copy(TreeNode node) {
return new DefaultMutableTreeNode(node);
}
//TransferHandler
#Override public boolean importData(TransferHandler.TransferSupport support) {
if (!canImport(support)) {
return false;
}
// Extract transfer data.
DefaultMutableTreeNode[] nodes = null;
try {
Transferable t = support.getTransferable();
nodes = (DefaultMutableTreeNode[]) t.getTransferData(nodesFlavor);
} catch (UnsupportedFlavorException ufe) {
System.out.println("UnsupportedFlavor: " + ufe.getMessage());
} catch (java.io.IOException ioe) {
System.out.println("I/O error: " + ioe.getMessage());
}
// Get drop location info.
JTree.DropLocation dl
= (JTree.DropLocation) support.getDropLocation();
int childIndex = dl.getChildIndex();
TreePath dest = dl.getPath();
DefaultMutableTreeNode parent
= (DefaultMutableTreeNode) dest.getLastPathComponent();
JTree tree = (JTree) support.getComponent();
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
// Configure for drop mode.
int index = childIndex; // DropMode.INSERT
if (childIndex == -1) { // DropMode.ON
index = parent.getChildCount();
}
// Add data to model.
for (int i = 0; i < nodes.length; i++) {
model.insertNodeInto(nodes[i], parent, index++);
}
return true;
}
//TransferHandler
public boolean importData(JComponent comp, Transferable t) {
// Extract transfer data.
DefaultMutableTreeNode[] nodes;
try {
nodes = (DefaultMutableTreeNode[]) t.getTransferData(nodesFlavor);
if (comp instanceof JTree) {
JTree tree = (JTree)comp;
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
JTree.DropLocation dl = tree.getDropLocation();
int childIndex = dl.getChildIndex();
TreePath dest = dl.getPath();
DefaultMutableTreeNode parent =
(DefaultMutableTreeNode)dest.getLastPathComponent();
// Configure for drop mode.
int index = childIndex; // DropMode.INSERT
if (childIndex == -1) { // DropMode.ON
index = parent.getChildCount();
}
// Add data to model.
for (DefaultMutableTreeNode node : nodes) {
model.insertNodeInto(node, parent, index++);
}
return true;
}
} catch (UnsupportedFlavorException ufe) {
System.out.println("UnsupportedFlavor: " + ufe.getMessage());
} catch (java.io.IOException ioe) {
System.out.println("I/O error: " + ioe.getMessage());
}
return false;
}
public class NodesTransferable implements Transferable {
DefaultMutableTreeNode[] nodes;
public NodesTransferable(DefaultMutableTreeNode[] nodes) {
this.nodes = nodes;
}
//Transferable
#Override public Object getTransferData(DataFlavor flavor) {
if(!isDataFlavorSupported(flavor)) {
return false;
}
return nodes;
}
//Transferable
#Override public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
//Transferable
#Override public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(nodesFlavor);
}
}
}
TransferHandler.TransferSupport#isDrop() method returns false in the case of via Clipboard, so switching it here seems to work fine.
// Get drop location info.
int childIndex;
TreePath dest;
if (support.isDrop()) {
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
childIndex = dl.getChildIndex();
dest = dl.getPath();
} else {
childIndex = -1;
JTree tree = (JTree) support.getComponent();
dest = tree.getSelectionPath();
}
JTreeTransferHandler2.java
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.ActionEvent;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.tree.*;
public class JTreeTransferHandler2 extends JFrame {
public JTreeTransferHandler2() {
initiate();
}
private void initiate() {
setTitle("Copy from JTree");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
setLocation(200,200);
setVisible(true);
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode vegetableNode = new DefaultMutableTreeNode("Vegetables");
vegetableNode.add(new DefaultMutableTreeNode("Capsicum"));
vegetableNode.add(new DefaultMutableTreeNode("Carrot"));
DefaultMutableTreeNode fruitNode = new DefaultMutableTreeNode("Fruits");
fruitNode.add(new DefaultMutableTreeNode("Mango"));
fruitNode.add(new DefaultMutableTreeNode("Apple"));
root.add(vegetableNode);
root.add(fruitNode);
JTree tree = new JTree(root);
TreeTransferHandler treeTransferHandler = new TreeTransferHandler();
tree.setTransferHandler(treeTransferHandler);
Clipboard clipboard = this.getToolkit().getSystemClipboard();
JButton copy = new JButton("Copy");
copy.addActionListener((ActionEvent e) -> {
TransferHandler handler = tree.getTransferHandler();
handler.exportToClipboard(tree, clipboard, TransferHandler.COPY);
});
JButton paste = new JButton("Paste");
paste.addActionListener((ActionEvent e) -> {
Transferable clipData = clipboard.getContents(null);
if (clipData != null) {
if (clipData.isDataFlavorSupported(treeTransferHandler.nodesFlavor)) {
TransferHandler handler = tree.getTransferHandler();
handler.importData(tree, clipData);
}
}
});
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(copy);
buttonsPanel.add(paste);
add(new JScrollPane(tree), BorderLayout.CENTER);
add(buttonsPanel, BorderLayout.SOUTH);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JTreeTransferHandler2 jTreeTransferHandler = new JTreeTransferHandler2();
});
}
}
class TreeTransferHandler extends TransferHandler {
DataFlavor nodesFlavor;
DataFlavor[] flavors = new DataFlavor[1];
DefaultMutableTreeNode[] nodesToRemove;
public TreeTransferHandler() {
try {
String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=\"" +
DefaultMutableTreeNode[].class.getName() + "\"";
nodesFlavor = new DataFlavor(mimeType);
flavors[0] = nodesFlavor;
} catch(ClassNotFoundException e) {
System.out.println("ClassNotFound: " + e.getMessage());
}
}
//TransferHandler
#Override public int getSourceActions(JComponent c) {
return TransferHandler.COPY;
}
//TransferHandler
#Override public boolean canImport(JComponent comp, DataFlavor flavor[]) {
for (int i = 0, n = flavor.length; i < n; i++) {
for (int j = 0, m = flavors.length; j < m; j++) {
if (flavor[i].equals(flavors[j])) {
return true;
}
}
}
return false;
}
//TransferHandler
#Override protected Transferable createTransferable(JComponent c) {
JTree tree = (JTree) c;
TreePath[] paths = tree.getSelectionPaths();
if (paths != null) {
List<DefaultMutableTreeNode> copies = new ArrayList<>();
List<DefaultMutableTreeNode> toRemove = new ArrayList<>();
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) paths[0].getLastPathComponent();
DefaultMutableTreeNode copy = copy(node);
copies.add(copy);
toRemove.add(node);
for (int i = 1; i < paths.length; i++) {
DefaultMutableTreeNode next =
(DefaultMutableTreeNode) paths[i].getLastPathComponent();
// Do not allow higher level nodes to be added to list.
if (next.getLevel() < node.getLevel()) {
break;
} else if (next.getLevel() > node.getLevel()) { // child node
copy.add(copy(next));
// node already contains child
} else { // sibling
copies.add(copy(next));
toRemove.add(next);
}
}
DefaultMutableTreeNode[] nodes =
copies.toArray(new DefaultMutableTreeNode[copies.size()]);
nodesToRemove =
toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
return new NodesTransferable(nodes);
}
return null;
}
/** Defensive copy used in createTransferable. */
private DefaultMutableTreeNode copy(TreeNode node) {
return new DefaultMutableTreeNode(node);
}
//TransferHandler
#Override public boolean importData(TransferHandler.TransferSupport support) {
if (!canImport(support)) {
return false;
}
// Extract transfer data.
DefaultMutableTreeNode[] nodes = null;
try {
Transferable t = support.getTransferable();
nodes = (DefaultMutableTreeNode[]) t.getTransferData(nodesFlavor);
} catch (UnsupportedFlavorException ufe) {
System.out.println("UnsupportedFlavor: " + ufe.getMessage());
} catch (java.io.IOException ioe) {
System.out.println("I/O error: " + ioe.getMessage());
}
// Get drop location info.
int childIndex;
TreePath dest;
if (support.isDrop()) {
JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
childIndex = dl.getChildIndex();
dest = dl.getPath();
} else {
childIndex = -1;
JTree tree = (JTree) support.getComponent();
dest = tree.getSelectionPath();
}
DefaultMutableTreeNode parent
= (DefaultMutableTreeNode) dest.getLastPathComponent();
JTree tree = (JTree) support.getComponent();
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
// Configure for drop mode.
int index = childIndex; // DropMode.INSERT
if (childIndex == -1) { // DropMode.ON
index = parent.getChildCount();
}
// Add data to model.
for (int i = 0; i < nodes.length; i++) {
// ArrayIndexOutOfBoundsException
model.insertNodeInto(nodes[i], parent, index++);
}
return true;
}
//TransferHandler
#Override public boolean importData(JComponent comp, Transferable t) {
return importData(new TransferHandler.TransferSupport(comp, t));
}
public class NodesTransferable implements Transferable {
DefaultMutableTreeNode[] nodes;
public NodesTransferable(DefaultMutableTreeNode[] nodes) {
this.nodes = nodes;
}
//Transferable
#Override public Object getTransferData(DataFlavor flavor) {
if(!isDataFlavorSupported(flavor)) {
return false;
}
return nodes;
}
//Transferable
#Override public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
//Transferable
#Override public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(nodesFlavor);
}
}
}
I have a customized JTree that shows file and folders of a directory, currently it only pick the desktop directory. I could not fine any line to modify it. also fileSystemView only has getters and not setter.
Here is my working code with default 'desktop' parent directory.
public final class MainPanel extends JPanel {
private MainPanel() {
super(new BorderLayout());
final FileSystemView fileSystemView = FileSystemView.getFileSystemView();
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
final DefaultTreeModel treeModel = new DefaultTreeModel(root);
for (File fileSystemRoot: fileSystemView.getRoots()) {
DefaultMutableTreeNode node = new DefaultMutableTreeNode(new CheckBoxNode(fileSystemRoot, Status.DESELECTED));
root.add(node);
for (File file: fileSystemView.getFiles(fileSystemRoot, true)) {
if (file.isDirectory()) {
node.add(new DefaultMutableTreeNode(new CheckBoxNode(file, Status.DESELECTED)));
}
}
}
treeModel.addTreeModelListener(new CheckBoxStatusUpdateListener());
final JTree tree = new JTree(treeModel) {
#Override public void updateUI() {
setCellRenderer(null);
setCellEditor(null);
super.updateUI();
//???#1: JDK 1.6.0 bug??? Nimbus LnF
setCellRenderer(new FileTreeCellRenderer(fileSystemView));
setCellEditor(new CheckBoxNodeEditor(fileSystemView));
}
};
tree.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
tree.setRootVisible(false);
tree.addTreeSelectionListener(new FolderSelectionListener(fileSystemView));
//tree.setCellRenderer(new FileTreeCellRenderer(fileSystemView));
//tree.setCellEditor(new CheckBoxNodeEditor(fileSystemView));
tree.setEditable(true);
tree.expandRow(0);
//tree.setToggleClickCount(1);
setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
add(new JScrollPane(tree));
add(new JButton(new AbstractAction("test") {
#Override public void actionPerformed(ActionEvent ae) {
System.out.println("------------------");
searchTreeForCheckedNode(tree.getPathForRow(0));
// DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();
// Enumeration e = root.breadthFirstEnumeration();
// while (e.hasMoreElements()) {
// DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
// CheckBoxNode check = (CheckBoxNode) node.getUserObject();
// if (check != null && check.status == Status.SELECTED) {
// System.out.println(check.file.toString());
// }
// }
}
}), BorderLayout.SOUTH);
setPreferredSize(new Dimension(320, 240));
}
private static void searchTreeForCheckedNode(TreePath path) {
Object o = path.getLastPathComponent();
if (!(o instanceof DefaultMutableTreeNode)) {
return;
}
DefaultMutableTreeNode node = (DefaultMutableTreeNode) o;
o = node.getUserObject();
if (!(o instanceof CheckBoxNode)) {
return;
}
CheckBoxNode check = (CheckBoxNode) o;
if (check.status == Status.SELECTED) {
System.out.println(check.file.toString());
} else if (check.status == Status.INDETERMINATE && !node.isLeaf() && node.getChildCount() >= 0) {
Enumeration e = node.children();
while (e.hasMoreElements()) {
searchTreeForCheckedNode(path.pathByAddingChild(e.nextElement()));
}
}
}
public static void main(String... args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("FileSystemTreeWithCheckBox");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
//frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new MainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class TriStateCheckBox extends JCheckBox {
private Icon currentIcon;
#Override public void updateUI() {
currentIcon = getIcon();
setIcon(null);
super.updateUI();
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
if (currentIcon != null) {
setIcon(new IndeterminateIcon());
}
setOpaque(false);
}
});
}
}
class IndeterminateIcon implements Icon {
private static final Color FOREGROUND = new Color(50, 20, 255, 200); //TEST: UIManager.getColor("CheckBox.foreground");
private static final int SIDE_MARGIN = 4;
private static final int HEIGHT = 2;
private final Icon icon = UIManager.getIcon("CheckBox.icon");
#Override public void paintIcon(Component c, Graphics g, int x, int y) {
icon.paintIcon(c, g, x, y);
int w = getIconWidth();
int h = getIconHeight();
Graphics2D g2 = (Graphics2D) g.create();
g2.setPaint(FOREGROUND);
g2.translate(x, y);
g2.fillRect(SIDE_MARGIN, (h - HEIGHT) / 2, w - SIDE_MARGIN - SIDE_MARGIN, HEIGHT);
//g2.translate(-x, -y);
g2.dispose();
}
#Override public int getIconWidth() {
return icon.getIconWidth();
}
#Override public int getIconHeight() {
return icon.getIconHeight();
}
}
enum Status { SELECTED, DESELECTED, INDETERMINATE }
class CheckBoxNode {
public final File file;
public final Status status;
public CheckBoxNode(File file) {
this.file = file;
status = Status.INDETERMINATE;
}
public CheckBoxNode(File file, Status status) {
this.file = file;
this.status = status;
}
#Override public String toString() {
return file.getName();
}
}
class FolderSelectionListener implements TreeSelectionListener {
private final FileSystemView fileSystemView;
public FolderSelectionListener(FileSystemView fileSystemView) {
this.fileSystemView = fileSystemView;
}
#Override public void valueChanged(TreeSelectionEvent e) {
final JTree tree = (JTree) e.getSource();
final DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.getPath().getLastPathComponent();
if (!node.isLeaf()) {
return;
}
CheckBoxNode check = (CheckBoxNode) node.getUserObject();
if (check == null) {
return;
}
final File parent = check.file;
if (!parent.isDirectory()) {
return;
}
final Status parentStatus = check.status == Status.SELECTED ? Status.SELECTED : Status.DESELECTED;
final DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
Task worker = new Task(fileSystemView, parent) {
#Override protected void process(List<File> chunks) {
if (!tree.isDisplayable()) {
System.out.println("process: DISPOSE_ON_CLOSE");
cancel(true);
return;
}
for (File file: chunks) {
model.insertNodeInto(new DefaultMutableTreeNode(new CheckBoxNode(file, parentStatus)), node, node.getChildCount());
//node.add(new DefaultMutableTreeNode(new CheckBoxNode(file, parentStatus)));
}
//model.reload(parent); //= model.nodeStructureChanged(parent);
}
};
worker.execute();
}
}
class Task extends SwingWorker<String, File> {
private final FileSystemView fileSystemView;
private final File parent;
public Task(FileSystemView fileSystemView, File parent) {
super();
this.fileSystemView = fileSystemView;
this.parent = parent;
}
#Override public String doInBackground() {
File[] children = fileSystemView.getFiles(parent, true);
for (File child: children) {
if (child.isDirectory()) {
publish(child);
}
}
return "done";
}
}
class FileTreeCellRenderer extends TriStateCheckBox implements TreeCellRenderer {
private final FileSystemView fileSystemView;
private final JPanel panel = new JPanel(new BorderLayout());
private final DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
public FileTreeCellRenderer(FileSystemView fileSystemView) {
super();
String uiName = getUI().getClass().getName();
if (uiName.contains("Synth") && System.getProperty("java.version").startsWith("1.7.0")) {
System.out.println("XXX: FocusBorder bug?, JDK 1.7.0, Nimbus start LnF");
renderer.setBackgroundSelectionColor(new Color(0, 0, 0, 0));
}
this.fileSystemView = fileSystemView;
panel.setFocusable(false);
panel.setRequestFocusEnabled(false);
panel.setOpaque(false);
panel.add(this, BorderLayout.WEST);
this.setOpaque(false);
}
#Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
JLabel l = (JLabel) renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
l.setFont(tree.getFont());
if (value instanceof DefaultMutableTreeNode) {
this.setEnabled(tree.isEnabled());
this.setFont(tree.getFont());
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode) userObject;
if (node.status == Status.INDETERMINATE) {
setIcon(new IndeterminateIcon());
} else {
setIcon(null);
}
setText("");
File file = (File) node.file;
l.setIcon(fileSystemView.getSystemIcon(file));
l.setText(fileSystemView.getSystemDisplayName(file));
l.setToolTipText(file.getPath());
setSelected(node.status == Status.SELECTED);
}
//panel.add(this, BorderLayout.WEST);
panel.add(l);
return panel;
}
return l;
}
#Override public void updateUI() {
super.updateUI();
if (panel != null) {
//panel.removeAll(); //??? Change to Nimbus LnF, JDK 1.6.0
panel.updateUI();
//panel.add(this, BorderLayout.WEST);
}
setName("Tree.cellRenderer");
//???#1: JDK 1.6.0 bug??? #see 1.7.0 DefaultTreeCellRenderer#updateUI()
//if (System.getProperty("java.version").startsWith("1.6.0")) {
// renderer = new DefaultTreeCellRenderer();
//}
}
}
class CheckBoxNodeEditor extends TriStateCheckBox implements TreeCellEditor {
private final FileSystemView fileSystemView;
private final JPanel panel = new JPanel(new BorderLayout());
private final DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
private File file;
public CheckBoxNodeEditor(FileSystemView fileSystemView) {
super();
this.fileSystemView = fileSystemView;
this.addActionListener(new ActionListener() {
#Override public void actionPerformed(ActionEvent e) {
stopCellEditing();
}
});
panel.setFocusable(false);
panel.setRequestFocusEnabled(false);
panel.setOpaque(false);
panel.add(this, BorderLayout.WEST);
this.setOpaque(false);
}
#Override public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
//JLabel l = (JLabel) renderer.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
JLabel l = (JLabel) renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
l.setFont(tree.getFont());
setOpaque(false);
if (value instanceof DefaultMutableTreeNode) {
this.setEnabled(tree.isEnabled());
this.setFont(tree.getFont());
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode) userObject;
if (node.status == Status.INDETERMINATE) {
setIcon(new IndeterminateIcon());
} else {
setIcon(null);
}
file = node.file;
l.setIcon(fileSystemView.getSystemIcon(file));
l.setText(fileSystemView.getSystemDisplayName(file));
setSelected(node.status == Status.SELECTED);
}
//panel.add(this, BorderLayout.WEST);
panel.add(l);
return panel;
}
return l;
}
#Override public Object getCellEditorValue() {
return new CheckBoxNode(file, isSelected() ? Status.SELECTED : Status.DESELECTED);
}
#Override public boolean isCellEditable(EventObject e) {
if (e instanceof MouseEvent && e.getSource() instanceof JTree) {
MouseEvent me = (MouseEvent) e;
JTree tree = (JTree) e.getSource();
TreePath path = tree.getPathForLocation(me.getX(), me.getY());
Rectangle r = tree.getPathBounds(path);
if (r == null) {
return false;
}
Dimension d = getPreferredSize();
r.setSize(new Dimension(d.width, r.height));
if (r.contains(me.getX(), me.getY())) {
if (file == null && System.getProperty("java.version").startsWith("1.7.0")) {
System.out.println("XXX: Java 7, only on first run\n" + getBounds());
setBounds(new Rectangle(0, 0, d.width, r.height));
}
//System.out.println(getBounds());
return true;
}
}
return false;
}
#Override public void updateUI() {
super.updateUI();
setName("Tree.cellEditor");
if (panel != null) {
//panel.removeAll(); //??? Change to Nimbus LnF, JDK 1.6.0
panel.updateUI();
//panel.add(this, BorderLayout.WEST);
}
//???#1: JDK 1.6.0 bug??? #see 1.7.0 DefaultTreeCellRenderer#updateUI()
//if (System.getProperty("java.version").startsWith("1.6.0")) {
// renderer = new DefaultTreeCellRenderer();
//}
}
//Copied from AbstractCellEditor
// protected EventListenerList listenerList = new EventListenerList();
// protected transient ChangeEvent changeEvent;
#Override public boolean shouldSelectCell(EventObject anEvent) {
return true;
}
#Override public boolean stopCellEditing() {
fireEditingStopped();
return true;
}
#Override public void cancelCellEditing() {
fireEditingCanceled();
}
#Override public void addCellEditorListener(CellEditorListener l) {
listenerList.add(CellEditorListener.class, l);
}
#Override public void removeCellEditorListener(CellEditorListener l) {
listenerList.remove(CellEditorListener.class, l);
}
public CellEditorListener[] getCellEditorListeners() {
return listenerList.getListeners(CellEditorListener.class);
}
protected void fireEditingStopped() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == CellEditorListener.class) {
// Lazily create the event:
if (changeEvent == null) {
changeEvent = new ChangeEvent(this);
}
((CellEditorListener) listeners[i + 1]).editingStopped(changeEvent);
}
}
}
protected void fireEditingCanceled() {
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == CellEditorListener.class) {
// Lazily create the event:
if (changeEvent == null) {
changeEvent = new ChangeEvent(this);
}
((CellEditorListener) listeners[i + 1]).editingCanceled(changeEvent);
}
}
}
}
class CheckBoxStatusUpdateListener implements TreeModelListener {
private boolean adjusting;
#Override public void treeNodesChanged(TreeModelEvent e) {
if (adjusting) {
return;
}
adjusting = true;
Object[] children = e.getChildren();
DefaultTreeModel model = (DefaultTreeModel) e.getSource();
DefaultMutableTreeNode node;
CheckBoxNode c; // = (CheckBoxNode) node.getUserObject();
if (children != null && children.length == 1) {
node = (DefaultMutableTreeNode) children[0];
c = (CheckBoxNode) node.getUserObject();
TreePath parent = e.getTreePath();
DefaultMutableTreeNode n = (DefaultMutableTreeNode) parent.getLastPathComponent();
while (n != null) {
updateParentUserObject(n);
DefaultMutableTreeNode tmp = (DefaultMutableTreeNode) n.getParent();
if (tmp == null) {
break;
} else {
n = tmp;
}
}
model.nodeChanged(n);
} else {
node = (DefaultMutableTreeNode) model.getRoot();
c = (CheckBoxNode) node.getUserObject();
}
updateAllChildrenUserObject(node, c.status);
model.nodeChanged(node);
adjusting = false;
}
private void updateParentUserObject(DefaultMutableTreeNode parent) {
Object userObject = parent.getUserObject();
if (userObject instanceof CheckBoxNode) {
File file = ((CheckBoxNode) userObject).file;
int selectedCount = 0;
int indeterminateCount = 0;
Enumeration children = parent.children();
while (children.hasMoreElements()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) children.nextElement();
CheckBoxNode check = (CheckBoxNode) node.getUserObject();
if (check.status == Status.INDETERMINATE) {
indeterminateCount++;
break;
}
if (check.status == Status.SELECTED) {
selectedCount++;
}
}
if (indeterminateCount > 0) {
parent.setUserObject(new CheckBoxNode(file));
} else if (selectedCount == 0) {
parent.setUserObject(new CheckBoxNode(file, Status.DESELECTED));
} else if (selectedCount == parent.getChildCount()) {
parent.setUserObject(new CheckBoxNode(file, Status.SELECTED));
} else {
parent.setUserObject(new CheckBoxNode(file));
}
}
}
private void updateAllChildrenUserObject(DefaultMutableTreeNode root, Status status) {
Enumeration breadth = root.breadthFirstEnumeration();
while (breadth.hasMoreElements()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) breadth.nextElement();
if (root.equals(node)) {
continue;
}
CheckBoxNode check = (CheckBoxNode) node.getUserObject();
node.setUserObject(new CheckBoxNode(check.file, status));
}
}
#Override public void treeNodesInserted(TreeModelEvent e) { /* not needed */ }
#Override public void treeNodesRemoved(TreeModelEvent e) { /* not needed */ }
#Override public void treeStructureChanged(TreeModelEvent e) { /* not needed */ }
}
Any idea to change its directory ?
Use a File instead of FileSystemView to define the root.
Use File#listFiles to list the files within a specified directory instead of FileSystemView#getFiles
Something like...
File rootPath = new File(".");
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
final DefaultTreeModel treeModel = new DefaultTreeModel(root);
DefaultMutableTreeNode node = new DefaultMutableTreeNode(new CheckBoxNode(rootPath, Status.DESELECTED));
root.add(node);
for (File file : rootPath.listFiles()) {
if (file.isDirectory()) {
node.add(new DefaultMutableTreeNode(new CheckBoxNode(file, Status.DESELECTED)));
}
}
for example.
Take a look at java.io.File for more details
I have two java class under same package 1. mrbpdf.java and FileTreeDemo.java, I want set the JTree functionalities from FileTreeDemo.java to mrbpdf.java JTree, since I am newbie! finding difficulties to do it. Please give me directions, thanks, if my question is unclear please comment it, will change it accordingly.
Basically I want show the root (for example c:/ ) file directory in mrbpdf.java;
mrbpdf.java
public class mrbpdf {
private JFrame frmViperManufacturingRecord;
private JTextField txtText; //declearing here for global variable
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
mrbpdf window = new mrbpdf();
window.frmViperManufacturingRecord.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public mrbpdf() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
public void initialize() {
frmViperManufacturingRecord = new JFrame();
frmViperManufacturingRecord.setBackground(Color.GRAY);
frmViperManufacturingRecord.setTitle("Manufacturing Record Book");
frmViperManufacturingRecord.setBounds(100, 100, 1026, 702);
frmViperManufacturingRecord.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frmViperManufacturingRecord.getContentPane().setLayout(null);
JButton btnGeneratePdfHeader = new JButton("Generate PDF Header");
btnGeneratePdfHeader.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//JOptionPane.showMessageDialog(null, "Welcome to viper");
txtText.setText("Hi User....");
}
});
btnGeneratePdfHeader.setFont(new Font("Calibri", Font.BOLD, 12));
btnGeneratePdfHeader.setBounds(786, 183, 156, 23);
frmViperManufacturingRecord.getContentPane().add(btnGeneratePdfHeader);
txtText = new JTextField();
txtText.setText("text1");
txtText.setBounds(678, 182, 98, 23);
frmViperManufacturingRecord.getContentPane().add(txtText);
txtText.setColumns(10);
JTree tree = new JTree();
tree.setBounds(10, 11, 304, 624);
//tree.setModel("FileTreeDemo");
JScrollPane scrollpane = new JScrollPane(tree);
frmViperManufacturingRecord.getContentPane().add(tree);
}
}
FileTreeDemo.java
public class FileTreeDemo {
public static void main(String[] args) {
File root;
if (args.length > 0) root = new File(args[0]);
else root = new File(System.getProperty("user.home"));
FileTreeModel model = new FileTreeModel(root);
JTree tree = new JTree();
tree.setModel(model);
// The JTree can get big, so allow it to scroll.
JScrollPane scrollpane = new JScrollPane(tree);
// Display it all in a window and make the window appear
JFrame frame = new JFrame("FileTreeDemo");
frame.getContentPane().add(scrollpane, "Center");
frame.setSize(400,600);
frame.setVisible(true);
}
}
class FileTreeModel implements TreeModel {
// We specify the root directory when we create the model.
protected File root;
public FileTreeModel(File root) { this.root = root; }
// The model knows how to return the root object of the tree
public Object getRoot() { return root; }
// Tell JTree whether an object in the tree is a leaf or not
public boolean isLeaf(Object node) { return ((File)node).isFile(); }
// Tell JTree how many children a node has
public int getChildCount(Object parent) {
String[] children = ((File)parent).list();
if (children == null) return 0;
return children.length;
}
public Object getChild(Object parent, int index) {
String[] children = ((File)parent).list();
if ((children == null) || (index >= children.length)) return null;
return new File((File) parent, children[index]);
}
public int getIndexOfChild(Object parent, Object child) {
String[] children = ((File)parent).list();
if (children == null) return -1;
String childname = ((File)child).getName();
for(int i = 0; i < children.length; i++) {
if (childname.equals(children[i])) return i;
}
return -1;
}
public void valueForPathChanged(TreePath path, Object newvalue) {}
public void addTreeModelListener(TreeModelListener l) {}
public void removeTreeModelListener(TreeModelListener l) {}
}
If you want to use the FileTreeModel tree model in the mrbpdf class, you can use this code in the mrbpdf.initialize method:
//JTree tree = new JTree();
File root = new File(System.getProperty("user.home"));
FileTreeModel model = new FileTreeModel(root);
JTree tree = new JTree(model);
To add scrolling as well, this code could help you (in the same method as above):
//tree.setBounds(10, 11, 304, 624);
JScrollPane scrollpane = new JScrollPane(tree);
scrollpane.setBounds(10, 11, 304, 624);
//frmViperManufacturingRecord.getContentPane().add(tree);
frmViperManufacturingRecord.getContentPane().add(scrollpane);
Edit - to support multiple roots, you could use a different tree model, like for example:
class MultipleDirectoriesTreeModel implements TreeModel {
protected List<File> roots;
public MultipleDirectoriesTreeModel(File... roots) {
this.roots = Arrays.asList(roots);
}
public Object getRoot() { return this; }
public boolean isLeaf(Object node) {
return node instanceof File && ((File)node).isFile();
}
public int getChildCount(Object parent) {
if (parent == this)
return roots.size();
else {
String[] children = ((File) parent).list();
if (children == null)
return 0;
return children.length;
}
}
public Object getChild(Object parent, int index) {
if (parent == this)
return index >= 0 && index < roots.size() ? roots.get(index) : null;
else {
String[] children = ((File) parent).list();
if ((children == null) || (index >= children.length))
return null;
return new File((File) parent, children[index]);
}
}
public int getIndexOfChild(Object parent, Object child) {
String childname = ((File) child).getName();
if (parent == this) {
for (int rootIndex = 0; rootIndex < roots.size(); rootIndex++)
if (childname.equals(roots.get(rootIndex).getName()))
return rootIndex;
return -1;
} else {
String[] children = ((File) parent).list();
if (children == null)
return -1;
for (int i = 0; i < children.length; i++) {
if (childname.equals(children[i]))
return i;
}
return -1;
}
}
#Override
public String toString() {
return "My Computer";
}
public void valueForPathChanged(TreePath path, Object newvalue) {}
public void addTreeModelListener(TreeModelListener l) {}
public void removeTreeModelListener(TreeModelListener l) {}
}
This tree model could be initialized like this:
MultipleDirectoriesTreeModel model
= new MultipleDirectoriesTreeModel(new File("C:\\"), new File("D:\\"));
I have been trying to implement a CheckBox Node Tree, where the parent nodes also have checkbox.
I have been able to achieve the structure .
But when i try to implement the CellEditor , i am not able to check the leaf nodes .The parent nodes are checkable , but not the leaf nodes. Please give me some guidance .
/**
*CheckBoxNodeRenderer.java
*/
public class CheckBoxNodeRenderer implements TreeCellRenderer{
public JCheckBox getLeafRenderer()
{
return leafRenderer;
}
public CheckBoxNodeRenderer()
{
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
leafRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager
.get("Tree.drawsFocusBorderAroundIcon");
leafRenderer.setFocusPainted((booleanValue != null)
&& (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
Component returnValue;
if (leaf) {
String stringValue = tree.convertValueToText(value, selected,
expanded, leaf, row, false);
if (selected) {
leafRenderer.setForeground(selectionForeground);
leafRenderer.setBackground(selectionBackground);
} else {
leafRenderer.setForeground(textForeground);
leafRenderer.setBackground(textBackground);
}
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value)
.getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode) userObject;
leafRenderer.setText(node.getText());
leafRenderer.setSelected(node.isSelected());
}
}
returnValue = leafRenderer;
} else {
leafRenderer.setText(value.toString());
leafRenderer.setSelected(selected);
returnValue = leafRenderer;
}
return returnValue;
}
public JCheckBox leafRenderer = new JCheckBox();
private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();
Color selectionBorderColor, selectionForeground, selectionBackground,
textForeground, textBackground;
}
The code below is the checkBoxNodeEditor.java
/**
*CheckBoxNodeEditor.java
*/
public class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
public CheckBoxNodeEditor(JTree tree) {
this.tree = tree;
}
/**
* Checkbox node editor for node selection
*/
public Object getCellEditorValue() {
GenerateCube.nodeselection();
return GenerateCube.selectNode;
}
/**
* Cell Editable
*/
public boolean isCellEditable(EventObject event) {
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
path = tree.getPathForLocation(mouseEvent.getX(),
mouseEvent.getY());
model = (DefaultTreeModel) (tree.getModel());
if (path != null) {
if(path.getChildCount > 0) {
Object node = path.getPathComponent(0);
}
else{
Object node = path.getLastPathComponent();
}
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
returnValue = ((treeNode.isLeaf()) && (userObject instanceof CheckBoxNode));
}
}
}
return returnValue;
}
/**
* getting Tree Cell Editor Component
*/
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row) {
Component editor = GenerateCube.renderer.getTreeCellRendererComponent(tree, value,
true, expanded, leaf, row, true);
// editor always selected / focused
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
} };
if (editor instanceof JCheckBox) {
((JCheckBox) editor).addItemListener(itemListener);
}
return editor;
}
static int n;
public static JTree tree;
static DefaultTreeModel model ;
ChangeEvent changeEvent = null;
public static TreePath path ;
public static String parentstring;
public static DefaultMutableTreeNode nNode;
public static MutableTreeNode node;
String nodeName;
}
maybe this code can help you with TreeCellEditor/Renderer
import java.awt.*;
import java.awt.event.*;
import java.util.EventObject;
import java.util.Vector;
import javax.swing.*;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
//import org.pushingpixels.substance.api.skin.SubstanceOfficeSilver2007LookAndFeel;
public class CheckBoxNodeTreeSample {
public static void main(String args[]) {
JFrame frame = new JFrame("CheckBox Tree");
//setLookAndFeel();
CheckBoxNode accessibilityOptions[] = {new CheckBoxNode(
"Move system caret with focus/selection changes", false),
new CheckBoxNode("Always expand alt text for images", true)};
CheckBoxNode browsingOptions[] = {
new CheckBoxNode("Notify when downloads complete", true),
new CheckBoxNode("Disable script debugging", true),
new CheckBoxNode("Use AutoComplete", true),
new CheckBoxNode("Browse in a new process", false)};
Vector accessVector = new NamedVector("Accessibility",
accessibilityOptions);
Vector browseVector = new NamedVector("Browsing", browsingOptions);
Object rootNodes[] = {accessVector, browseVector};
Vector rootVector = new NamedVector("Root", rootNodes);
JTree tree = new JTree(rootVector);
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
tree.setCellRenderer(renderer);
tree.setCellEditor(new CheckBoxNodeEditor(tree));
tree.setEditable(true);
JScrollPane scrollPane = new JScrollPane(tree);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
frame.setSize(300, 150);
frame.setVisible(true);
}
private static void setLookAndFeel() {
/*SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
//UIManager.setLookAndFeel(new SubstanceAutumnLookAndFeel());
UIManager.setLookAndFeel(new SubstanceOfficeSilver2007LookAndFeel());
} catch (UnsupportedLookAndFeelException e) {
throw new RuntimeException(e);
}
}
});*/
}
}
class CheckBoxNodeRenderer implements TreeCellRenderer {
private JCheckBox leafRenderer = new JCheckBox();
private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();
private Color selectionBorderColor, selectionForeground, selectionBackground,
textForeground, textBackground;
protected JCheckBox getLeafRenderer() {
return leafRenderer;
}
public CheckBoxNodeRenderer() {
Font fontValue;
fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
leafRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
leafRenderer.setFocusPainted((booleanValue != null) && (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row,
boolean hasFocus) {
Component returnValue;
if (leaf) {
String stringValue = tree.convertValueToText(value, selected,
expanded, leaf, row, false);
leafRenderer.setText(stringValue);
leafRenderer.setSelected(false);
leafRenderer.setEnabled(tree.isEnabled());
if (selected) {
leafRenderer.setForeground(selectionForeground);
leafRenderer.setBackground(selectionBackground);
} else {
leafRenderer.setForeground(textForeground);
leafRenderer.setBackground(textBackground);
}
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode) userObject;
leafRenderer.setText(node.getText());
leafRenderer.setSelected(node.isSelected());
}
}
returnValue = leafRenderer;
} else {
returnValue = nonLeafRenderer.getTreeCellRendererComponent(tree,
value, selected, expanded, leaf, row, hasFocus);
}
return returnValue;
}
}
private class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
private CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
private ChangeEvent changeEvent = null;
private JTree tree;
public CheckBoxNodeEditor(JTree tree) {
this.tree = tree;
}
public Object getCellEditorValue() {
JCheckBox checkbox = renderer.getLeafRenderer();
CheckBoxNode checkBoxNode = new CheckBoxNode(checkbox.getText(),
checkbox.isSelected());
return checkBoxNode;
}
#Override
public boolean isCellEditable(EventObject event) {
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
TreePath path = tree.getPathForLocation(mouseEvent.getX(),
mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
if ((node != null) && (node instanceof DefaultMutableTreeNode)) {
DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) node;
Object userObject = treeNode.getUserObject();
returnValue = ((treeNode.isLeaf()) && (userObject instanceof CheckBoxNode));
}
}
}
return returnValue;
}
public Component getTreeCellEditorComponent(JTree tree, Object value,
boolean selected, boolean expanded, boolean leaf, int row) {
Component editor = renderer.getTreeCellRendererComponent(tree, value,
true, expanded, leaf, row, true);
// editor always selected / focused
ItemListener itemListener = new ItemListener() {
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
}
};
if (editor instanceof JCheckBox) {
((JCheckBox) editor).addItemListener(itemListener);
}
return editor;
}
}
class CheckBoxNode {
private String text;
private boolean selected;
public CheckBoxNode(String text, boolean selected) {
this.text = text;
this.selected = selected;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean newValue) {
selected = newValue;
}
public String getText() {
return text;
}
public void setText(String newValue) {
text = newValue;
}
#Override
public String toString() {
return getClass().getName() + "[" + text + "/" + selected + "]";
}
}
class NamedVector extends Vector {
private String name;
public NamedVector(String name) {
this.name = name;
}
public NamedVector(String name, Object elements[]) {
this.name = name;
for (int i = 0, n = elements.length; i < n; i++) {
add(elements[i]);
}
}
#Override
public String toString() {
return "[" + name + "]";
}
}