Home Tutorials Training Consulting Products Books Company Donate Contact us









NOW Hiring

Quick links

Share

Eclipse JFace TreeViewer tutorial. This tutorial explains the usage of Eclipse JFace TreeViewer. It also shows the usage of a DelegatingStyledCellLabelProvider. It is based on Eclipse 4.4. (Luna).

1. Prerequisites

Please see Introduction to JFace for an introduction to the concepts behind this example.

For an example on how to build JFace Tables please see JFace Table Tutorial.

2. JFace TreeViewer

2.1. Using viewers to display a tree

The TreeViewer class provides viewer support for displaying trees. The usage of this class is similar to the TableViewer class. The main difference is that the TreeViewer class requires a structured content provider. Typically your content provider has to implement the ITreeContentProvider interface to be used with your TreeViewer class.

Basically a TreeViewer can be used similar to a TableViewer, which just shows a list of elements by using the following content provider:

public class TreeContentProvider implements ITreeContentProvider {
        @Override
        public boolean hasChildren(Object element) {
                return false;
        }

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

        @Override
        public Object[] getElements(Object inputElement) {
                return ArrayContentProvider.getInstance().getElements(inputElement);
        }

        @Override
        public Object[] getChildren(Object parentElement) {
                return null;
        }
}

This ITreeContentProvider just delegates to the ArrayContentProvider in its getElements method and the elements have no children.

        @PostConstruct
        public void postConstruct(Composite parent) {
                TreeViewer viewer = new TreeViewer(parent);
                viewer.setContentProvider(new TreeContentProvider());
                viewer.getTree().setHeaderVisible(true);
                viewer.getTree().setLinesVisible(true);

                TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
                viewerColumn.getColumn().setWidth(300);
                viewerColumn.getColumn().setText("Names");
                viewerColumn.setLabelProvider(new ColumnLabelProvider());

                viewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });

                GridLayoutFactory.fillDefaults().generateLayout(parent);
        }

2.2. Selection and double-click listener

JFace allows you to access the SWT controls to define listeners on your viewer. For example you can add a SelectionListener implementation to the SWT control which is wrapped in the JFace object. The following code snippet demonstrates how to expand a tree with a mouse click.

// the viewer field is an already configured TreeViewer
Tree tree = (Tree) viewer.getControl();
tree.addSelectionListener(new SelectionAdapter() {
  @Override
  public void widgetSelected(SelectionEvent e) {
          TreeItem item = (TreeItem) e.item;
                  if (item.getItemCount() > 0) {
                          item.setExpanded(!item.getExpanded());
                          // update the viewer
                          viewer.refresh();
                  }
          }
});

Viewers allows you to add certain listeners directly to them. The following example shows how to expand an instance of a TreeViewer with a double click.

viewer.addDoubleClickListener(new IDoubleClickListener() {
        @Override
        public void doubleClick(DoubleClickEvent event) {
                TreeViewer viewer = (TreeViewer) event.getViewer();
                IStructuredSelection thisSelection = (IStructuredSelection) event.getSelection();
                Object selectedNode = thisSelection.getFirstElement();
                viewer.setExpandedState(selectedNode,
                                !viewer.getExpandedState(selectedNode));
        }
});

2.3. Adjusting tree columns TreeColumns on expand

In case a TreeViewer has multiple columns it does not look good, if the first column, which contains the expandable items is clipped.

ClippedTreeColumn

In order to let a column fit to it’s contents width you can invoke the columns pack method like that.

// the viewer field is an already configured TreeViewer
Tree tree = (Tree) viewer.getControl();

Listener listener = new Listener() {

   @Override
   public void handleEvent(Event event) {
      TreeItem treeItem = (TreeItem)event.item;
      final TreeColumn[] treeColumns treeItem.getParent().getColumns();
      display.asyncExec(new Runnable() {

         @Override
         public void run() {
            for (TreeColumn treeColumn : treeColumns)
                 treeColumn.pack();
         }
      });
   }
};

tree.addListener(SWT.Expand, listener);

With this code every column of the Tree will have the appropriate width, when an TreeItem is expaned, so that the contents of the column is not clipped.

2.4. Increasing the font size

The font size can be increased by simply setting a Font for the underlying SWT Tree.

        @PostConstruct
        public void postConstruct(Composite parent) {

                ResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);

                TreeViewer viewer = new TreeViewer(parent);
                viewer.setContentProvider(new TreeContentProvider());
                viewer.getTree().setHeaderVisible(true);
                viewer.getTree().setLinesVisible(true);
                viewer.getTree().setFont(resourceManager.createFont(FontDescriptor.createFrom("Arial", 32, SWT.ITALIC)));

                TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
                viewerColumn.getColumn().setWidth(300);
                viewerColumn.getColumn().setText("Names");
                viewerColumn.setLabelProvider(new ColumnLabelProvider());

                viewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });

                GridLayoutFactory.fillDefaults().generateLayout(parent);
        }

3. Optional Exercise: File browser via a TreeViewer

3.1. Create a new application

This exercise is a stand-alone exercise and can be used to repeat the steps of creating an Eclipse 4 application.

Use the Eclipse 4 wizard from File ▸ New ▸ Other…​ ▸ Eclipse 4 ▸ Eclipse 4 Application Project to create a new Eclipse 4 application without sample data called com.example.e4.filebrowser.

The important selection in the last wizard page is highlighted in the following screenshot.

Empty content

3.2. Add an image file

Download or create an icon called folder.png and place it into the "icons" folder of your plug-in.

You find an example icon under the following URL: Folder icon.

This icon is taken from the following icon collection: FamFamFam icons.

3.3. Create a part

Add a part stack with a part to your application model and display a TreeViewer in this part.

Implement a class for the ITreeContentProvider interface which allows you to browse the file system. Review the Javadoc of this class to understand the methods of this interface.

Also implement your custom LabelProvider for the tree.

Use viewer.setInput(File.listRoots()); to set the initial input to the viewer.

The following listing contains an example implementation for this exercise. It assumes that you added the "folder.png" icon to the "icons" folder. It also demonstrates the usage of a ViewLabelProvider.

package com.vogella.jface.treeviewer.parts;

import java.io.File;
import java.net.URL;

import javax.annotation.PostConstruct;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

public class FileBrowserPart {
        private TreeViewer viewer;

        @PostConstruct
        public void createControls(Composite parent) {
                viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
                viewer.setContentProvider(new ViewContentProvider());
                viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
                                new ViewLabelProvider(createImageDescriptor())));
                viewer.setInput(File.listRoots());
        }

        private ImageDescriptor createImageDescriptor() {
                Bundle bundle = FrameworkUtil.getBundle(ViewLabelProvider.class);
                URL url = FileLocator.find(bundle, new Path("icons/folder.png"), null);
                return ImageDescriptor.createFromURL(url);
        }

        class ViewContentProvider implements ITreeContentProvider {
                public void inputChanged(Viewer v, Object oldInput, Object newInput) {
                }

                @Override
                public void dispose() {
                }

                @Override
                public Object[] getElements(Object inputElement) {
                        return (File[]) inputElement;
                }

                @Override
                public Object[] getChildren(Object parentElement) {
                        File file = (File) parentElement;
                        return file.listFiles();
                }

                @Override
                public Object getParent(Object element) {
                        File file = (File) element;
                        return file.getParentFile();
                }

                @Override
                public boolean hasChildren(Object element) {
                        File file = (File) element;
                        if (file.isDirectory()) {
                                return true;
                        }
                        return false;
                }

        }

        class ViewLabelProvider extends LabelProvider implements IStyledLabelProvider {

                private ImageDescriptor directoryImage;
                private ResourceManager resourceManager;

                public ViewLabelProvider(ImageDescriptor directoryImage) {
                        this.directoryImage = directoryImage;
                }

                @Override
                public StyledString getStyledText(Object element) {
                        if(element instanceof File) {
                                File file = (File) element;
                                StyledString styledString = new StyledString(getFileName(file));
                                String[] files = file.list();
                                if (files != null) {
                                        styledString.append(" ( " + files.length + " ) ",
                                                        StyledString.COUNTER_STYLER);
                                }
                                return styledString;
                        }
                        return null;
                }

                @Override
                public Image getImage(Object element) {
                        if(element instanceof File) {
                                if(((File) element).isDirectory()) {
                                        return getResourceManager().createImage(directoryImage);
                                }
                        }

                        return super.getImage(element);
                }

                @Override
                public void dispose() {
                        // garbage collect system resources
                        if(resourceManager != null) {
                                resourceManager.dispose();
                                resourceManager = null;
                        }
                }

                protected ResourceManager getResourceManager() {
                        if(resourceManager == null) {
                                resourceManager = new LocalResourceManager(JFaceResources.getResources());
                        }
                        return resourceManager;
                }

                private String getFileName(File file) {
                        String name = file.getName();
                        return name.isEmpty() ? file.getPath() : name;
                }
        }

        @Focus
        public void setFocus() {
                viewer.getControl().setFocus();
        }
}

Link from your part in the application model to your new class.

3.4. Validating

Start your new application ensure that you see the content of your file system in your tree.

4. Optional exercise: Add multiple columns to a tree viewer

This exercise assumes that Optional Exercise: File browser via a TreeViewer has been done, since this exercise is based on it.

4.1. Using TreeViewerColumns in the file browser

To add multiple columns in a TreeViewer, the TreeViewerColumn class can be used to define columns for the underlying tree.

A TreeViewerColumn itself is a wrapper around the TreeColumn widget from SWT.

// make the table header visible to see the column's text
viewer.getTree().setHeaderVisible(true);

TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
TreeColumn column = viewerColumn.getColumn();
column.setText("Name");
column.setWidth(300);

Instead of applying a CellLabelProvider for the whole viewer each TreeViewerColumn gets its own CellLabelProivder assigned. This allows that label for each column can be defined separately. To assign a label provider to the tree viewer column the TreeViewerColumn#setLabelProvider method is used.

package com.vogella.jface.treeviewer.parts;

import java.io.File;
import java.net.URL;
import java.text.DateFormat;
import java.util.Date;

import javax.annotation.PostConstruct;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TreeColumn;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

public class FileBrowserPart {
        private TreeViewer viewer;

        private static final DateFormat dateFormat = DateFormat.getDateInstance();

        @PostConstruct
        public void createControls(Composite parent) {
                viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
                viewer.setContentProvider(new ViewContentProvider());
                viewer.getTree().setHeaderVisible(true);

                TreeViewerColumn mainColumn = new TreeViewerColumn(viewer, SWT.NONE);
                mainColumn.getColumn().setText("Name");
                mainColumn.getColumn().setWidth(300);
                mainColumn.setLabelProvider(
                                new DelegatingStyledCellLabelProvider(
                                                new ViewLabelProvider(createImageDescriptor())));

                TreeViewerColumn modifiedColumn = new TreeViewerColumn(viewer, SWT.NONE);
                modifiedColumn.getColumn().setText("Last Modified");
                modifiedColumn.getColumn().setWidth(100);
                modifiedColumn.getColumn().setAlignment(SWT.RIGHT);
                modifiedColumn
                                .setLabelProvider(new DelegatingStyledCellLabelProvider(
                                                new FileModifiedLabelProvider(dateFormat)));

                TreeViewerColumn fileSizeColumn = new TreeViewerColumn(viewer, SWT.NONE);
                fileSizeColumn.getColumn().setText("Size");
                fileSizeColumn.getColumn().setWidth(100);
                fileSizeColumn.getColumn().setAlignment(SWT.RIGHT);
                fileSizeColumn.setLabelProvider(new DelegatingStyledCellLabelProvider(
                                new FileSizeLabelProvider()));

                viewer.setInput(File.listRoots());
        }

        private ImageDescriptor createImageDescriptor() {
                Bundle bundle = FrameworkUtil.getBundle(ViewLabelProvider.class);
                URL url = FileLocator.find(bundle, new Path("icons/folder.png"), null);
                return ImageDescriptor.createFromURL(url);
        }

        class ViewContentProvider implements ITreeContentProvider {
                public void inputChanged(Viewer v, Object oldInput, Object newInput) {
                }

                @Override
                public void dispose() {
                }

                @Override
                public Object[] getElements(Object inputElement) {
                        return (File[]) inputElement;
                }

                @Override
                public Object[] getChildren(Object parentElement) {
                        File file = (File) parentElement;
                        return file.listFiles();
                }

                @Override
                public Object getParent(Object element) {
                        File file = (File) element;
                        return file.getParentFile();
                }

                @Override
                public boolean hasChildren(Object element) {
                        File file = (File) element;
                        if (file.isDirectory()) {
                                return true;
                        }
                        return false;
                }

        }

        class ViewLabelProvider extends LabelProvider implements IStyledLabelProvider {

                private ImageDescriptor directoryImage;
                private ResourceManager resourceManager;

                public ViewLabelProvider(ImageDescriptor directoryImage) {
                        this.directoryImage = directoryImage;
                }

                @Override
                public StyledString getStyledText(Object element) {
                        if (element instanceof File) {
                                File file = (File) element;
                                StyledString styledString = new StyledString(getFileName(file));
                                String[] files = file.list();
                                if (files != null) {
                                        styledString.append(" ( " + files.length + " ) ", StyledString.COUNTER_STYLER);
                                }
                                return styledString;
                        }
                        return null;
                }

                @Override
                public Image getImage(Object element) {
                        if (element instanceof File) {
                                if (((File) element).isDirectory()) {
                                        return getResourceManager().createImage(directoryImage);
                                }
                        }

                        return super.getImage(element);
                }

                @Override
                public void dispose() {
                        // garbage collection system resources
                        if (resourceManager != null) {
                                resourceManager.dispose();
                                resourceManager = null;
                        }
                }

                protected ResourceManager getResourceManager() {
                        if (resourceManager == null) {
                                resourceManager = new LocalResourceManager(JFaceResources.getResources());
                        }
                        return resourceManager;
                }

                private String getFileName(File file) {
                        String name = file.getName();
                        return name.isEmpty() ? file.getPath() : name;
                }
        }

        class FileModifiedLabelProvider extends LabelProvider implements IStyledLabelProvider {

                private DateFormat dateLabelFormat;

                public FileModifiedLabelProvider(DateFormat dateFormat) {
                        dateLabelFormat = dateFormat;
                }

                @Override
                public StyledString getStyledText(Object element) {
                        if (element instanceof File) {
                                File file = (File) element;
                                long lastModified = file.lastModified();
                                return new StyledString(dateLabelFormat.format(new Date(lastModified)));
                        }
                        return null;
                }
        }

        class FileSizeLabelProvider extends LabelProvider implements IStyledLabelProvider {

                @Override
                public StyledString getStyledText(Object element) {
                        if (element instanceof File) {
                                File file = (File) element;
                                if (file.isDirectory()) {
                                        // a directory is just a container and has no size
                                        return new StyledString("0");
                                }
                                return new StyledString(String.valueOf(file.length()));
                        }
                        return null;
                }
        }

        @Focus
        public void setFocus() {
                viewer.getControl().setFocus();
        }
}

4.2. Validating

Start your new application ensure that you see the content of your file system in your tree.

Multiple Column FileBrowser

5. Searchable TreeViewer

This example shows how to make a JFace TreeViewer searchable. Pressing Ctrl-f opens a search dialog. If a tree item is found it gets selected.

public class SearchDialog extends Dialog {

    public SearchDialog(Shell parent) {
        super(parent);
    }

    private Tree tree;
    private SearchableTreeViewer searchableTreeViewer;
    private Button searchButton;
    private Text searchField;
    private TreeSearchAlgorithm algorithm;

    public SearchDialog(Shell parent, SearchableTreeViewer searchableTreeViewer, TreeSearchAlgorithm algorithm) {
        super(parent);
        this.searchableTreeViewer = searchableTreeViewer;
        this.algorithm = algorithm;
        tree = searchableTreeViewer.getTree();
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite container = (Composite) super.createDialogArea(parent);
        container.setLayout(new FillLayout());
        searchField = new Text(container, SWT.NONE);
        return container;
    }

    @Override
    protected void createButtonsForButtonBar(Composite parent) {
        searchButton = createButton(parent, IDialogConstants.PROCEED_ID, "Search", false);
        searchButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(this::search));
    }

    private void search(SelectionEvent event) {
        TreeItem found = algorithm.search(tree.getItems(), searchField.getText());
        if (found != null) {
            searchableTreeViewer.setSelection(new StructuredSelection(found.getData()), true);
            close();
        } else {
            System.out.println("Nothing Found");
        }
    }
}
public interface TreeSearchAlgorithm {

    public TreeItem search(TreeItem[] treeItems, String searchTerm);
}
public class TreeContentProvider implements ITreeContentProvider {
    @Override
    public boolean hasChildren(Object element) {
        return false;
    }

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

    @Override
    public Object[] getElements(Object inputElement) {
        return ArrayContentProvider.getInstance().getElements(inputElement);
    }

    @Override
    public Object[] getChildren(Object parentElement) {
        return null;
    }
}
public class SearchableTreeViewer extends TreeViewer {

    private Composite parent;

    public SearchableTreeViewer(Composite parent, TreeSearchAlgorithm algorithm) {
        super(parent);
        this.parent = parent;
        parent.addKeyListener(KeyListener.keyPressedAdapter(event -> {
            if (event.stateMask == SWT.CTRL && event.keyCode == 'f') {
                startSearchDialog(algorithm);
            }
        }));
    }

    private void startSearchDialog(TreeSearchAlgorithm algorithm) {
        SearchDialog searchDialog = new SearchDialog(parent.getShell(), this, algorithm);
        searchDialog.open();
    }
}
public class TreeWindow extends ApplicationWindow {


    public TreeWindow() {
        super(null);
        // Don't return from open() until window closes
        setBlockOnOpen(true);

        // Open the main window
        open();

        // Dispose the display
        Display.getCurrent().dispose();
    }

    @Override
    protected void configureShell(Shell shell) {
        super.configureShell(shell);

        // Set the title bar text and the size
        shell.setText("Searchable Tree");
        shell.setSize(400, 400);
    }

    @Override
    protected Control createContents(Composite parent) {
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(new FillLayout());
        SearchableTreeViewer treeViewer = new SearchableTreeViewer(composite,
                (treeItems, searchText) -> {
                    for (TreeItem item : treeItems) {
                        if (item.getText().toUpperCase().contains(searchText.toUpperCase())) {
                            return item;
                        };
                    }
                    return null;
                });
        treeViewer.setContentProvider(new TreeContentProvider());
        TreeViewerColumn viewerColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
        treeViewer.getTree().setHeaderVisible(true);
        treeViewer.getTree().setLinesVisible(true);
        viewerColumn.getColumn().setWidth(300);
        viewerColumn.getColumn().setText("Names");
        viewerColumn.setLabelProvider(new ColumnLabelProvider());
        treeViewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });
        return composite;
    }

    public static void main(String[] args) {
        new TreeWindow();
    }
}

6. Learn more about Eclipse 4 RCP development

I hope you enjoyed this tutorial. You find this tutorial and much more information also in the Eclipse 4 RCP book.

7. About this website

8.2. vogella GmbH training and consulting support

TRAINING SERVICE & SUPPORT

The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

Copyright © 2012-2016 vogella GmbH. Free use of the software examples is granted under the terms of the EPL License. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

See Licence.