I have some DefaultMutableTreeNode's.
While the programm's running, I can change the text and revalidate it.
But if the text is too long, so for example the text "tested", the text will be displayed as "te...".
How do I change this ?
Thanks
you have to read tutorial about JTree, and examples how to use TreeCellRenderer
if you'll real question then please update your question and add there the code in SSCCE form
the underlying reason is that the layout of the tree nodes is cached and the cache not updated properly. Might f.i. happen if the node is changed under feet of the model, uncomment the nodeChanged to see the difference
JTree tree = new JTree();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
int index = 0;
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
String result = "\n";
Enumeration<?> enumer = root.preorderEnumeration();
while (enumer.hasMoreElements()) {
DefaultMutableTreeNode node = (DefaultMutableTreeNode) enumer.nextElement();
String nodeValue = String.valueOf(node.getUserObject());
node.setUserObject(nodeValue + ": " + index++);
//model.nodeChanged(node);
}
The exact reason in your context might vary, no way to tell without an sscce
Related
I am using a JTree in which new nodes needs to be inserted dynamically to the root(consider adding children to root). Once the user selects a node and clicks a button, the new node needs to be added after the selected node. If none of the node is selected then it adds the new node at the end of the tree. Below is my code
public void addNodeToRoot(TestCase testCase) {
DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(testCase.toString());
int currentNoOfChildren = getTcBuilderTree().getModel().getChildCount(getTcBuilderTree().getModel().getRoot());
TreePath currentSelection = getTcBuilderTree().getSelectionPath();
int currentIndex=0;
//if the user has not selected a node add the test case at the end of the tree
if (currentSelection == null) {
currentIndex = currentNoOfChildren;
}
//if user has selected a node then insert the new node after the selected node
else {
int[] currentSelectedIndex = getTcBuilderTree().getSelectionRows();
currentIndex = currentSelectedIndex[0];
}
treeModel.insertNodeInto(childNode, getRoot(), currentIndex);
}
it works all fine but the code gives an exception when there are child nodes in the level 3 as well. The reason is when the tree has more levels and when its expanded then the currentIndex gives unexpected number (it counts all the indexes in all levels up from the root to the selected node) and the app gives ArrayIndexOutOfBoundsException since the currentIndex becomes greater than currentNoOfChildren
If the tree is not expanded then everything happens correctly. Please let me know how to resolve this. Is there any other way to get the no of children of a specific level in the tree?
Maybe code below can resolve your problem.
int currentNoOfChildren = getTcBuilderTree().getVisibleRowCount();
I have constructed a rather large JTree from XML data, literally thousands of nodes, most of which display correctly. However for some reason some node's userData Strings are not being fully displayed, rather thay are cut short or cut off completely with ... appended to the end of the name.
The affected Nodes appear to be random, and are different each time the tree is refreshed or recreated from the XML.
Things I've ruled out:
Not enough space to display the full name.
The JTree is inside a JScrollPane with plenty of horizontal space, the JScrollPane isn't even showing a horizontal scroll bar to indicate a lack of space.
Even the shortest name can be affected.
The cut off point isn't consistent for all shortened node names.
Incomplete name loaded from XML
If the affected node isn't a leaf and is then expanded or collapsed, the full name is displayed properly when it is re-rendered, the XML isn't consulted at all during this process.
EDIT:
As requested some explanation of the code behind the JTree:
The creation of the TreeModel and the population of the tree:
public XMLDialogTree(Document doc)
{
DefaultTreeModel treeModel = new DefaultTreeModel(buildTreeNode(doc.getElementsByTagName("Dialogs").item(0)));
setModel(treeModel);
}
// A recursive function to build the tree
private DefaultMutableTreeNode buildTreeNode(Node xmlNode)
{
// Make sure the node's name is a description of what it is, as opposed to a generic XML tag
XMLDialogTreeNode = new XMLDialogTreeNode(xmlNode.getAttributes().getNamedItem("name").getNodeValue());
treeNode.controlName = xmlNode.getNodeName();
// Add children to the treeNode based on the xmlNode's children
NodeList nodeList = xmlNode.getChildNodes();
for (int i = 0; i < nodeList.getLength(); i++)
{
Node tempNode = nodeList.item(i);
if (tempNode.getNodeType() == Node.ELEMENT_NODE)
{
// loop again if has child nodes
treeNode.add(buildTreeNode(tempNode));
}
}
return treeNode;
}
Where doc is an org.w3c.dom.Document containing parsed XML and an XMLDialogTreeNode is literally just a javax.swing.tree.DefaultMutableTreeNode extended to contain String controlName = "CustomNodeName"
The custom CellRenderer is as follows, The code is pretty much all to do with loading custom icons, nothing that should affect the Displayed name.
public class XMLDialogTreeCellRenderer extends DefaultTreeCellRenderer
{
private static final long serialVersionUID = 1L;
// Icons used in the JTree, loaded once on an as needed basis once, and stored here.
private static final Map<String, Icon> icons = new HashMap<String, Icon>();
static
{
// Make sure the default icon is loaded
loadIcon("_not_found");
}
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean exp, boolean leaf, int row, boolean hasFocus)
{
XMLDialogTreeNode node = (XMLDialogTreeNode) value;
setIcons(node.controlName);
super.getTreeCellRendererComponent(tree, value, sel, exp, false/* leaf */, row, hasFocus);
return this;
}
/**
* Assigns both the open and close icons, which are specific to a certain control. Loads them if necessary.
*
* #param controlName
* - The name of the control in the XML and the name of the image in the resources.treeIcons package.
*/
private final void setIcons(String controlName)
{
// Make sure the Node has a controlName set
if (controlName != null)
{
// Try and get a pre-loaded icon
Icon controlIcon = icons.get(controlName);
// It wasn't there, try loading it
if (controlIcon == null)
loadIcon(controlName);
// If the icon doesn't exist, it is set to a default so this code is safe
setOpenIcon(controlIcon);
setClosedIcon(controlIcon);
// Stop here so we don't just set the default icons again
return;
}
setOpenIcon(getDefaultOpenIcon());
setClosedIcon(getDefaultClosedIcon());
}
/**
*
*
* #param iconName
* #return
*/
private static final void loadIcon(String iconName)
{
URL url = XMLDialogTreeCellRenderer.class.getResource("/com/phabrix/resources/dialogTreeIcons/" + iconName + ".png");
if (url == null)
{
// Tell the developer that they need to make a new icon for a new control type
if (Main.DEBUG && !iconName.equals("Dialogs"))
System.out.println("There is no icon for the control named: " + iconName);
url = XMLDialogTreeCellRenderer.class.getResource("/com/phabrix/resources/dialogTreeIcons/_not_found.png");
}
icons.put(iconName, new ImageIcon(Toolkit.getDefaultToolkit().getImage(url)));
}
}
So a simple answer; this code:
// Try and get a pre-loaded icon
Icon controlIcon = icons.get(controlName);
// It wasn't there, try loading it
if (controlIcon == null)
loadIcon(controlName);
became this code:
// Try and get a pre-loaded icon
Icon controlIcon = icons.get(controlName);
// It wasn't there, try loading it
if (controlIcon == null)
{
loadIcon(controlName);
controlIcon = icons.get(controlName);
}
Note that upon checking to see if our Icon variable is null, and then making sure an Icon has been loaded into the program, I now actually make sure that the Icon variable is updated so it is no longer null.
The affected Nodes in my tree were not random or fluctuating as I previously thought. It was always the first node to be drawn with each of the icons (different depending on the order I opened up the tree). I am loading each icon once, then caching it (the tree is huge and re-loading the same icon many times was a big performance hit). Essentially the node was created with just text, then had an icon set after being shown, which shunted the text and caused the end to be cut off. As above, the solution was to not forget to set the icon after loading it for the first time. A better solution would be for my icons.get() to be wrapped in such a way as to guarantee an icon was returned and to encapsulate the cache optimisation.
I found some details on the internet for this problem but the solution does not seem to work.
I want to be able to expand all the nodes ( including the leaf nodes ) of a JTree. This is the code I have so far.
tree = new JTree(treeModel);
tree.setShowsRootHandles(true);
tree.setEditable(true);
tree.addTreeSelectionListener(treeSelectionListener);
tree.addMouseListener(mouselistener);
tree.setCellRenderer(new FileCellRenderer());
for (int i = 0; i < tree.getRowCount(); i++) {
tree.expandRow(i);
}
This however does not expand all the leaf nodes. What I get is IMAGE A but what I want is IMAGE B:
Use a recursive call, as in the following sample:
private void expandAllNodes(JTree tree, int startingIndex, int rowCount){
for(int i=startingIndex;i<rowCount;++i){
tree.expandRow(i);
}
if(tree.getRowCount()!=rowCount){
expandAllNodes(rowCount, tree.getRowCount());
}
}
I know this question has been asked before in a similar way, maybe for icons.
What I'm trying is to change the color of the text of the tree node.
In fact, I have a jTree and I will want to set up three differents colors, default one, red and orange.
The purposse, is that if I compare that tree with another one, highlight differences between both trees (default means no diff, orange means just value diff and red means node is complete different)
I have two functions, one which trasverse the "original" tree looking for a node from the compared one, and returns false if {node} is not found:
private Boolean findNodeInRefTree(DefaultTreeModel model, Object root, DefaultMutableTreeNode node){
Boolean bRet = false;
for (int i = 0; ((i < model.getChildCount(root))&&(!bRet)); i++){
DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(root, i);
bRet = node.getUserObject().equals(child.getUserObject());
if (!bRet)
bRet = findNodeInRefTree(model, child, node);
}//for:i
return bRet;
}
And another function that trasverse the "compare" tree and calls the above for each node.
private void compareTrees(TreeModel model, Object root){
for (int i = 0; i < model.getChildCount(root); i++){
DefaultMutableTreeNode child = (DefaultMutableTreeNode) model.getChild(root, i);
//find if node exists in original
DefaultTreeModel modelRef = (DefaultTreeModel) _ref.getModel();
if (!findNodeInRefTree(modelRef, modelRef.getRoot(), child)){
DefaultTreeCellRenderer render = (DefaultTreeCellRenderer) _temp.getCellRenderer();
render.setForeground(Color.RED);
_temp.setCellRenderer(render);
}//fi
_new.insertNodeInto((DefaultMutableTreeNode) child, (DefaultMutableTreeNode) root, i);
compareTrees(model, child);
}//for:i
}
Then, when it ends I just set the model of the new tree {_new} to the new tree {_temp}, and add the tree to its panel. But the tree doesn't has any different color. Obviously, I'm testing with different trees. Any suggestion?
If I understand your code correctly, your do the comparison at creation time and set the renderer for each tree node (i.e. multiple times) inside method compareTrees.
Unfortunately, that is not the way tree renderers are handled in swing. The renderer is prepared on request during rendering the tree component. Thus setting multiple renderer beforehand won't do anything useful.
A possible approach would be to do the comparison and save the result (i.e. color) in your tree model. You can then write a basic tree renderer which reads this value for the current node and sets the rendering color accordingly.
I have a ftp program that retrieve folder data each time expanded. It does this by using a model like this:
private void FilesTreeTreeExpanded(javax.swing.event.TreeExpansionEvent evt) {
String path = new String("");
DefaultMutableTreeNode chosen = (DefaultMutableTreeNode) evt.getPath().getLastPathComponent();
String[] pathArray = evt.getPath().toString().replaceAll("]", "").split(",");
for (int i = 1 ; i < pathArray.length ; i++) path += "/"+ pathArray[i].trim();
// i were aded chosen.removeAllChildren(); without success
ftp.GoTo(path);
ArrayList listDir = null;
listDir = ftp.ListDir();
ArrayList listFiles = null;
listFiles = ftp.ListFiles();
DefaultMutableTreeNode child = null , dir = null , X = null;
//this will add files to tree
for (int i = 0; i < listFiles.size(); i++) {
child = new DefaultMutableTreeNode(listFiles.get(i));
if(listFiles.size() > 0)
model.insertNodeInto(child, chosen, 0);
}
//this will add dirs to list
for (int i = 0; i < listDir.size(); i++) {
X = new DirBranch("در حال دریافت اطلاعات ...").node();
dir = new DirBranch( (String) listDir.get(i)).node();
dir.add(X);
if(listDir.size() > 0)
model.insertNodeInto(dir, chosen, 0);
}
FilesTree.setModel(model); //this is my Swing JTree
}
the problem is every time i expand the JTree it duplicate list of files and folders. so i tried to use chosen.removeAllChildren(); # the top of the code but it didnt remove anything. what should i do?
Your model is correct, but JTree is operating on old information.
The removeAllChildren() method removes the children, but it does not fire any events and the model.insertNodeInto() does fire insert events. So, JTree sees the nodes being added, but never sees the nodes being removed.
After adding the new children, try calling model.reload(chosen) to invalidate the tree below chosen.
Since you will be reloading the branch, you can also change model.insertNodeInto(dir, chosen,0) to chosen.insert(dir,0). That reduces the number of events posted.
Calling removeAllChildren() will remove the children from the node. There must be something else happening here that is creating duplicates. Make sure you are not calling anything twice and that you are refreshing the display of the tree.
In my application also i just fact the same problem. For that i just used the following code.
JTree.removeAll();
JTree.setModel(null);
It remove all child nodes from my Jtree.