How to disable a leaf of the JTree - java

I have a JTree that shows files and folders, I would like to have a access to nodes to set them enabled or disabled. For example if the button is pressed, or when their text is equal to a specific text, then set them disabled.
Here is my main class:
public FileViewer(){
frame = new JFrame("File Viewer");
panel = new JPanel(new BorderLayout());
root = new File("D:\\Documents\\A X");
FileSystemModel model = new FileSystemModel(root);
tree = new JTree();
tree.setModel(model);
panel.add(tree, BorderLayout.CENTER);
traverse(tree, "DS.png");
frame.add(panel);
frame.setSize(600, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
// TODO code application logic here
new FileViewer();
}
I could make a traverse method that check every child of the model and find a specific child:
public void traverse(JTree tree, String word) {
TreeModel model = tree.getModel();
if (model != null) {
Object root = model.getRoot();
//System.out.println("THIS IS ROOOT >>>>>> " + root.toString());
walk(model, root, word);
}
else
System.out.println("Tree is empty.");
}
protected void walk(TreeModel model, Object o, String word){
int cc;
cc = model.getChildCount(o);
for( int i=0; i < cc; i++) {
Object child = model.getChild(o, i);
if (model.isLeaf(child) && child.toString().equals(word)){
System.out.println(child.toString());
}
else {
//System.out.println("--" + child.toString());
walk(model,child, word);
}
}
}
JTree has the method setEnabled(Boolean), but its nodes don't. Is there any idea how to make the nodes disabled?
This is my FileSystemModel file, if you like to know about it.

You should create custom DefaultTreeCellRenderer class and use it setEnabled method.
package com.company;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import java.awt.*;
public class Main {
public static void main(String[] args) {
TreeNode treeNode = new DefaultMutableTreeNode("Test");
JTree tree = new JTree();
tree.setModel(new DefaultTreeModel(treeNode));
tree.setCellRenderer(new CustomDefaultTreeCellRenderer());
JFrame frame = new JFrame();
frame.setContentPane(tree);
frame.setSize(320, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
static class CustomDefaultTreeCellRenderer extends DefaultTreeCellRenderer {
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
boolean enabled = false; // <-- here is your logic for enable/disable cell
sel = enabled;
hasFocus = enabled;
Component treeCellRendererComponent = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
treeCellRendererComponent.setEnabled(enabled);
return treeCellRendererComponent;
}
}
}

Related

Swing - JTree multiple root folders selected from JFileChooser

I am beginner in Swings. Trying to build a small application on JTree. Stuck with this issue.
I am able to load a selected folder using JFileChooser to the tree, but if I open one more folder, previous folder is replaced with the new folder. Is there any way we can open multiple folders in tree? As I keep opening folders from file chooser, it has to keep adding to existing tree. Please suggest how to achieve this?
Here is the working example I tried (picked from other posts):
import javax.swing.*;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.File;
public class TreeFrame {
public static void main(String[] args) {
JFrame frame = createFrame();
JPanel browsePanel = new JPanel();
JButton open = new JButton();
open.setPreferredSize(new Dimension(70, 25));
open.setText("Open");
final JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
JTree tree = new JTree();
JScrollPane scrollPane = new JScrollPane(tree);
Action action = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
int result = fileChooser.showOpenDialog(frame);
if (result == JFileChooser.APPROVE_OPTION) {
File file = fileChooser.getSelectedFile();
TreeModel model = new FileTreeModel(file);
tree.setModel(model);
scrollPane.add(tree);
}
}
};
open.addActionListener(action);
browsePanel.add(open);
frame.add(browsePanel, BorderLayout.WEST);
frame.add(scrollPane, BorderLayout.EAST);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static JFrame createFrame() {
JFrame frame = new JFrame("JTree Expand/Collapse example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(500, 400));
frame.setLayout(new GridLayout(1, 2));
return frame;
}
static class FileTreeModel implements TreeModel {
protected File root;
public FileTreeModel(File root) { this.root = root; }
public Object getRoot() { return root; }
public boolean isLeaf(Object node) { return ((File)node).isFile(); }
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) {}
}
}
Thanks for the suggestion, found solution:
DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot();
root.add(addNodes(null, file));
model.reload(root);

How does one properly initialize a JTextPane StyleSheet, so no other HTML enabled component is affected by the style?

I'm trying to use a JTextPane to render some HTML and apply a CSS stylesheet to it. This means I'm using HTMLEditorKit and StyleSheet classes. I know that all HTMLEditorKits share the same default StyleSheet instance, so if you change this default stylesheet object, you are applying changes at application level (all components that render HTML).
But in my example I thought that I had avoided this by creating my own StyleSheet instance based on the default. This does not work however, as evident on the displayed JTree, which renders as per the stylesheet that was only intended to be applied to the JTextPane.
import java.awt.*;
import javax.swing.*;
import javax.swing.text.html.*;
import javax.swing.tree.*;
public class TextPaneCssSpill extends JFrame {
private JTextPane textPane;
private JTree tree;
private JSplitPane splitPane;
public TextPaneCssSpill() {
HTMLEditorKit hed = new HTMLEditorKit();
StyleSheet defaultStyle = hed.getStyleSheet();
StyleSheet style = new StyleSheet();
style.addStyleSheet(defaultStyle);
style.addRule("body {font-family:\"Monospaced\"; font-size:9px;}");
style.addRule("i {color:#bababa; font-size:9px;}"); // gray italic
hed.setStyleSheet(style);
textPane = new JTextPane();
textPane.setEditorKit(hed);
textPane.setDocument(hed.createDefaultDocument());
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MyNode("name", "argument"), true);
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
tree = new JTree(root);
tree.setCellRenderer(new MyNodeTreeRenderer());
setLayout(new BorderLayout());
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textPane, tree);
add(splitPane);
pack();
setLocationRelativeTo(null);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TextPaneCssSpill().setVisible(true);
}
});
}
private static class MyNode {
private final String name;
private final String argument;
public MyNode(String name, String argument) {
this.name = name;
this.argument = argument;
}
#Override
public String toString() {
return name + " " + argument;
}
}
private static class MyNodeTreeRenderer extends DefaultTreeCellRenderer {
#Override
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 DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof MyNode) {
MyNode mynode = (MyNode) node.getUserObject();
setText("<html>" + mynode.name + " <i>" + mynode.argument);
}
}
return this;
}
}
}
So how does one properly initialize these objects, so that there is no CSS spillage across the application (so that text pane renders according to CSS, yet the tree does not)?
Note: the red underline in the above image indicates the spillage problem and was added by me later (no, it is not the renderer).
The problematic part of my code is calling HTMLEditorKit.setStyleSheet(style);. This replaces the default stylesheet instance for all HTMLEditorKits, which I was not aware of.
/**
* Set the set of styles to be used to render the various
* HTML elements. These styles are specified in terms of
* CSS specifications. Each document produced by the kit
* will have a copy of the sheet which it can add the
* document specific styles to. By default, the StyleSheet
* specified is shared by all HTMLEditorKit instances.
* This should be reimplemented to provide a finer granularity
* if desired.
*/
public void setStyleSheet(StyleSheet s) {
if (s == null) {
AppContext.getAppContext().remove(DEFAULT_STYLES_KEY);
} else {
AppContext.getAppContext().put(DEFAULT_STYLES_KEY, s);
}
}
/**
* Get the set of styles currently being used to render the
* HTML elements. By default the resource specified by
* DEFAULT_CSS gets loaded, and is shared by all HTMLEditorKit
* instances.
*/
public StyleSheet getStyleSheet() {
AppContext appContext = AppContext.getAppContext();
StyleSheet defaultStyles = (StyleSheet) appContext.get(DEFAULT_STYLES_KEY);
if (defaultStyles == null) {
defaultStyles = new StyleSheet();
appContext.put(DEFAULT_STYLES_KEY, defaultStyles);
try {
InputStream is = HTMLEditorKit.getResourceAsStream(DEFAULT_CSS);
Reader r = new BufferedReader(
new InputStreamReader(is, "ISO-8859-1"));
defaultStyles.loadRules(r, null);
r.close();
} catch (Throwable e) {
// on error we simply have no styles... the html
// will look mighty wrong but still function.
}
}
return defaultStyles;
}
So what needs to be done is to extend HTMLEditorKit to make it return your stylesheet without changing defaults.
import java.awt.*;
import java.io.IOException;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.tree.*;
public class TextPaneCssSpill extends JFrame {
private JTextPane textPane;
private JTree tree;
private JSplitPane splitPane;
private StyleSheet style;
public TextPaneCssSpill() {
MyHTMLEditorKit hed = new MyHTMLEditorKit();
StyleSheet defaultStyle = hed.getDefaultStyleSheet();
style = new StyleSheet();
style.addStyleSheet(defaultStyle);
style.addRule("body {font-family:\"Monospaced\"; font-size:9px;}");
style.addRule("i {color:#bababa; font-size:9px;}"); // gray italic
hed.setStyleSheet(style);
textPane = new JTextPane();
textPane.setEditorKit(hed);
textPane.setDocument(hed.createDefaultDocument());
appendHtmlToTextPane("<i>our gray italic text</i>", textPane);
DefaultMutableTreeNode root = new DefaultMutableTreeNode(new MyNode("name", "argument"), true);
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
root.add(new DefaultMutableTreeNode(new MyNode("name", "argument"), false));
tree = new JTree(root);
tree.setCellRenderer(new MyNodeTreeRenderer());
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, textPane, tree);
add(splitPane);
pack();
setLocationRelativeTo(null);
}
private void appendHtmlToTextPane(String str, JTextPane pane) {
Document doc = pane.getDocument();
if (doc != null) {
if (doc instanceof HTMLDocument) {
HTMLDocument htmlDoc = (HTMLDocument) doc;
Element html = htmlDoc.getDefaultRootElement();
if (HTML.Tag.HTML.toString().equalsIgnoreCase(html.getName())) {
Element body = null;
for (int i = 0; i < html.getElementCount(); i++) {
Element element = html.getElement(i);
if (element.getAttributes().getAttribute(StyleConstants.NameAttribute) == HTML.Tag.BODY) {
body = element;
break;
}
}
if (HTML.Tag.BODY.toString().equalsIgnoreCase(body.getName())) {
try {
htmlDoc.insertBeforeEnd(body, str);
Element lastLine = body.getElement(body.getElementCount() - 1);
int end = lastLine.getStartOffset();
textPane.setCaretPosition(end);
} catch (BadLocationException e) {
} catch (IOException ex) {
}
}
}
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TextPaneCssSpill().setVisible(true);
}
});
}
private static class MyNode {
private final String name;
private final String argument;
public MyNode(String name, String argument) {
this.name = name;
this.argument = argument;
}
#Override
public String toString() {
return name + " " + argument;
}
}
private static class MyNodeTreeRenderer extends DefaultTreeCellRenderer {
#Override
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 DefaultMutableTreeNode) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
if (node.getUserObject() instanceof MyNode) {
MyNode mynode = (MyNode) node.getUserObject();
setText("<html>" + mynode.name + " <i>" + mynode.argument);
}
}
return this;
}
}
/**
* Avoid setting the stylesheet for all HTMLEditorKit instances.
*/
private static class MyHTMLEditorKit extends HTMLEditorKit {
private StyleSheet myStyle;
#Override
public StyleSheet getStyleSheet() {
return myStyle == null ? super.getStyleSheet() : myStyle;
}
#Override
public void setStyleSheet(StyleSheet s) {
this.myStyle = s;
}
public StyleSheet getDefaultStyleSheet() {
return super.getStyleSheet();
}
public void setDefaultStyleSheet(StyleSheet s) {
super.setStyleSheet(s);
}
}
}

Java 'CheckBox Node Tree Sample' DefaultMutableNode doesn't have any listener?

I have a problem with adding new nodes to an existing tree. My tree is based on the CheckBoxNodeTreeSample you can see in the link:
http://www.java2s.com/Code/Java/Swing-JFC/CheckBoxNodeTreeSample.htm
I'm simply trying to add a new node by doing the 'standard' thing:
DefaultTreeModel model = (DefaultTreeModel) this.view.getResultTree().getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("newNode");
root.add(newNode);
model.reload(root);
This works fine, a new node is added, but the difference from the other existing nodes is that I can't click the checkbox, it's not clickable. I tried to print the path of the new node and an existing node, it looks like this:
newNode
Model.CheckBoxNode[Person,..../true]
I been trying to solve this for some time now, but I can't see how to solve this.. Some help would be appreciated! :)
Should i try to add vectors like this? and might this be why the standard solution doesn't work? Been trying this, but the model doesn't like vectors, objects or anything like that.. Casting isn't working either. This is the initialization of the tree, and how they use vectors and checkboxnodes :
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);
Just in case if you didn't find any proper answer, here is a post I asked and answered how to build checkbox tree:
LINK
It might help you
I made an SSCCE. There is a button you can click to add a new node, with a checkbox but it's not clickable. I'm not creating a jcheckbox anywhere, so don't really understand where I should put an extra listener, and don't understand why it doesn't use getTreeCellEditorComponent() in the editor?
I also tried a slightly different approach where I use model.insertNodeInto(), but still I can't click the checkbox. I believe that I should be able to create a checkboxNode instead of a DefaultMutableTreeNode, but the JTree model doesn't want that, therefore trying to cast it to a DefaultMutableTreeNode.. Anyways, thanks for your help so far!
UPDATE:Because the new node is not of type CheckBoxNode. Therefore listeners doesn't work on the nodes. So now I'm looking for solutions to create a CheckBoxNode[], add it to a Vector, and then add that as a node (it that is possible!). That's how the author made the JTree, so I guess something like that could be the answer!
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import java.util.Vector;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreePath;
public class CheckBoxNodeTreeSample {
private static JTree tree;
public static void main(String args[]) {
JFrame frame = new JFrame("CheckBox Tree");
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);
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.NORTH);
JPanel buttonPanel = new JPanel();
JButton button = new JButton("new node");
buttonPanel.add(button);
frame.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New node");
root.add(newNode);
model.reload();
}
});
frame.setSize(300, 450);
frame.setVisible(true);
}
}
class CheckBoxNodeRenderer implements TreeCellRenderer {
private JCheckBox leafRenderer = new JCheckBox();
private DefaultTreeCellRenderer nonLeafRenderer = new DefaultTreeCellRenderer();
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;
}
}
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
ChangeEvent changeEvent = null;
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;
}
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 {
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 + "]";
}
}
class NamedVector extends Vector {
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]);
}
}
public String toString() {
return "[" + name + "]";
}
}

How do I auto-expand a JTree when setting a new TreeModel?

I have a custom JTree and a custom JModel; I would for the JTree to "auto-expand" when I give it a new model. At the moment, it simply collapse all the nodes to the root.
Here is an example:
private class CustomTree extends JTree {
#Override
public boolean isExpanded(TreePath path) {
return ((Person) path.getLastPathComponent).hasChildren();
}
private class CustomTreeModel extends TreeModel {
// ... omitting various implementation details
#Override
public boolean isLeaf(Object object) {
return !((Person) object).hasChildren();
}
}
Model model = new Model();
Person bob = new Person();
Person alice = new Person();
bob.addChild(alice);
model.setRoot(bob);
JTree tree = new CustomTree(new CustomTreeModel(model));
At this point, the tree correctly displays:
- BOB
- ALICE
where Alice is a child of Bob (both in the data and in the visual tree)
However, if I call:
tree.setModel(new CustomTreeModel(model));
everything is collapsed:
+ BOB
Is there a way to "auto-expand" everything in the tree when setting a new model?
The following worked for me (called after setting the new model):
for (int i = 0; i < tree.getRowCount(); i++) {
tree.expandRow(i);
}
I had a similar problem.
Your solution (as posted https://stackoverflow.com/a/15211697/837530) seemed to work for me only for the top level tree nodes.
But I needed to expand all the a descendants node. So I solved it with the following recursive method:
private void expandAllNodes(JTree tree, int startingIndex, int rowCount){
for(int i=startingIndex;i<rowCount;++i){
tree.expandRow(i);
}
if(tree.getRowCount()!=rowCount){
expandAllNodes(tree, rowCount, tree.getRowCount());
}
}
which is invoked with
expandAllNodes(tree, 0, tree.getRowCount());
where, tree is a JTree.
Unless someone has a better solution.
There's also this non-recursive version.
private void expandAllNodes(JTree tree) {
int j = tree.getRowCount();
int i = 0;
while(i < j) {
tree.expandRow(i);
i += 1;
j = tree.getRowCount();
}
}
this worked for me..
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Enumeration;
public class JTreeNodeAutoExpandCollapse extends JFrame {
public JTreeNodeAutoExpandCollapse() throws HeadlessException {
initializeUI();
}
private void initializeUI() {
setSize(200, 200);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
DefaultMutableTreeNode chapter1 = new DefaultMutableTreeNode("Chapter 1");
DefaultMutableTreeNode sub1 = new DefaultMutableTreeNode("1.1");
DefaultMutableTreeNode sub2 = new DefaultMutableTreeNode("1.2");
DefaultMutableTreeNode sub3 = new DefaultMutableTreeNode("1.3");
DefaultMutableTreeNode sub31 = new DefaultMutableTreeNode("1.3.1");
DefaultMutableTreeNode sub32 = new DefaultMutableTreeNode("1.3.2");
root.add(chapter1);
chapter1.add(sub1);
chapter1.add(sub2);
chapter1.add(sub3);
sub3.add(sub31);
sub3.add(sub32);
final JTree tree = new JTree(root);
expandTree(tree, false);
JScrollPane pane = new JScrollPane(tree);
pane.setPreferredSize(new Dimension(200, 200));
JPanel buttonPanel = new JPanel(new BorderLayout());
JButton expandAll = new JButton("Expand All");
expandAll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
expandTree(tree, true);
}
});
JButton collapseAll = new JButton("Collapse All");
collapseAll.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
expandTree(tree, false);
}
});
buttonPanel.add(expandAll, BorderLayout.WEST);
buttonPanel.add(collapseAll, BorderLayout.EAST);
getContentPane().setLayout(new BorderLayout());
getContentPane().add(pane, BorderLayout.CENTER);
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
}
private void expandTree(JTree tree, boolean expand) {
TreeNode root = (TreeNode) tree.getModel().getRoot();
expandAll(tree, new TreePath(root), expand);
}
private void expandAll(JTree tree, TreePath path, boolean expand) {
TreeNode node = (TreeNode) path.getLastPathComponent();
if (node.getChildCount() >= 0) {
Enumeration enumeration = node.children();
while (enumeration.hasMoreElements()) {
TreeNode n = (TreeNode) enumeration.nextElement();
TreePath p = path.pathByAddingChild(n);
expandAll(tree, p, expand);
}
}
if (expand) {
tree.expandPath(path);
} else {
tree.collapsePath(path);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new JTreeNodeAutoExpandCollapse().setVisible(true);
}
});
}
}
public void expandTree(){
int row = 1;
while (row++ < tree.getRowCount()){
tree.expandRow(row);
}
public void collapseTree(){
int row = tree.getRowCount() - 1;
while (row-- > 0){
tree.collapseRow(row);
}
}

Mouse action on custom node in JTree: difference between jre6 and jre7

A JTree uses TreeCellRenderer and TreeCellEditor to display three custom nodes:
a single JCheckBox
a single JButton
a JPanel composed by any action Component (here is a JCheckBox on North and a JButton on South)
screenshot: http://i.imgur.com/9j6B2ji.png
There is no problem with such a tree if you use Java Runtime Environment 6.
Besides using Java Runtime Environment 7, my problem occurs when I click on the JButton inside the JPanel node. The 'click' animation for JButton is not rendered the first time you click in JPanel (and after each time the node with JPanel stops being edited). The first node JCheckBox and the second node JButton are working perfectly with jre6 and jre7 either.
I would like to make it work exactly like before in jre6, is there a workaround?
Note: it's not a problem on making a button perform your code.
import java.awt.BorderLayout;
import java.awt.Component;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreeCellRenderer;
class TreeNodeModel{
public boolean isJCheckBox = false;
public boolean isJButton = false;
public boolean isJPanel = false;
public Component userObject = null;
public TreeNodeModel(String name){
if( name.equals("JCheckBox") ){
isJCheckBox = true;
}else if( name.equals("JButton") ){
isJButton = true;
}else if( name.equals("JPanel") ){
isJPanel = true;
}
}
}
class CustomTreeCellRenderer implements TreeCellRenderer{
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Component returnValue = null;
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof TreeNodeModel) {
TreeNodeModel node = (TreeNodeModel) userObject;
if(node.isJButton){
returnValue = new JButton("Ok");
}else if(node.isJCheckBox){
returnValue = new JCheckBox("Ok");
}else{
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JButton("Problem"), BorderLayout.SOUTH);
panel.add(new JCheckBox("Problem"), BorderLayout.NORTH);
returnValue = panel;
}
}
}
if(returnValue == null)
returnValue = new DefaultTreeCellRenderer().getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
return returnValue;
}
}
class CustomTreeCellEditor extends AbstractCellEditor implements TreeCellEditor {
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
Component returnValue = null;
if ((value != null) && (value instanceof DefaultMutableTreeNode)) {
Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
if (userObject instanceof TreeNodeModel) {
TreeNodeModel node = (TreeNodeModel) userObject;
if(node.isJButton){
returnValue = new JButton("Ok");
}else if(node.isJCheckBox){
returnValue = new JCheckBox("Ok");
}else{
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JButton("Problem"), BorderLayout.SOUTH);
panel.add(new JCheckBox("Problem"), BorderLayout.NORTH);
returnValue = panel;
}
}
}
return returnValue;
}
#Override
public Object getCellEditorValue() { return null; }
}
public class MyTestRendererEditor extends JFrame {
public MyTestRendererEditor() {
DefaultMutableTreeNode node = new DefaultMutableTreeNode("root");
node.add(new DefaultMutableTreeNode(new TreeNodeModel("JCheckBox")));
node.add(new DefaultMutableTreeNode(new TreeNodeModel("JButton")));
node.add(new DefaultMutableTreeNode(new TreeNodeModel("JPanel")));
final JTree tree = new JTree(new DefaultTreeModel(node));
tree.setCellRenderer(new CustomTreeCellRenderer());
tree.setCellEditor(new CustomTreeCellEditor());
tree.setEditable(true);
getContentPane().add(new JScrollPane(tree));
pack();
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MyTestRendererEditor();
}
});
}
}

Categories