In-place Editing in Eclipse TreeViewer

In an Eclipse RCP application I was working on recently, I had to implement a TreeViewer with in-place editing feature. It was not easy to find all the information required to implement this, so I thought I would explain it here.

First let’s take a simple example, where hierarchical data is displayed in  a tree.
tree_viewer1

When any item in the tree is double clicked, I want to edit the value in-place.

Here is the code to create this tree, without editing support. I will first create a data model using a Map. To simplify the example, I am assuming only one level of hierarchy.

//Class to represent each node of the tree
class TreeItemData
{
    String value;
    public TreeItemData (String value)
    {
        this.value = value;
    }

    public String toString()
    {
        return value;
    }
}

Map<TreeItemData,TreeItemData[]> treeModelData = new HashMap<>();

treeModelData.put(new TreeItemData("parent1"), new TreeItemData[]{
    new TreeItemData("child1"),
    new TreeItemData("child2")
    });

Then create an instanceof TreeViewer and its content provider.

TreeViewer treeViewer = new TreeViewer(shell);

treeViewer.setContentProvider(new ITreeContentProvider() {

    @Override
    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    }

    @Override
    public void dispose() {
    }

    @Override
    public boolean hasChildren(Object element) {
        if (treeModelData.containsKey(element))
            return true;
        return false;
    }

    @Override
    public Object getParent(Object element) {
        return null;
    }

    @Override
    public Object[] getElements(Object inputElement) {
        if (inputElement.equals("initial_input"))
            return treeModelData.keySet().toArray();
        return null;
    }

    @Override
    public Object[] getChildren(Object parentElement) {
        parentElement.toString();
        Object value = treeModelData.get(parentElement);
        if (value != null)
            return (TreeItemData[])value;
        return null;
    }
});

//set initial input
treeViewer.setInput("initial_input");

The above code will create a TreeViewer with tree nodes, but with no editing support.

To trigger editing in Tree cells on double click, we need to create TreeViewerEditor.

TreeViewerEditor.create(treeViewer, new ColumnViewerEditorActivationStrategy(treeViewer){
    protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
        return event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION;
    }

There are two ways to add editing support for cells in the tree. 1)Using setCellEditors method of TreeViewer, and 2)Using ViewerColumn.setEditingSupport API. Eclipse documentation says that the second API is available since Eclipse 3.3 and it is more flexible.

First I will show how to use setCellEditors API

private void enableEditing1(TreeViewer treeViewer)
{
    //You have to create identifier for tree columns
    treeViewer.setColumnProperties(new String[]{"col1"});
    treeViewer.setLabelProvider(new TreeLabelProvider());
    //Create text editor
    treeViewer.setCellEditors(new CellEditor[]{new TextCellEditor(treeViewer.getTree())});
    treeViewer.setCellModifier(new ICellModifier() {

        @Override
        public void modify(Object element, String property, Object value) {
            if (element instanceof TreeItem)
            {
                //update element and tree model
                TreeItem treeItem = (TreeItem)element;
                TreeItemData data = (TreeItemData)treeItem.getData();
                data.value = value.toString();
                treeItem.setText(value.toString());
            }
        }

        @Override
        public Object getValue(Object element, String property) {
            return element.toString();
        }

        @Override
        public boolean canModify(Object element, String property) {
            return true;
        }
    });
}
class TreeLabelProvider extends ColumnLabelProvider
{
    public String getText(Object element) {
        return element.toString();
    }
}

Alternatively, here is the code to add editing support using ViewerColumn.setEditingSupport API.

private void enableEditing2 (TreeViewer treeViewer)
{
    final TreeViewerColumn column = new TreeViewerColumn(treeViewer, SWT.NONE);
    column.getColumn().setWidth(150);
    column.setLabelProvider(new TreeLabelProvider());
    final TreeViewer finalTreeViewer = treeViewer;
    final TextCellEditor cellEditor = new TextCellEditor(treeViewer.getTree());

    column.setEditingSupport(new EditingSupport(treeViewer) {

        @Override
        protected void setValue(Object element, Object value) {
            if (element instanceof TreeItemData)
            {
                TreeItemData data = (TreeItemData)element;
                data.value = value.toString();
            }
            finalTreeViewer.update(element, null);
        }

        @Override
        protected Object getValue(Object element) {
            return element.toString();
        }

        @Override
        protected TextCellEditor getCellEditor(Object element) {
            return cellEditor;
        }

        @Override
        protected boolean canEdit(Object element) {
            return true;
        }
    });

    //To set width of the column to tree width
    treeViewer.getControl().addControlListener(new ControlListener() {

        @Override
        public void controlResized(ControlEvent e) {
            column.getColumn().setWidth(((Tree)e.getSource()).getBounds().width);
        }

        @Override
        public void controlMoved(ControlEvent e) {
        }
    });
}

Note that you have to specify the width of the table cell column, else it would be set to zero. If you want to set column width equal to the width of the tree then you can do that in the ControlListener, as shown in the above code.

Both above methods will let you edit values in TreeViewer by double clicking on tree items.

tree_viewer2

The default text box created by TextCellEditor is without borders. You can customize this text box by extending TexCellEditor. The following class adds border to the text box and sets it height to 10 pixels more than the font height.

class MyTextCellEditor extends TextCellEditor
{
    int minHeight = 0;

    public MyTextCellEditor(Tree tree) {
        super(tree, SWT.BORDER);
        Text txt = (Text)getControl();

        Font fnt = txt.getFont();
        FontData[] fontData = fnt.getFontData();
        if (fontData != null && fontData.length > 0)
            minHeight = fontData[0].getHeight() + 10;
    }

    public LayoutData getLayoutData() {
        LayoutData data = super.getLayoutData();
        if (minHeight > 0)
            data.minimumHeight = minHeight;
        return data;
    }
}

Now set instance of this class as cell editor in the function enableEditing1 –

treeViewer.setCellEditors(new CellEditor[]{new MyTextCellEditor(treeViewer.getTree())});

And in the function enableEditing2  –

final TextCellEditor cellEditor = new MyTextCellEditor(treeViewer.getTree());

tree_viewer3With the above changes, the cell editor is created with border, as shown in the image on the left.

 

 

 

 

You can find the complete source code of this example here.

-Ram Kulkarni

6 Replies to “In-place Editing in Eclipse TreeViewer”

  1. Pingback: moncler mantel
  2. Hello, I really appreciate your article, it has saved me lot of time, i was having a hard time implementing the the tree viewer, however my problem is still a bit more complex than the topics your article covered. I would like to have multiple columns, each with different data that can be edited independently, however when I try doing this i get a very nasty exception thrown at me, the only column I’m able to add editingSupport to is the first. Any help you can provide me to sole my problem will be greatly appreciated.Once again thank you for the nice article.

  3. Hello! I would really appreciate if you’d explain how to change the initial input data given with viewer.setInput(listOfSomething) into another list listOfSomethingElse. It is in vain to say viewer.setInput(listOfSomethingElse). The TreeView’s content just won’t change. Thanks!

Leave a Reply

Social