I have a JTree with a custom model and custom renderer. They seem to work fine, except that when I expand a node that has multiple child nodes, it draws all child nodes to the same rectangle so only the last node is visible. I can iterate through the child nodes and I can print them to the console, but the UI is different.
This is a sketch of the UI:
// for 1 child node:
ROOT
-- <rendered node>
//for 2 child nodes:
ROOT
|
-- <rendered node - the data of the 2nd node>
//for 3 child nodes:
ROOT
|
|
-- <rendered node - the data of the 3rd node>
I figured out that the BasicTreeUI uses getPathBounds() to determine the bounds of a certain node. Here is how I check it:
doStuffWithTree(JTree tree) {
tree.expandNode(0);
for (int i = 0; i < tree.getRowCount(); i++) {
TreePath tp = tree.getPathForRow(i);
tree.makeVisible(tp);
System.out.println("Bounds of row " + i + ": " + tree.getPathBounds(tp));
}
}
With this check I get that all the nodes - except the node 0 - have the same bounds.
I tried a few tricks with expanding the tree, e.g. expanding all nodes starting from the first; then starting from the last - they all have the same effect.
Finally, the question: what class could I modify to fix this behavior? Are there any known workarounds?
UPDATE
Apparently, something needs to be fixed on the TreeNode implementation. Also, I need to correct myself: I didn't have a custom model, but a cusomtom TreeNode. Here is the SSCCE:
package com.example.jtree;
//lots of obvious imports
public class JTreeBoundsExample extends JFrame {
public static void main(String... args) {
JTreeBoundsExample m = new JTreeBoundsExample();
m.setSize(275, 300);
m.setDefaultCloseOperation(EXIT_ON_CLOSE);
m.setVisible(true);
}
public JTreeBoundsExample() {
ValueTreeNode root = new ValueTreeNode(null, "root");
ValueTreeNode category = new ValueTreeNode(root, "category");
category.put("k1", new ValueTreeNode(category, "v1"));
category.put("k2", new ValueTreeNode(category, "v2"));
category.put("k3", new ValueTreeNode(category, "v3"));
root.put("category", category);
JTree tree = new JTree(root);
tree.setCellRenderer(new ValueCellRenderer());
tree.setRootVisible(false);
tree.expandRow(0);
for (int i = 0; i < tree.getRowCount(); i++) {
TreePath tp = tree.getPathForRow(i);
tree.makeVisible(tp);
System.out.println("Bounds of row " + i + ": " + tree.getPathBounds(tp));
}
Container content = getContentPane();
content.add(new JScrollPane(tree), BorderLayout.CENTER);
}
private static class ValueTreeNode extends HashMap<String, ValueTreeNode> implements TreeNode {
private List<String> keys = new ArrayList<String>();
private TreeNode parent;
private String value;
public ValueTreeNode(TreeNode parent, String value) {
this.parent = parent;
this.value = value;
}
#Override
public ValueTreeNode put(String key, ValueTreeNode val) {
ValueTreeNode old = super.put(key, val);
if (old == null) {
sortKeys();
}
return old;
}
private void sortKeys() {
keys.clear();
keys.addAll(keySet());
Collections.sort(keys);
}
#Override
public Enumeration children() {
final Iterator<String> it = keys.iterator();
return new Enumeration() {
#Override
public boolean hasMoreElements() {
return it.hasNext();
}
#Override
public Object nextElement() {
return it.next();
}
};
}
#Override
public boolean getAllowsChildren() {
return true;
}
#Override
public TreeNode getChildAt(int pos) {
return get(keys.get(pos));
}
#Override
public int getChildCount() {
return size();
}
#Override
public int getIndex(TreeNode arg0) {
return -1;
}
#Override
public TreeNode getParent() {
return parent;
}
#Override
public boolean isLeaf() {
return size() == 0;
}
}
private static class ValueCellRenderer extends DefaultTreeCellRenderer {
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if (value instanceof ValueTreeNode) {
ValueTreeNode vtn = (ValueTreeNode) value;
setText(vtn.value);
}
return this;
}
}
}
Related
When I use insert() to inset new nodes into the binary tree it only inserts the new nodes on the place of child nodes even when root node already have left and right child. It is not visiting the child nodes to make deeper levels of binary tree.
Sorry for the bad English.
class Node
{
int key;
String value;
Node lc = null;
Node rc = null;
Node(int k,String v)
{
key = k;
value = v;
}
public String toString()
{
return value + "is" + key;
}
}
class BT
{
Node root;
public void insert(int k,String v)
{
Node newnode = new Node(k,v);
if(root == null)
{
System.out.println("root");
root = newnode;
return;
}
Node n = root;
while(n != null)
{
if(newnode.key <= n.key)
{
n = n.lc;
System.out.println("left");
if(n==null){n = newnode; break;}
}
else
{
n = n.rc;
System.out.println("right");
if(n==null){n = newnode; break;}
}
}
System.out.println("loop ended");
return;
}
}
public class test
{
public static void main(String arg[])
{
BT list = new BT();
list.insert(19,"one");
list.insert(67,"sixtyseven");
list.insert(5,"five");
list.insert(12,"twelve");
list.insert(67,"sixtyseven");
}
}
You never change the lc and rc links. Try something like this:
if(newnode.key <= n.key)
{
if(n.lc==null){n.lc = newnode; break;}
n = n.lc;
System.out.println("left");
}
else
{
if(n.rc==null){n.rc = newnode; break;}
n = n.rc;
System.out.println("right");
}
What about using recursion? It's much more clear to realise.
public final class BinaryTree {
private static final boolean ADD_TO_PARENT = true;
private static final boolean ALREADY_ADDED = false;
private Node root;
public void add(int key, String value) {
Node node = new Node(key, value);
if (add(node, root) == ADD_TO_PARENT)
root = node;
}
private static boolean add(Node node, Node parent) {
if (parent == null)
return ADD_TO_PARENT;
if (node.key <= parent.key) {
if (add(node, parent.left) == ADD_TO_PARENT)
parent.left = node;
} else if (add(node, parent.right) == ADD_TO_PARENT)
parent.right = node;
return ALREADY_ADDED;
}
public static final class Node {
private final int key;
private final String value;
private Node left;
private Node right;
public Node(int key, String value) {
this.key = key;
this.value = value;
}
#Override
public String toString() {
return value + " is " + key;
}
}
}
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 wrote a class that defines some operations on binary trees.
Now I have to add a method that returns a reference to the node (or to one of the nodes, if there are more than one) U such that the ratio between the number of nodes in the subtree of the root U (thus including the node itself) and height (+1) of the same is maximized.
So I have to add to the sum the value 1 for the leaves otherwise the ratio would be 1/0 = infinity: that is, the result would be any leaf (wrong).
The algorithm must be linear in the number of nodes, visiting the tree once.
Can I define a private class auxiliary in case I need it.
I do not know how to do, someone help me?
My code is this.
public class BinaryTree {
protected class Node {
protected Integer element;
protected Node left;
protected Node right;
Node(int element) {
this.element = element;
left = right = null;
}
Node(int element, Node left, Node right) {
this.element = element;
this.left = left;
this.right = right;
}
boolean isLeaf() {
return left == null && right == null;
}
} //end Node class
public class NodeReference {
private Node node;
private NodeReference(Node node) {
this.node = node;
}
public int getElement() {
return node.element;
}
public void setElement(int e) {
node.element = e;
}
} //end class NodeReference
protected Node root;
public BinaryTree() {
root = null;
}
public boolean isEmpty() {
return root == null;
}
public void add(int element, String path) {
//working properly
}
protected Node add(int elem, String path, Node node) {
//working properly
}
public void printPreOrder() {
//working properly
}
protected void printPreOrder(Node node) {
//working properly
}
public int height() {
//working properly
}
protected int height(Node node) {
//working properly
}
public int sum() {
//working properly
}
private int sum(Node node) {
//working properly
}
public int size() {
//working properly
}
private int size(Node node) {
//working properly
}
public boolean search(int x) {
//working properly
}
protected boolean search(int x, Node node) {
//working properly
}
public boolean equalTo(BinaryTree t) {
//working properly
}
public boolean equals(Object ob) {
//working properly
}
protected boolean areEqual(Node node1, Node node2) {
//working properly
}
public BinaryTree copy() {
//working properly
}
protected Node copy(Node node) {
//working properly
}
public NodeReference find(int x) {
//working properly
}
private Node find(int x, Node nd) {
//working properly
}
public boolean isCompletelyBalanced() {
//working properly
}
private int isCompletelyBalanced(Node node) {
//working properly
}
public boolean is1Balanced() {
//working properly
}
private int is1Balanced(Node node) {
//working properly
}
private class BoolNode {
boolean found;
Node node;
BoolNode(boolean found, Node node) {
this.found = found;
this.node = node;
}
}
public boolean removeSubtree(int x) {
//working properly
}
protected BoolNode removeSubtree(int x, Node node) {
//working properly
}
public int maxElem() throws IllegalStateException {
if(root == null)
throw new IllegalStateException("Empty tree.");
return maxElem(root);
}
private static int max3(int x, int y, int z) {
return max(x, max(y, z));
}
private int maxElem(Node node) {
int max = node.element;
if(node.left != null)
max = Math.max(max, maxElem(node.left));
if(node.right != null)
max = Math.max(max, maxElem(node.right));
return max;
}
public NodeReference maxDescendantsHeightRatio() {
//As I write this method?
}
}
I started doing it this way:
public NodeReference maxDescendantsHeightRatio() {
ArrayList<Node> list = iteratorPreOrder();
ArrayList<NodeWithRatio> listRatio = new ArrayList<NodeWithRatio>();
for(int i = 0; i < list.size(); i++) {
int s = size();
int h = height() + 1;
int r = ratioScore(s, h);
listRatio.add(new NodeWithRatio(this, r));
}
//sort the array list
Collections.sort(listRatio, new Comparator<Point>() {
public int compare(NodeWithRatio o1, NodeWithRatio o2) {
return Integer.compare(o1.ratio, o2.ratio);
}
});
//find max value in the list of node with ratio
NodeWithRatio result = listRatio.get(listRatio.size() - 1); //gets the last item, largest for an ascending sort
return result.node;
//return null;
}
private int ratioScore(int size, int height) {
return size / height;
}
private class NodeWithRatio {
Node node;
int ratio;
public NodeWithRatio(Node n, int r) {
node = n;
ratio = r;
}
} //end NodeWithRatio class
public ArrayList<Node> iteratorPreOrder() {
ArrayList<Node> templist = new ArrayList<Node>();
preorder(root, templist);
for(int i = 0; i < templist.size(); i++)
System.out.println(templist.get(i).element);
return templist;
}
private void preorder(Node node, ArrayList<Node> templist) {
if(node != null) {
templist.add(node); // adds to end of list.
preorder(node.left, templist);
preorder(node.right, templist);
}
}
public int height() {
if(isEmpty())
return -1;
return height(root);
}
protected int height(Node node) {
return (node == null)? -1: 1 + Math.max(height(node.left), height(node.right));
}
public int size() {
if(isEmpty())
return 0;
return size(root);
}
private int size(Node node) {
if(node == null)
return 0;
return size(node.left) + size(node.right) + 1;
}
I think it's the wrong piece of code:
for(int i = 0; i < list.size(); i++) {
int s = size();
int h = height() + 1;
int r = ratioScore(s, h);
listRatio.add(new NodeWithRatio(this, r));
}
Since it is not recursive, but I do not know how to fix it ...
Does anyone have any advice?
Thanks!
As you figured out, the calculation of the ratio is a combination of the calculation of the size and height of the tree.
In order to find the max ratio in one pass over the nodes of the tree, we can use a recursive method that would calculate both the size and height of the tree. In order to calculate the ratio for a node, it's not enough to know the ratios of its two children - we need to know the size and height of the children's sub trees. For this purpose I'll return an array of two ints - the first is the size and the second the height.
public int[] ratio(Node node)
{
int[] result = new int[2];
int[0] = 0;
int[1] = 0;
if (node = null)
return result;
int[] leftRatio = ratio(node.left);
int[] rightRatio = ratio(node.right);
result[0] = leftRatio[0] + rightRatio[0] + 1; // current sub tree size
result[1] = Math.max(leftRatio[1] + rightRatio[1]) + 1; // current sub tree height
return result;
}
Now, if you want to find all the nodes with the highest ratio, you can add a static variable holding the current max ratio and another static variable holding the nodes having that max ratio (I know, it's ugly, but it works).
...
static float maxRatio = 0;
static Set<Node> maxRatioNodes = new Set<Node>();
...
public int[] ratio(Node node)
{
int[] result = new int[2];
int[0] = 0;
int[1] = 0;
if (node = null)
return result;
int[] leftRatio = ratio(node.left);
int[] rightRatio = ratio(node.right);
result[0] = leftRatio[0] + rightRatio[0] + 1; // current sub tree size
result[1] = Math.max(leftRatio[1] + rightRatio[1]) + 1; // current sub tree height
float currRatio = result[0]/result[1];
if (currRatio > maxRatio) { // found a new max ratio
maxRatio = currRatio;
maxRatioNodes.clear();
maxRatioNodes.add (node);
} else if (currRatio == maxRatio) { // node's ratio equals the current max ratio
maxRatioNodes.add (node);
}
return result;
}
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.
I want to make it like when I click a button, it will create a new file. Then the jTree will highlight the new file. Below are my code. Currently I create new file, i will show the new file but no highlight the file.
class FileTreeModel implements TreeModel {
private FileNode root;
public FileTreeModel(String directory) {
root = new FileNode(directory);
}
public Object getRoot() {
return root;
}
public Object getChild(Object parent, int index) {
FileNode parentNode = (FileNode) parent;
return new FileNode(parentNode, parentNode.listFiles()[index].getName());
}
public int getChildCount(Object parent) {
FileNode parentNode = (FileNode) parent;
if (parent == null || !parentNode.isDirectory()
|| parentNode.listFiles() == null) {
return 0;
}
return parentNode.listFiles().length;
}
public boolean isLeaf(Object node) {
return (getChildCount(node) == 0);
}
public int getIndexOfChild(Object parent, Object child) {
FileNode parentNode = (FileNode) parent;
FileNode childNode = (FileNode) child;
return Arrays.asList(parentNode.list()).indexOf(childNode.getName());
}
public void valueForPathChanged(TreePath path, Object newValue) {
}
public void addTreeModelListener(TreeModelListener l) {
}
public void removeTreeModelListener(TreeModelListener l) {
}
}
class FileNode extends java.io.File {
public FileNode(String directory) {
super(directory);
}
public FileNode(FileNode parent, String child) {
super(parent, child);
}
#Override
public String toString() {
return getName();
}
}
jTree = new JTree();
jTree.setBounds(new Rectangle(164, 66, 180, 421));
jTree.setBackground(SystemColor.inactiveCaptionBorder);
jTree.setBorder(BorderFactory.createTitledBorder(null, "",
TitledBorder.LEADING, TitledBorder.TOP, new Font("Arial",
Font.BOLD, 12), new Color(0, 0, 0)));
FileTreeModel model = new FileTreeModel(root);
jTree.setRootVisible(false);
jTree.setModel(model);
expandAll(jTree);
public void expandAll(JTree tree) {
int row = 0;
while (row < tree.getRowCount()) {
tree.expandRow(row);
row++;
}
}
you can do this by calling JTree.setSelectionPath(TreePath path);