2

I have a JTree that behaves like so:

  • The root has a user object of type RootObject; it uses a plaintext label and is static throughout the lifespan of the tree.
  • Each child has a user object of type ChildObject, which may be in one of three states: not running, running, or finished.
  • When the ChildObject is not running, it's a plaintext label.
  • When the ChildObject is running, it uses an icon resource and switches to HTML rendering so the text is in italics.
  • When the ChildObject has finished, it uses a different icon resource and uses HTML rendering to show the text in bold.

Currently, my code looks like so:

public class TaskTreeCellRenderer extends DefaultTreeCellRenderer {
    private JLabel label;

    public TaskTreeCellRenderer() {
        label = new JLabel();
    }

    public Component getTreeCellRendererComponent(JTree tree,
           Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
        Object nodeValue = ((DefaultMutableTreeNode) value).getUserObject();

        if (nodeValue instanceof RootObject) {
            label.setIcon(null);
            label.setText(((RootObject) nodeValue).getTitle());
        } else if (nodeValue instanceof ChildObject) {
            ChildObject thisChild = (ChildObject) nodeValue;

            if (thisChild.isRunning()) {
                label.setIcon(new ImageIcon(getClass().getResource("arrow.png")));
                label.setText("<html><nobr><b>" + thisChild.getName() + "</b></nobr></html>");
            } else if (thisChild.isComplete()) {
                label.setIcon(new ImageIcon(getClass().getResource("check.png")));
                label.setText("<html><nobr><i>" + thisChild.getName() + "</i></nobr></html>");
            } else {
                label.setIcon(null);
                label.setText(thisChild.getName());
            }
        }

        return label;
    }
}

For the most part, this renders fine. The initial tree renders fine with the labels using plaintext. The problem is once the ChildObject instances start changing state, the JLabels update to use HTML rendering, but don't resize to compensate for the text or icon. For example:

Initial state:
http://imageshack.us/a/img14/3636/psxi.png

In progress:
http://imageshack.us/a/img36/7426/bl8.png

Finished:
http://imageshack.us/a/img12/4117/u34l.png

Any ideas where I'm going wrong? Thanks in advance!

Jesse
  • 253
  • 1
  • 3
  • 12
  • 1
    Looks like a wrong update of the tree. After you change the icons you must tell the tree that the model is changed. – Sergiy Medvynskyy Oct 16 '13 at 17:19
  • `label.setText("..` Try it **without** HTML. For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Oct 16 '13 at 17:29
  • maybe related (or not, no way to tell without a SSCCE): your renderer looks fishy - it _extends_ default (aka: is-a label), has-a label field, re-create new labels in two of the if-blocks and returns the field ... Anyway, my bet is the same as @SergiyMedvynskyy - most probably incorrect notification on change – kleopatra Oct 16 '13 at 17:35
  • @Kleopatra: That was mostly just me trying different ideas on fixing the problem. The original implementation did not create new JLabels in the if clauses and still exhibited the issue. I've edited the code to reflect that. – Jesse Oct 16 '13 at 18:48

1 Answers1

2

So you need to tell the tree model that the content is changed. Each time if the status of your ChildObject is changed you must do following:

((DefaultTreeModel)tree.getModel()).reload(node);

Where node is the DefaultMutableTreeNode, which contains the changed ChildObject.

Don't forget to use SwingUtilities.invokeLater() if the Status of child object is changed outside of the Swing-Thread (EDT).

Sergiy Medvynskyy
  • 11,160
  • 1
  • 32
  • 48
  • ... probably not: reload is needed only if the change is very scattered and the more focused events don't fit. – kleopatra Oct 16 '13 at 17:39
  • That was it! In retrospect it seems obvious, but it didn't occur to me at the time. Thanks! I should mention that instead of using reload(node) I used the standard reload() method, since my program isn't aware of which ChildObjects change their state. It probably could, but that's for a later refactoring. – Jesse Oct 16 '13 at 18:56