JTree single node foreground - java

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.

Related

Alternative to manually creating each node in a game tree? (java)

I'm working on building a partial tree for Monte Carlo Search Tree, I have my node class and it contains things like the game board, who's turn it is, and a list of Nodes for the children nodes. The node constructor takes in a game board and the depth (of the tree) as the parameters. For the root node I call it like so:
Node root = new Node(this.quartoBoard, currentDepth);
(Where depth is 0) that works fine, however, when depth 1, which will be the children of the root node needs to contain 32 nodes. I naively tried this:
for(int i = 0; i < NUMBER_OF_PIECES; i++) {
Node c1 = new Node(this.quartoBoard, currentDepth);
c1.setParent(root);
childrenList1.add(c1);
}
And realized that Nodes can not be created in a for loop like this. Is there an alternative to declaring each node one at a time like so:
Node child1 = new Node(this.quartoBoard, currentDepth);
Node child2 = new Node(this.quartoBoard, currentDepth);
I need to create over 800 nodes and I feel like there is a better way to do it, I just am drawing blanks on how. Any help is greatly appreciated!

Converting a 2-3-4 tree into a red black tree

I'm trying to convert a 2-3-4 Tree into a Red-Black tree in java, but am having trouble figuring it out.
I've written these two basic classes as follows, to make the problem straightforward, but can't figure out where to go from here.
public class TwoThreeFour<K> {
public List<K> keys;
public List<TwoThreeFour<K>> children;
}
public class RedBlack<K> {
public K key;
public boolean isBlack;
public RedBlack<K> left,right;
public RedBlack<K key, boolean isBlack, RedBlack<K> left, RedBlack<K> right){
this.key = key; this.isBlack = isBlack; this.left = left; this.right = right;
}
}
I'm assuming the 2-3-4 tree is valid, and want to return a red black tree when the method is called.
I've also tried the following code with no luck:
public convert(TwoThreeFour<K> tTF){
if (ttf.keys.size() == 3)
RedBlack<K> node = RedBlack<ttf.keys[1], true, RedBlack<ttf.keys[0], false, /* not sure what to put here for left */, /* not sure what to put here for right */), RedBlack<ttf.keys[2], false, /* not sure what to put here for left */, /* not sure what to put here for right */)
etc. for keys.size() == 2, 1....
I know it has to be recursive in theory, but am having a hard time figuring it out. Any thoughts?
Consider these three rules:
Transform any 2-node in the 2-3-4 tree into a black node in the
red-black tree.
Transform any 3-node into a child node and a parent node. The
child node has two children of its own: either W and X or X and Y.
The parent has one other child: either Y or W. It doesn’t matter
which item becomes the child and which the parent. The child is
colored red and the parent is colored black.
Transform any 4-node into a parent and two children, the first
child has its own children W and X; the second child has children Y
and Z. As before, the children are colored red and the parent is
black.
The red-black rules are automatically satisfied if you follow these rules. Here's the resulting example tree after applying the transformations.
Hopefully that should get you going. For easy to understand and detailed explanation, you can refer to Robert Lafore's Data Structures book.

How to get the index of the selected node in a selcted level in a JTree

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();

What is causing arbitrary JTree Nodes to incorrectly display their names and how can I stop it from happening?

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.

DefaultMutableTreeNode-Text is too long?

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

Categories

Resources