6

I have a JTree with a custom TreeModel and a custom TreeRenderer. The Tree Model contains a bunch of objects of different types. One of these types is displayed differently than the others: The displayed text is a concatenation of two fields of the object. When i edit the cell, I want to update one of these fields with the edited text. So far i got it working pretty well.

My Problem: It is confusing when the text, which is displayed while editing, is the complete concatenated value of 2 fields, even though you're in fact just editing one of the fields. So i want to display only the content of the one field you're editing when the user starts editing.

I tried to do this with a custom CellEditor, and I saw the way it's supposed to work and the approach seems overkill in my case. I just want to alter the displayed text in one of many cases, so naturally I want to implement that, and not a whole CellEditor for the entire content of my Tree.

Is there a faster and easier way to do this, or do I have to use a custom editor?

Thanks

Desolate Planet
  • 561
  • 5
  • 19
Simiil
  • 2,281
  • 1
  • 22
  • 32

2 Answers2

9

There is no way around a custom editor and it is the shortest solution possible :-) Additionally, you'll need some means in the data realm that can interpret the editing value as appropriate and update itself, f.i. a custom node.

F.i (will comment it later, my lame firefox on this machine is driving me up the walls)

/**
 * Basic code stolen from @trashgod at
* @see http://stackoverflow.com/a/11113648/230513
*/
public class TreeEditDemo extends JPanel {

    private JTree tree;
    private DefaultMutableTreeNode root;
    private DefaultTreeCellEditor editor;

    public TreeEditDemo() {
        super.setLayout(new GridLayout());
        root = new DefaultMutableTreeNode("Nodes");
        root.add(new MyResourceNode(new Resource("one", "first")));
        root.add(new MyResourceNode(new Resource("two", "first")));
        tree = new JTree(root);
        tree.setEditable(true);
        editor = new MyTreeCellEditor(tree,
            (DefaultTreeCellRenderer) tree.getCellRenderer());
        tree.setCellEditor(editor);
        this.add(new JScrollPane(tree));
    }

    private static class MyTreeCellEditor extends DefaultTreeCellEditor {

        public MyTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
            super(tree, renderer);
        }

        @Override
        public Component getTreeCellEditorComponent(JTree tree, Object value,
                boolean isSelected, boolean expanded, boolean leaf, int row) {
            if (value instanceof MyResourceNode) {
                value = ((MyResourceNode) value).getName();
            }
            return super.getTreeCellEditorComponent(tree, value, isSelected, expanded,
                    leaf, row);
        }

        @Override
        public boolean isCellEditable(EventObject e) {
            return super.isCellEditable(e)
                && ((TreeNode) lastPath.getLastPathComponent()).isLeaf();
        }
    }

    public static class MyResourceNode extends DefaultMutableTreeNode {

        /**
         * @param resource
         */
        public MyResourceNode(Resource resource) {
            super(resource);
        }

        @Override
        public void setUserObject(Object userObject) {
            if (userObject instanceof String) {
                setName((String) userObject);
            } else if (userObject instanceof Resource) {
                super.setUserObject(userObject);
            }    
        }

        public void setName(String name) {
            if (getUserObject() != null) {
                getUserObject().setName(name);
            }
        }

        public String getName() {
            if (getUserObject() != null) {
                return getUserObject().getName();
            }
            return null;
        }

        @Override
        public Resource getUserObject() {
            return (Resource) super.getUserObject();
        }


    }
    private static class Resource {

        String name;
        private String category;

        public Resource(String name, String category) {
            this.name = name;
            this.category = category;
        }

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

        public String getName() {
            return name;
        }
        @Override
        public String toString() {
            // BEWARE: don't do this in production code!
            return name + " (" + category + ")";
        }
    }

    private void display() {
        JFrame f = new JFrame("TreeEditorDemo");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(this);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TreeEditDemo().display();
            }
        });
    }
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • +1 for the more reliable approach. Might a `CellEditorListener` be a suitable alternative? – trashgod Sep 29 '12 at 15:39
  • good question - tend to not recommend it because that would compete with the standard listeners (in tree or table) – kleopatra Sep 29 '12 at 15:41
  • Hello. I'm new to swing. What should "lastPath" be set to? – null Apr 23 '13 at 03:25
  • @hal9000 the setting is done in super.isCellEditable :-) Though you might hit a slight glitch: it's set only if the event is of type MouseEvent, not for Keyboard or others ... need to dig one of these days - thanks for the heads up! After all, no glitch: the property is updated in a selectionListener - what a vivid beast, the DTreeCR, taking waaayyy too much tasks .. – kleopatra Apr 23 '13 at 08:22
  • @kleopatra Sorry, I'm still missing something. Shere is "lastPath" defined? – null Apr 24 '13 at 04:25
  • @hal9000 lastpath is the path to the row: either one clicked in isCellEditable or the last selected if the event is not a mouseEvent - have a look at the source of super, that is DTreeCR – kleopatra Apr 24 '13 at 08:26
  • @kleopatra thank you, I've been thoroughly mystified by how to do this kind of thing (very common requirement). A great how-to solution. One thing: the "lastPath" business complicates things a bit... you could just return true from isCellEditable (for demo purposes). – mike rodent Apr 26 '14 at 19:15
3

A custom editor is required. MyTreeCellEditor, illustrated here, shows one approach. It updates an arbitrary attribute of a userObject, which is named Resource and held in a DefaultMutableTreeNode. As the attribute is text, it also uses the DefaultTreeCellRenderer. Your custom TreeModel probably manages a similar userObject that is the parent of your "objects of different types."

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045