Link to home
Create AccountLog in
Avatar of mock5c
mock5c

asked on

Changing node text color when member data changes

I've attached some code for your review.  Basically, I have a JTree which I have popup menus for the nodes.  When the node is right-clicked, the user can select "mark/unmark".  When they do this, the MyTreeNode member boolean uploadStatus is changed to true or false.  On change, I would like to change the text color of that node.

My first approach was this line:

      this.setCellRenderer(new MyTreeCellRenderer(mytn));

but found that I'm getting very odd results such as the root node changing color or a different sibling node changing.  Then I realized that that perhaps I should not be doing a setCellRenderer() and instead should invoking treeNodesChanged(TreeModelEvent e).  Can you provide sample code for doing this?

By treeNodesChanges, does that include the node's  member data such as boolean uploadStatus changing value?


class MyJTree extends JTree implements ActionListener
{
  private MyTreeModel treeModel;

  JPopupMenu popup;
  JMenuItem mi;

  MyJTree(MyTreeModel mytm)
  {
    super(mytm);

    treeModel = mytm;

    // define the popup
    popup = new JPopupMenu();

    mi = new JMenuItem("Mark/Unmark upload");
    mi.addActionListener(this); 
    mi.setActionCommand("upload");
    popup.add(mi);
    popup.setOpaque(true);
    popup.setLightWeightPopupEnabled(true);

    addMouseListener(
      new MouseAdapter() {
        public void mouseReleased(MouseEvent e) {
          if (e.isPopupTrigger()) {
            popup.show((JComponent)e.getSource(), e.getX(), e.getY());
          }       
        }       
      }
    );
  }

  public void actionPerformed(ActionEvent ae) {
    MyTreeNode mytn, node;

    TreePath path = this.getSelectionPath();
    mytn = (MyTreeNode) path.getLastPathComponent();
    if (ae.getActionCommand().equals("upload"))
    {
      System.out.println("Upload status before = " + mytn.getUploadStatus());
      if (mytn.getUploadStatus()) {
        mytn.setUploadStatus(false);
      }
      else {  
        mytn.setUploadStatus(true);
      }

      System.out.println("Upload status after = " + mytn.getUploadStatus());

      this.setCellRenderer(new MyTreeCellRenderer(mytn));

    }
  }
}
//--------------------
class MyTreeCellRenderer extends DefaultTreeCellRenderer {

  private MyTreeNode m_node;

  MyTreeCellRenderer(MyTreeNode node)
  {
    m_node = node;
  }

  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);
    MyTreeNode mytn = (MyTreeNode) value;

    if (mytn.isLeaf() && mytn.getUploadStatus()) {
        setTextNonSelectionColor(Color.blue);
    }
    else {
        setTextNonSelectionColor(Color.black);
    }

    return this;
  }
}
//---------------------

...
    tree = new MyJTree(treeModel);

    treeModel.addTreeModelListener(new TreeModelListener()
      {
        public void treeNodesChanged(TreeModelEvent e)
        {
          DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) tree.getCellRenderer();
          MyTreeCellRenderer renderer = (MyTreeCellRenderer) tree.getCellRenderer();
          System.out.println("in treeNodeChanged.");
          renderer.setTextNonSelectionColor(Color.blue);
        }
        public void treeNodesInserted(TreeModelEvent e)
        {
        }
        public void treeNodesRemoved(TreeModelEvent e)
        {
        }
        public void treeStructureChanged(TreeModelEvent e)
        {
        }
      }
    );
...

Open in new window

Avatar of Mick Barry
Mick Barry
Flag of Australia image

you only need to set the node renderer once when you setup the tree.
you don't need to call it everytime

and you don't need that TreeModelListener
and yes you need to fire an eventIf you're using the DefaultTreeModel you can call the nodeChanged() method

model.nodeChanged(node);

http://download.oracle.com/javase/6/docs/api/javax/swing/tree/DefaultTreeModel.html#nodeChanged(javax.swing.tree.TreeNode)
Avatar of mock5c
mock5c

ASKER

I had already tried treeModel.nodeChanged(mytn);

I had this line before:

this.setCellRenderer(new MyTreeCellRenderer(mytn));

in the actionPerformed code I pasted.

Is this nodeChanged line in the wrong place?  I figured it would be called after mytn.setUploadStatus() was called.

You mentioned I don't need TreeModelListener.  But isn't this invoked when I call nodeChanged()?
> Is this nodeChanged line in the wrong place?  I figured it would be called after mytn.setUploadStatus() was called.

yes thats the right place

> You mentioned I don't need TreeModelListener.  But isn't this invoked when I call nodeChanged()?

it is, but theres nothing you need to do
the tree will get notified of the change and repaint the cell (using your renderer)
Avatar of mock5c

ASKER

hmmm, I'm still getting odd behavior.  I would expect that the node I right-clicked on and marked (uploadStatus got set to true) would change color.  Instead, I see the sibling node change color and then just clicking on different nodes and not marking them results in the root node and other nodes changing color.
Avatar of mock5c

ASKER

A link I found (http://www.thatsjava.com/java-swing/4572/) describes a similar method that I'm trying to do.  I do not want to do drag and drop, I just want to do some action (e.g. change node text color) when the node uploadStatus value is changed to true.  This should be for specific nodes.  The link above provides some great suggestions but there is no response to the last post which says that the modifications to the tree are made but to ALL nodes of the tree.  Unfortunately, there is no further solution provided.

In my case, not all nodes are being modified but the specific node is not being modified, only sibling and ancestor nodes.
what you are doing should work. If you can put together a small example I could run that demonstrates the problem hen  I'll have a look at it for you
Avatar of mock5c

ASKER

I'll put together a small example.  In the meantime, can you look at the new code I'm posting?   This is the TreeModelListener.  I know when I call treeModel.nodeChanged(mytn), it will enter this listener.  Now I can see with the print statements that it actually prints the parent node's name of the node I selected (right clicked and marked for upload).  How do I make sure it's the node I selected?

Also, is it correct that I getCellRenderer() in this listener so that I can change the node text color when this listener is invoked via nodeChanged()?
treeModel.addTreeModelListener(new TreeModelListener() 
      {
        public void treeNodesChanged(TreeModelEvent e)
        {       
          MyTreeNode node = (MyTreeNode) e.getTreePath().getLastPathComponent();

          System.out.println("In treeNodeChanged ...");
          System.out.println("Node name = " + node.getNodeName());

          if (node.isLeaf() && node.getUploadStatus()) {
            node.setNodeName("new name"); 
        
            MyTreeCellRenderer renderer = (MyTreeCellRenderer) tree.getTreeCellRenderer();
        
            renderer.setTextNonSelectionColor(Color.blue);
          }       
        }       
        public void treeNodesInserted(TreeModelEvent e)
        {       
        }       
        public void treeNodesRemoved(TreeModelEvent e)
        {       
        }       
        public void treeStructureChanged(TreeModelEvent e)
        {       
        }       
      }
    );

Open in new window

>             MyTreeCellRenderer renderer = (MyTreeCellRenderer) tree.getTreeCellRenderer();
    >             renderer.setTextNonSelectionColor(Color.blue);

you shouldn't be doing that
Avatar of mock5c

ASKER

Here's some code that should be sufficient to demonstrate my problem.
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.event.*;
import java.awt.*;
import java.util.*;
import javax.swing.event.*;

public class TreeWithPopup extends JPanel {
    
    MyTreeNode root, node1, node2, node1a, node1b, node2a, node2b;
    DefaultTreeModel treeModel = null;
    
    public TreeWithPopup() {
        MyJTree tree;
        root = new MyTreeNode("root","root");
        node1 = new MyTreeNode("node 1","branch");
        node1a = new MyTreeNode("node 1a","leaf");
        node1b = new MyTreeNode("node 1b","leaf");
        node2 = new MyTreeNode("node 2","branch");
        node2a = new MyTreeNode("node 2a","leaf");
        node2b = new MyTreeNode("node 2b","leaf");
        root.add(node1);
        node1.add(node1a);
        node1.add(node1b);
        root.add(node2);
        node2.add(node2a);
        node2.add(node2b);
        setLayout(new BorderLayout());
        treeModel = new DefaultTreeModel(root);
        tree = new MyJTree(treeModel);

        tree.setCellRenderer(new MyTreeCellRenderer(root));

         treeModel.addTreeModelListener(new TreeModelListener()
           {
             public void treeNodesChanged(TreeModelEvent e)
             {
                // there's nothing in here for when nodeChanged(node) is called!
                System.out.println("In treeNodesChanged");
             }
             public void treeNodesInserted(TreeModelEvent e)
             {
             }
             public void treeNodesRemoved(TreeModelEvent e)
             {
             }
             public void treeStructureChanged(TreeModelEvent e)
             {
             }
           }
         );

        add(new JScrollPane(tree),"Center");
    }
    
    // Size of the window
    public Dimension getPreferredSize(){
        return new Dimension(300, 300);
    }
    
    public static void main(String s[]){
        JFrame frame = new JFrame("Tree With Popup");
        TreeWithPopup panel = new TreeWithPopup();
        
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.setForeground(Color.black);
        frame.setBackground(Color.lightGray);
        frame.getContentPane().add(panel,"Center");
        
        frame.setSize(panel.getPreferredSize());
        frame.setVisible(true);
        frame.addWindowListener(new WindowCloser());
    }
}

class WindowCloser extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
        Window win = e.getWindow();
        win.setVisible(false);
        System.exit(0);
    }
}

class MyJTree extends JTree implements ActionListener{

    private DefaultTreeModel treeModel;

    JPopupMenu popup;
    JMenuItem mi;
    
    MyJTree(DefaultTreeModel dtm) {
        super(dtm);

        treeModel = dtm;

        // define the popup
        popup = new JPopupMenu();

        mi = new JMenuItem("mark/unmark");  
        mi.addActionListener(this);
        mi.setActionCommand("upload");
        popup.add(mi);
        popup.setOpaque(true);
        popup.setLightWeightPopupEnabled(true);
        
        addMouseListener(
                new MouseAdapter() {
            public void mouseReleased( MouseEvent e ) {
                if ( e.isPopupTrigger()) {
                    popup.show( (JComponent)e.getSource(), e.getX(), e.getY() );
                }
            }
        }
        );
    }

    public void actionPerformed(ActionEvent ae) {
        MyTreeNode mytn;
        
        TreePath path = this.getSelectionPath();
        mytn = (MyTreeNode) path.getLastPathComponent();
        if (ae.getActionCommand().equals("upload")) {
            System.out.println("Upload status before = " + mytn.getUploadStatus());
            if (mytn.getUploadStatus()){
                mytn.setUploadStatus(false); // was true, make it false
            }
            else{
                mytn.setUploadStatus(true); // make it true
            }
            System.out.println("Upload status after = " + mytn.getUploadStatus());

            treeModel.nodeChanged(mytn);
        }
    }
}

class MyTreeNode implements TreeNode
{

  private String type = null;
  private String nodeName = null;
  private ArrayList<TreeNode> children = new ArrayList();
  private TreeNode parent = null;
  private String nodeId = null;
  private boolean uploadStatus = false;

  public MyTreeNode(String name, String nodetype)
  {
    nodeName = name;
    type = nodetype;
  }

  public void setNodeId(String nodeId)
  {
    this.nodeId = nodeId;
  }

  public String getNodeId()
  {
    return nodeId;
  }

  public void setType(String type)
  {
    this.type = type;
  }

  public String getType()
  {
    return this.type;
  }


  public void setNodeName(String name)
  {
    this.nodeName = name;
  }

  public String getNodeName()
  {
    return this.nodeName;
  }

  public void add(MyTreeNode node)
  {
    if (children == null)
    {
      children = new ArrayList();
    }
    System.out.println("adding :" + node.getNodeName() + " to " + nodeName);
    children.add(node);
    System.out.println("Child count for :" + nodeName + " is now: " + children.size());
  }

  public void setParent(TreeNode parent)
  {
    this.parent = parent;
  }

  public Enumeration children()
  {
    Enumeration e = Collections.enumeration(children);
    System.out.println("children() :" + nodeName + " : " + e);
    return e;
  }

  public boolean getAllowsChildren()
  {
    System.out.println("getAllowsChildren() :" + nodeName);
    boolean retVal = true;

    if (type.equals("leaf"))
    {
      retVal = false;
    }     

    return true;
  }

  public TreeNode getChildAt(int childIndex)
  {
    System.out.println("getChildAt() :" + nodeName);
    TreeNode retVal = null;

    if (childIndex < children.size() && childIndex >= 0)
    {
      retVal = children.get(childIndex);
    }

    return retVal;
  }

  public int getChildCount()
  {
    System.out.println("getChildCount() :" + nodeName + " : " + children.size());
    int retVal = 0;
    
    if (children != null)
    {
      retVal = children.size();
    }

    return retVal;
  }

  public int getIndex(TreeNode node)
  {
    System.out.println("getIndex() :" + nodeName);
    int retVal = -1;

    if (children != null)
    {
      for (int i=0; i < children.size(); i++)
      {
        TreeNode childNode = children.get(i);
        if (childNode == node)
        {
          retVal = i;
          break;
        }
      }
    }  
 
    return retVal;
  }

  public TreeNode getParent()
  {
    System.out.println("getParent() :" + nodeName);
    return parent;
  }

  public boolean isLeaf()
  {
    boolean retVal = false;

    if (type.equals("leaf"))
    {
      retVal = true;
    }

    System.out.println("isLeaf() :" + nodeName + " : " + retVal);
    return retVal;
  }

  public void setUploadStatus(boolean status)
  {
    this.uploadStatus = status;
  }

  public boolean getUploadStatus()
  {
    return this.uploadStatus;
  }

  public String toString()
  {
    return this.nodeName;
  }
}

class MyTreeCellRenderer extends DefaultTreeCellRenderer
{
  private MyTreeNode m_node;

  MyTreeCellRenderer(MyTreeNode node)
  {
    m_node = node;
  }

  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);
    MyTreeNode mytn = (MyTreeNode) value;

    System.out.println("In MyTreeCellRenderer");

    //if (mytn.isLeaf() && mytn.getUploadStatus()) {
    if (mytn.getUploadStatus()) {
        setTextNonSelectionColor(Color.blue);
    }
    else {
        setTextNonSelectionColor(Color.black);
    }

    return this;
  }
}

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of Mick Barry
Mick Barry
Flag of Australia image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
Avatar of mock5c

ASKER

That worked.  Such a small change and something I did not understand clearly.

If you don't mind me asking, when would the TreeModelListener() treeNodeChanged(TreeModelEvent e) be used?  Is that when I don't have my own renderer?
its used when you want to take some action (excluding updating your tree) when the data in your tree changes.
Avatar of mock5c

ASKER

OK, thanks for your help.