vogella training Android Training Eclipse Training

Eclipse JFace Table - Advanced Tutorial

Lars Vogel

Version 3.2

08.04.2012

Revision History
Revision 0.1 01.07.2007 Lars
Vogel
created
Revision 0.2 - 3.2 12.09.2007 - 08.04.2012 Lars
Vogel
bug fixes and enhancements

Eclipse JFace Table

This tutorial explains advanced usage of the JFace TableViewer including inline table editing, table filtering and sorting, and model / view interaction. StyledLabelProvider are also discussed.

This tutorial is based on Eclipse 3.7 (Indigo).


Table of Contents

1. Introduction
2. JFace Table Features
2.1. Editing Support
2.2. Filtering data
2.3. ViewerComparator
2.4. StyledCellLabelProvider
2.5. Show and hide table columns
2.6. Tooltips for Viewer
3. Tutorial: Sort Columns
4. Tutorial: Editing the table content
5. Tutorial: Using a Filter
6. Tutorial: Using a Styled Label Providers
7. Commands Prerequisitions
8. Commands
8.1. Add a objects to the table
8.2. Delete objects from your table
8.3. Copy data to the system clipboard
9. Thank you
10. Questions and Discussion
11. Download
12. Links and Literature
12.1. Source Code
12.2. JFace Resources
12.3. vogella Resources

1. Introduction

Eclipse JFace provides functionality to build tables. The following tutorial is based on the JFace Table Tutorial. Please finish that tutorial before continuing with this one.

This tutorial demonstrates how to sort based on table columns, how to make table columns editable and how to filter the displayed data.

We will also learn how to use the StyledCellLabelProvider class to influence the display of data in the table. The usage of commands and how to hide and sort table columns is demonstrated.

The final application will look like this.

2. JFace Table Features

2.1. Editing Support

To make a JFace TableColumn editable, you need an object of type EditingSupport.

The following shows an example of an EditorSupport implementation.

				
package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;

import de.vogella.jface.tableviewer.model.Person;

public class FirstNameEditingSupport extends EditingSupport {

	private final TableViewer viewer;

	public FirstNameEditingSupport(TableViewer viewer) {
		super(viewer);
		this.viewer = viewer;
	}

	@Override
	protected CellEditor getCellEditor(Object element) {
		return new TextCellEditor(viewer.getTable());
	}

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

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

	@Override
	protected void setValue(Object element, Object value) {
		((Person) element).setFirstName(String.valueOf(value));
		viewer.refresh();
	}
}

			

The EditingSupport implementation defines how the content can be changed. EditingSupport returns in his getCellEditor() method an object of type CellEditor. This object creates the controls to change the data.

The method setValue() in EditingSupport receives the changed value based on the user input. In this method you assign the value to your data object.

The getValue() method receives the current object and returns the value which should be displayed. The canEdit() method defines, if the cell can be edited.

Eclipse provides default implementations for typical cases, e.g. TextCellEditor, ColorCellEditor, CheckboxCellEditor, DialogCellEditor and ComboBoxViewerCellEditor.

You can assign the EditorSupport class to your TableColumn via the setEditingSupport() method of your TableViewerColumn.

				
col.setEditingSupport(new FirstNameEditingSupport(viewer));

			

2.2. Filtering data

JFace Viewer support filtering of data via the setFilters() or addFilter() methods. These methods expect ViewerFilter objects as arguments.

For each registered ViewerFilter object the checked() method is called. The methods returns true if the data should be shown and false if it should be filtered.

				
package de.vogella.jface.tableviewer.filter;

import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;

import de.vogella.jface.tableviewer.model.Person;

public class PersonFilter extends ViewerFilter {

	private String searchString;

	public void setSearchText(String s) {
		// Search must be a substring of the existing value
		this.searchString = ".*" + s + ".*";
	}

	@Override
	public boolean select(Viewer viewer, Object parentElement, Object element) {
		if (searchString == null || searchString.length() == 0) {
			return true;
		}
		Person p = (Person) element;
		if (p.getFirstName().matches(searchString)) {
			return true;
		}
		if (p.getLastName().matches(searchString)) {
			return true;
		}

		return false;
	}
}

			

All filters are checked whenever the input on the Viewer changes, or whenever the refresh() method is called on the Viewer.

If more than one filter is defined for a Viewer, all filters must return true for the table to display the data.

2.3. ViewerComparator

JFace supports sorting of the table content via the setComparator() method on the Viewer object. This method expects an ViewerComparator object. Per default it will sort based on the toString() method of the objects in the viewer.

				
// Sort according to due date
viewer.setComparator(new ViewerComparator() {
	public int compare(Viewer viewer, Object e1, Object e2) {
		Todo t1 = (Todo) e1;
		Todo t2 = (Todo) e2;
		return t1.getDueDate().compareTo(t2.getDueDate());
	};
});
			

2.4. StyledCellLabelProvider

It is also possible to use a StyleCellLabelProvider for a very flexible styling of your text. StyledCellLabelProvider extends CellLabelProvider and allows to draw via its update() method.

The following example show how to use a StyledCellLabelProvider. In this example a part of a pre-defined text is highlighted.

				
column.setLabelProvider(new StyledCellLabelProvider() {
@Override
	public void update(ViewerCell cell) {
		StyledString text = new StyledString();
		StyleRange myStyledRange = new StyleRange(17, 2, null, Display
			.getCurrent().getSystemColor(SWT.COLOR_YELLOW));
		text.append("This is a test", StyledString.DECORATIONS_STYLER);
		text.append(" ( " + 15 + " ) ", StyledString.DECORATIONS_STYLER);
		cell.setText(text.toString());

		StyleRange[] range = { myStyledRange };
		cell.setStyleRanges(range);
		super.update(cell);
	}
});
			

The result might look like the following.

Screenshot of a styledLabelProvider

2.5. Show and hide table columns

You can add a Menu to your table. This menu can get a MenuItem for each column and you can use it to hide and show column based on the width setting.

				
// Define the menu and assign to the table
Menu headerMenu = new Menu(parent.getShell(), SWT.POP_UP);
viewer.getTable().setMenu(headerMenu);
// Create your columns....
// ... as usual


// Now add a MenuItem for the colum to the table menu
createMenuItem(headerMenu, column.getColumn());

// The createMenuItem method add per column a 
// new MenuItem to the menu
private void createMenuItem(Menu parent, final TableColumn column) {
	final MenuItem itemName = new MenuItem(parent, SWT.CHECK);
	itemName.setText(column.getText());
	itemName.setSelection(column.getResizable());
	itemName.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event event) {
			if (itemName.getSelection()) {
				column.setWidth(150);
				column.setResizable(true);
			} else {
				column.setWidth(0);
				column.setResizable(false);
			}
		}
	});
}



			

2.6. Tooltips for Viewer

You can also to use tooltips for the cells of the Viewer. For this you have to activate tooltips for your Viewer.

				
// Activate the tooltip support for the viewer
ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);
			

In your CellLabelProvider you specify the related methods for displaying the tooltip.

				
col.setLabelProvider(new CellLabelProvider() {
	@Override
	public void update(ViewerCell cell) {
		cell.setText(((Person) cell.getElement()).getLastName());
	}

	@Override
	public String getToolTipText(Object element) {
		return "Tooltip (" + element + ")";
	}

	@Override
	public Point getToolTipShift(Object object) {
		return new Point(5, 5);
	}

	@Override
	public int getToolTipDisplayDelayTime(Object object) {
		return 100;
	}

	@Override
	public int getToolTipTimeDisplayed(Object object) {
		return 5000;
	}

});
			

3. Tutorial: Sort Columns

The following example will allow to sort the table based on different columns, therefore our ViewerComparator will have the option to set the sort column.

Re-using the project "de.vogella.jface.tableviewer" from JFace Table Tutorial create the Class "de.vogella.jface.tableviewer.sorter.MyViewerComparator.java"

			
package de.vogella.jface.tableviewer.sorter;

import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;

import de.vogella.jface.tableviewer.model.Person;

public class MyViewerComparator extends ViewerComparator {
	private int propertyIndex;
	private static final int DESCENDING = 1;
	private int direction = DESCENDING;

	public MyViewerComparator() {
		this.propertyIndex = 0;
		direction = DESCENDING;
	}

	public int getDirection() {
		return direction == 1 ? SWT.DOWN : SWT.UP;
	}

	public void setColumn(int column) {
		if (column == this.propertyIndex) {
			// Same column as last sort; toggle the direction
			direction = 1 - direction;
		} else {
			// New column; do an ascending sort
			this.propertyIndex = column;
			direction = DESCENDING;
		}
	}

	@Override
	public int compare(Viewer viewer, Object e1, Object e2) {
		Person p1 = (Person) e1;
		Person p2 = (Person) e2;
		int rc = 0;
		switch (propertyIndex) {
		case 0:
			rc = p1.getFirstName().compareTo(p2.getFirstName());
			break;
		case 1:
			rc = p1.getLastName().compareTo(p2.getLastName());
			break;
		case 2:
			rc = p1.getGender().compareTo(p2.getGender());
			break;
		case 3:
			if (p1.isMarried() == p2.isMarried()) {
				rc = 0;
			} else
				rc = (p1.isMarried() ? 1 : -1);
			break;
		default:
			rc = 0;
		}
		// If descending order, flip the direction
		if (direction == DESCENDING) {
			rc = -rc;
		}
		return rc;
	}

}

		

In "View" change the method "createTableViewerColumn" and create a new method "getSelectionAdapter()". This method will add a listener to the columns. If one of the columns is selected the sorting column in ViewerComparator will be set.

			
package de.vogella.jface.tableviewer;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;

import de.vogella.jface.tableviewer.model.ModelProvider;
import de.vogella.jface.tableviewer.model.Person;
import de.vogella.jface.tableviewer.sorter.MyViewerComparator;

public class View extends ViewPart {
	public static final String ID = "de.vogella.jface.tableviewer.view";
	private MyViewerComparator comparator;

	private TableViewer viewer;
	// We use icons
	private static final Image CHECKED = Activator.getImageDescriptor(
			"icons/checked.gif").createImage();
	private static final Image UNCHECKED = Activator.getImageDescriptor(
			"icons/unchecked.gif").createImage();

	public void createPartControl(Composite parent) {
		GridLayout layout = new GridLayout(2, false);
		parent.setLayout(layout);
		Label searchLabel = new Label(parent, SWT.NONE);
		searchLabel.setText("Search: ");
		final Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH);
		searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
				| GridData.HORIZONTAL_ALIGN_FILL));
		createViewer(parent);
		// Set the sorter for the table
		comparator = new MyViewerComparator();
		viewer.setComparator(comparator);

	}

	private void createViewer(Composite parent) {
		viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
				| SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
		createColumns(parent, viewer);
		final Table table = viewer.getTable();
		table.setHeaderVisible(true);
		table.setLinesVisible(true);

		viewer.setContentProvider(new ArrayContentProvider());
		// Get the content for the viewer, setInput will call getElements in the
		// contentProvider
		viewer.setInput(ModelProvider.INSTANCE.getPersons());
		// Make the selection available to other views
		getSite().setSelectionProvider(viewer);

		// Layout the viewer
		GridData gridData = new GridData();
		gridData.verticalAlignment = GridData.FILL;
		gridData.horizontalSpan = 2;
		gridData.grabExcessHorizontalSpace = true;
		gridData.grabExcessVerticalSpace = true;
		gridData.horizontalAlignment = GridData.FILL;
		viewer.getControl().setLayoutData(gridData);
	}

	public TableViewer getViewer() {
		return viewer;
	}

	// This will create the columns for the table
	private void createColumns(final Composite parent, final TableViewer viewer) {
		String[] titles = { "First name", "Last name", "Gender", "Married" };
		int[] bounds = { 100, 100, 100, 100 };

		// First column is for the first name
		TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				Person p = (Person) element;
				return p.getFirstName();
			}
		});

		// Second column is for the last name
		col = createTableViewerColumn(titles[1], bounds[1], 1);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				Person p = (Person) element;
				return p.getLastName();
			}
		});

		// Now the gender
		col = createTableViewerColumn(titles[2], bounds[2], 2);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				Person p = (Person) element;
				return p.getGender();
			}
		});

		// // Now the status married
		col = createTableViewerColumn(titles[3], bounds[3], 3);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				return null;
			}

			@Override
			public Image getImage(Object element) {
				if (((Person) element).isMarried()) {
					return CHECKED;
				} else {
					return UNCHECKED;
				}
			}
		});

	}

	private TableViewerColumn createTableViewerColumn(String title, int bound,
			final int colNumber) {
		final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
				SWT.NONE);
		final TableColumn column = viewerColumn.getColumn();
		column.setText(title);
		column.setWidth(bound);
		column.setResizable(true);
		column.setMoveable(true);
		column.addSelectionListener(getSelectionAdapter(column, colNumber));
		return viewerColumn;
	}

	private SelectionAdapter getSelectionAdapter(final TableColumn column,
			final int index) {
		SelectionAdapter selectionAdapter = new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				comparator.setColumn(index);
				int dir = comparator.getDirection();
				viewer.getTable().setSortDirection(dir);
				viewer.getTable().setSortColumn(column);
				viewer.refresh();
			}
		};
		return selectionAdapter;
	}

	
/** * Passing the focus request to the viewer's control. */
public void setFocus() { viewer.getControl().setFocus(); } }

If you run the example and click on a column header, the table should be sorted according to the content of this column. There will also appear a sort-direction in the top of the column.

4. Tutorial: Editing the table content

Create the following new classes in the new package "de.vogella.jface.tableviewer.edit".

			
package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;

import de.vogella.jface.tableviewer.model.Person;

public class FirstNameEditingSupport extends EditingSupport {

	private final TableViewer viewer;

	public FirstNameEditingSupport(TableViewer viewer) {
		super(viewer);
		this.viewer = viewer;
	}

	@Override
	protected CellEditor getCellEditor(Object element) {
		return new TextCellEditor(viewer.getTable());
	}

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

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

	@Override
	protected void setValue(Object element, Object value) {
		((Person) element).setFirstName(String.valueOf(value));
		viewer.refresh();
	}
}

		

			
package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.TableViewer;

import de.vogella.jface.tableviewer.model.Person;

public class LastNameEditingSupport extends FirstNameEditingSupport {

	private final TableViewer viewer;

	public LastNameEditingSupport(TableViewer viewer) {
		super(viewer);
		this.viewer = viewer;
	}

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

	@Override
	protected void setValue(Object element, Object value) {
		((Person) element).setLastName(String.valueOf(value));
		viewer.refresh();
	}
}

		

			
package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;

import de.vogella.jface.tableviewer.model.Person;

public class GenderEditingSupport extends EditingSupport {

	private final TableViewer viewer;

	public GenderEditingSupport(TableViewer viewer) {
		super(viewer);
		this.viewer = viewer;
	}

	@Override
	protected CellEditor getCellEditor(Object element) {
		String[] gender = new String[2];
		gender[0] = "male";
		gender[1] = "female";

		return new ComboBoxCellEditor(viewer.getTable(), gender);
	}

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

	@Override
	protected Object getValue(Object element) {
		Person person = (Person) element;
		if (person.getGender().equals("male")) {
			return 0;
		}
		return 1;

	}

	@Override
	protected void setValue(Object element, Object value) {
		Person pers = (Person) element;
		if (((Integer) value) == 0) {
			pers.setGender("male");
		} else {
			pers.setGender("female");
		}
		viewer.refresh();
	}
}

		

			
package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;

import de.vogella.jface.tableviewer.model.Person;

public class MarriedEditingSupport extends EditingSupport {

	private final TableViewer viewer;

	public MarriedEditingSupport(TableViewer viewer) {
		super(viewer);
		this.viewer = viewer;
	}

	@Override
	protected CellEditor getCellEditor(Object element) {
		return new CheckboxCellEditor(null, SWT.CHECK | SWT.READ_ONLY);

	}

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

	@Override
	protected Object getValue(Object element) {
		Person person = (Person) element;
		return person.isMarried();

	}

	@Override
	protected void setValue(Object element, Object value) {
		Person pers = (Person) element;
		pers.setMarried((Boolean) value);
		viewer.refresh();
	}
}

		

Now assign EditorSupport objects to your TableColumnViewers in your View class. Replace the existing method createColumns() with the following one and adjust your imports.

			
	// This will create the columns for the table
	private void createColumns(final Composite parent, final TableViewer viewer) {
		String[] titles = { "First name", "Last name", "Gender", "Married" };
		int[] bounds = { 100, 100, 100, 100 };

		// First column is for the first name
		TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0);
		col.setLabelProvider(new CellLabelProvider() {
			@Override
			public void update(ViewerCell cell) {
				cell.setText(((Person) cell.getElement()).getFirstName());
			}
		});
		col.setEditingSupport(new FirstNameEditingSupport(viewer));

		// Second column is for the last name
		col = createTableViewerColumn(titles[1], bounds[1], 1);
		col.setLabelProvider(new CellLabelProvider() {
			@Override
			public void update(ViewerCell cell) {
				cell.setText(((Person) cell.getElement()).getLastName());
			}
		});
		col.setEditingSupport(new LastNameEditingSupport(viewer));

		// Now the gender
		col = createTableViewerColumn(titles[2], bounds[2], 2);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				Person p = (Person) element;
				return p.getGender();
			}
		});
		col.setEditingSupport(new GenderEditingSupport(viewer));

		// // Now the status married
		col = createTableViewerColumn(titles[3], bounds[3], 3);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				return null;
			}

			@Override
			public Image getImage(Object element) {
				if (((Person) element).isMarried()) {
					return CHECKED;
				} else {
					return UNCHECKED;
				}
			}
		});
		col.setEditingSupport(new MarriedEditingSupport(viewer));

	}
		

Run your application. You should now be able to modify the content of the JFace table.

5. Tutorial: Using a Filter

The following will add a filter to the table by using the "Search" field. The user can enter search pattern in the Search box and only data which fits to the pattern will be displayed.

Create a new Class "de.vogella.jface.tableviewer.filter.PersonFilter.java"

			
package de.vogella.jface.tableviewer.filter;

import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;

import de.vogella.jface.tableviewer.model.Person;

public class PersonFilter extends ViewerFilter {

	private String searchString;

	public void setSearchText(String s) {
		// Search must be a substring of the existing value
		this.searchString = ".*" + s + ".*";
	}

	@Override
	public boolean select(Viewer viewer, Object parentElement, Object element) {
		if (searchString == null || searchString.length() == 0) {
			return true;
		}
		Person p = (Person) element;
		if (p.getFirstName().matches(searchString)) {
			return true;
		}
		if (p.getLastName().matches(searchString)) {
			return true;
		}

		return false;
	}
}

		

Add to your search text field a keyListener which updates the filter and the viewer. You need also to define a new field "private PersonFilter filter;" and change the method createPartControl().

			
	private PersonFilter filter;
	//// .... more fields 
	
	public void createPartControl(Composite parent) {
		GridLayout layout = new GridLayout(2, false);
		parent.setLayout(layout);
		Label searchLabel = new Label(parent, SWT.NONE);
		searchLabel.setText("Search: ");
		final Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH);
		searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
				| GridData.HORIZONTAL_ALIGN_FILL));
		createViewer(parent);
		// Set the sorter for the table
		comparator = new MyViewerComparator();
		viewer.setComparator(comparator);

		// New to support the search
		searchText.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent ke) {
				filter.setSearchText(searchText.getText());
				viewer.refresh();
			}

		});
		filter = new PersonFilter();
		viewer.addFilter(filter);
	}
	
		

Run the example, filtering should work.

6. Tutorial: Using a Styled Label Providers

First create the following helper class which will determine which occurrence of the search string is in the column.

			
package de.vogella.jface.tableviewer.util;

import java.util.ArrayList;
import java.util.List;

public class SearchUtil {
	
/** * Searches "searchTerm" in "content" and returns an array of int pairs * (index, length) for each occurrence. The search is case-sensitive. The * consecutive occurrences are merged together.<code> Examples: content = "123123x123" searchTerm = "1" --> [0, 1, 3, 1, 7, 1] content = "123123x123" searchTerm = "123" --> [0, 6, 7, 3] </code> * * @param searchTerm * can be null or empty. int[0] is returned in this case! * @param content * a not-null string (can be empty!) * @return an array of int pairs (index, length) */
public static int[] getSearchTermOccurrences(final String searchTerm, final String content) { if (searchTerm == null || searchTerm.length() == 0) { return new int[0]; } if (content == null) { throw new IllegalArgumentException("content is null"); } final List<Integer> list = new ArrayList<Integer>(); int searchTermLength = searchTerm.length(); int index; int fromIndex = 0; int lastIndex = -1; int lastLength = 0; while (true) { index = content.indexOf(searchTerm, fromIndex); if (index == -1) { // no occurrence of "searchTerm" in "content" starting from // index "fromIndex" if (lastIndex != -1) { // but there was a previous occurrence list.add(Integer.valueOf(lastIndex)); list.add(Integer.valueOf(lastLength)); } break; } if (lastIndex == -1) { // the first occurrence of "searchTerm" in "content" lastIndex = index; lastLength = searchTermLength; } else { if (lastIndex + lastLength == index) { // the current occurrence is right after the previous // occurrence lastLength += searchTermLength; } else { // there is at least one character between the current // occurrence and the previous one list.add(Integer.valueOf(lastIndex)); list.add(Integer.valueOf(lastLength)); lastIndex = index; lastLength = searchTermLength; } } fromIndex = index + searchTermLength; } final int n = list.size(); final int[] result = new int[n]; for (int i = 0; i != n; i++) { result[i] = list.get(i); } return result; } }

Change the class View to the following. We will only use StyledCellLabelProvider for the firstName. Most of the code changes are in method createColumns() plus a view variable definitions.

			
package de.vogella.jface.tableviewer;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;

import de.vogella.jface.tableviewer.edit.FirstNameEditingSupport;
import de.vogella.jface.tableviewer.edit.GenderEditingSupport;
import de.vogella.jface.tableviewer.edit.LastNameEditingSupport;
import de.vogella.jface.tableviewer.edit.MarriedEditingSupport;
import de.vogella.jface.tableviewer.filter.PersonFilter;
import de.vogella.jface.tableviewer.model.ModelProvider;
import de.vogella.jface.tableviewer.model.Person;
import de.vogella.jface.tableviewer.sorter.MyViewerComparator;
import de.vogella.jface.tableviewer.util.SearchUtil;

public class View extends ViewPart {
	public static final String ID = "de.vogella.jface.tableviewer.view";
	private MyViewerComparator comparator;

	private TableViewer viewer;
	private PersonFilter filter;
	// We use icons
	private static final Image CHECKED = Activator.getImageDescriptor(
			"icons/checked.gif").createImage();
	private static final Image UNCHECKED = Activator.getImageDescriptor(
			"icons/unchecked.gif").createImage();
	private Text searchText;
	private static Color colorYellow = Display.getCurrent().getSystemColor(
			SWT.COLOR_YELLOW);

	public void createPartControl(Composite parent) {
		GridLayout layout = new GridLayout(2, false);
		parent.setLayout(layout);
		Label searchLabel = new Label(parent, SWT.NONE);
		searchLabel.setText("Search: ");
		searchText = new Text(parent, SWT.BORDER | SWT.SEARCH);
		searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
				| GridData.HORIZONTAL_ALIGN_FILL));
		createViewer(parent);
		// Set the sorter for the table
		comparator = new MyViewerComparator();
		viewer.setComparator(comparator);

		// New to support the search
		searchText.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent ke) {
				filter.setSearchText(searchText.getText());
				viewer.refresh();
			}

		});
		filter = new PersonFilter();
		viewer.addFilter(filter);
	}

	private void createViewer(Composite parent) {
		viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
				| SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
		createColumns(parent, viewer);
		final Table table = viewer.getTable();
		table.setHeaderVisible(true);
		table.setLinesVisible(true);

		viewer.setContentProvider(new ArrayContentProvider());
		// Get the content for the viewer, setInput will call getElements in the
		// contentProvider
		viewer.setInput(ModelProvider.INSTANCE.getPersons());
		// Make the selection available to other views
		getSite().setSelectionProvider(viewer);

		// Layout the viewer
		GridData gridData = new GridData();
		gridData.verticalAlignment = GridData.FILL;
		gridData.horizontalSpan = 2;
		gridData.grabExcessHorizontalSpace = true;
		gridData.grabExcessVerticalSpace = true;
		gridData.horizontalAlignment = GridData.FILL;
		viewer.getControl().setLayoutData(gridData);
	}

	public TableViewer getViewer() {
		return viewer;
	}

	// This will create the columns for the table
	private void createColumns(final Composite parent, final TableViewer viewer) {
		String[] titles = { "First name", "Last name", "Gender", "Married" };
		int[] bounds = { 100, 100, 100, 100 };

		// First column is for the first name
		TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0);
		col.setLabelProvider(new StyledCellLabelProvider() {
			@Override
			public void update(ViewerCell cell) {
				String search = searchText.getText();
				Person person = (Person) cell.getElement();
				String cellText = person.getFirstName();
				cell.setText(cellText);
				if (search != null && search.length() > 0) {
					int intRangesCorrectSize[] = SearchUtil
							.getSearchTermOccurrences(search, cellText);
					List<StyleRange> styleRange = new ArrayList<StyleRange>();
					for (int i = 0; i < intRangesCorrectSize.length / 2; i++) {
						int start = intRangesCorrectSize[i];
						int length = intRangesCorrectSize[++i];
						StyleRange myStyledRange = new StyleRange(start,
								length, null, colorYellow);

						styleRange.add(myStyledRange);
					}
					cell.setStyleRanges(styleRange
							.toArray(new StyleRange[styleRange.size()]));
				} else {
					cell.setStyleRanges(null);
				}

				super.update(cell);

			}
		});
		col.setEditingSupport(new FirstNameEditingSupport(viewer));

		// Second column is for the last name
		col = createTableViewerColumn(titles[1], bounds[1], 1);
		col.setLabelProvider(new CellLabelProvider() {
			@Override
			public void update(ViewerCell cell) {
				cell.setText(((Person) cell.getElement()).getLastName());
			}
		});
		col.setEditingSupport(new LastNameEditingSupport(viewer));

		// Now the gender
		col = createTableViewerColumn(titles[2], bounds[2], 2);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				Person p = (Person) element;
				return p.getGender();
			}
		});
		col.setEditingSupport(new GenderEditingSupport(viewer));

		// // Now the status married
		col = createTableViewerColumn(titles[3], bounds[3], 3);
		col.setLabelProvider(new ColumnLabelProvider() {
			@Override
			public String getText(Object element) {
				return null;
			}

			@Override
			public Image getImage(Object element) {
				if (((Person) element).isMarried()) {
					return CHECKED;
				} else {
					return UNCHECKED;
				}
			}
		});
		col.setEditingSupport(new MarriedEditingSupport(viewer));

	}

	private TableViewerColumn createTableViewerColumn(String title, int bound,
			final int colNumber) {
		final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
				SWT.NONE);
		final TableColumn column = viewerColumn.getColumn();
		column.setText(title);
		column.setWidth(bound);
		column.setResizable(true);
		column.setMoveable(true);
		column.addSelectionListener(getSelectionAdapter(column, colNumber));
		return viewerColumn;
	}

	private SelectionAdapter getSelectionAdapter(final TableColumn column,
			final int index) {
		SelectionAdapter selectionAdapter = new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				comparator.setColumn(index);
				int dir = comparator.getDirection();
				viewer.getTable().setSortDirection(dir);
				viewer.refresh();
			}
		};
		return selectionAdapter;
	}

	
/** * Passing the focus request to the viewer's control. */
public void setFocus() { viewer.getControl().setFocus(); } }

Run the example, if you search now the selected content should get highlighted should work.

7. Commands Prerequisitions

If you are not familiar with Eclipse Commands, please have a look at the following tutorial: Eclipse Commands Tutorial

8. Commands

8.1. Add a objects to the table

This chapter shows how to add Persons to the TableViewer.

You have to make the selection of the Viewer available and also provide public method to refresh the data of to the Viewer so that other components can trigger the refresh process.

				
public void createPartControl(Composite parent) {
	//.... existing coding
	// Make selection available via the SelectionProvider
	getSite().setSelectionProvider(viewer);
}

//Used to update the viewer from outsite
public void refresh() {
	viewer.refresh();
}

			

Create a Dialog to enter the data for the additional person. Create package "de.vogella.jface.tableviewer.dialogs" and the following AddPersonDialog class.

				
package de.vogella.jface.tableviewer.dialogs;

import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import de.vogella.jface.tableviewer.model.Person;

public class AddPersonDialog extends TitleAreaDialog {

	private Text text1;
	private Text text2;
	private Person person;
	private Button button1;
	private Combo combo1;

	public Person getPerson() {
		return person;
	}

	public AddPersonDialog(Shell parentShell) {
		super(parentShell);
	}

	@Override
	protected Control createContents(Composite parent) {
		Control contents = super.createContents(parent);
		setTitle("Add a new Person");
		setMessage("Please enter the data of the new person",
				IMessageProvider.INFORMATION);
		return contents;
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		GridLayout layout = new GridLayout();
		layout.numColumns = 2;
		parent.setLayout(layout);
		Label label1 = new Label(parent, SWT.NONE);
		label1.setText("First Name");
		text1 = new Text(parent, SWT.BORDER);
		Label label2 = new Label(parent, SWT.NONE);
		label2.setText("Last Name");
		text2 = new Text(parent, SWT.BORDER);
		Label label3 = new Label(parent, SWT.NONE);
		label3.setText("Gender");
		GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
		gd.horizontalSpan = 2;
		combo1 = new Combo(parent, SWT.READ_ONLY);
		combo1.add("male");
		combo1.add("female");
		button1 = new Button(parent, SWT.CHECK);
		button1.setText("Is married?");
		button1.setLayoutData(gd);
		return parent;

	}

	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		((GridLayout) parent.getLayout()).numColumns++;

		Button button = new Button(parent, SWT.PUSH);
		button.setText("OK");
		button.setFont(JFaceResources.getDialogFont());
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				if (text1.getText().length() != 0
						&& text2.getText().length() != 0
						&& combo1.getItem(combo1.getSelectionIndex()).length() != 0) {
					person = new Person(text1.getText(), text2.getText(),
							combo1.getItem(combo1.getSelectionIndex()), button1
									.getSelection());
					close();

				} else {
					setErrorMessage("Please enter all data");
				}
			}
		});
	}
}

			

Create a new View called "ChangeView" to your application and add it to your perspective.

This View contains a Button which allow to start the Dialog, adds the new Person to the data and refreshes the Viewer.

				
package de.vogella.jface.tableviewer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;

import de.vogella.jface.tableviewer.dialogs.AddPersonDialog;
import de.vogella.jface.tableviewer.model.ModelProvider;

public class ChangeView extends ViewPart {
	public static final String ID = "de.vogella.jface.tableviewer.changeview";
	private Button btnNewButton;

	@Override
	public void createPartControl(Composite parent) {
		parent.setLayout(new GridLayout(1, false));

		btnNewButton = new Button(parent, SWT.NONE);
		btnNewButton.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {

				ModelProvider persons = ModelProvider.INSTANCE;
				AddPersonDialog dialog = new AddPersonDialog(getViewSite()
						.getShell());
				dialog.open();
				if (dialog.getPerson() != null) {
					persons.getPersons().add(dialog.getPerson());
					View part = (View) getViewSite().getPage()
							.findView(View.ID);
					// Updating the display in the view
					part.refresh();
				}
			}
		});
		btnNewButton.setText("Add");

		Button btnDelete = new Button(parent, SWT.NONE);
		btnDelete.setText("Delete");

	}

	@Override
	public void setFocus() {
		btnNewButton.setFocus();
	}
}

			

When you finished this successful you are able to add new persons to your TableViewer via clicking on the Button.

8.2. Delete objects from your table

We will implement that a the current selected persons can be deleted from the list.

Create the command "de.vogella.jface.tableviewer.commands.DeletePerson" with the default handler "de.vogella.jface.tableviewer.commands.DeletePerson". Add the command to your menu.

Implement class "de.vogella.jface.tableviewer.commands.DeletePerson".

				
package de.vogella.jface.tableviewer.commands;

import java.util.Iterator;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.handlers.HandlerUtil;

import de.vogella.jface.tableviewer.View;
import de.vogella.jface.tableviewer.model.ModelProvider;
import de.vogella.jface.tableviewer.model.Person;

public class DeletePerson extends AbstractHandler {
	@SuppressWarnings("unchecked")
	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
		IWorkbenchPage page = window.getActivePage();
		View view = (View) page.findView(View.ID);
		ISelection selection = view.getSite().getSelectionProvider()
				.getSelection();

		if (selection != null && selection instanceof IStructuredSelection) {
			List<Person> persons = ModelProvider.INSTANCE.getPersons();
			IStructuredSelection sel = (IStructuredSelection) selection;

			for (Iterator<Person> iterator = sel.iterator(); iterator.hasNext();) {
				Person person = iterator.next();
				persons.remove(person);
			}
			view.getViewer().refresh();
		}
		return null;
	}
}

			

Please try to delete entries of the table by selection entries and executing "Delete Action".

8.3. Copy data to the system clipboard

This chapter shows how to copy the table data to the system clipboard via a command.

Create the command "de.vogella.jface.tableviewer.commands.CopyPersonClipboard" with the default handler "de.vogella.jface.tableviewer.commands.CopyPersonClipboard". Add the command to the menu.

				
package de.vogella.jface.tableviewer.commands;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

import de.vogella.jface.tableviewer.View;
import de.vogella.jface.tableviewer.model.Person;

public class CopyPersonClipboard extends AbstractHandler {

	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {

		IWorkbenchWindow window = PlatformUI.getWorkbench()
				.getActiveWorkbenchWindow();
		IWorkbenchPage page = window.getActivePage();
		IViewPart view = page.findView(View.ID);
		Clipboard cb = new Clipboard(Display.getDefault());
		ISelection selection = view.getSite().getSelectionProvider()
				.getSelection();
		List<Person> personList = new ArrayList<Person>();
		if (selection != null && selection instanceof IStructuredSelection) {
			IStructuredSelection sel = (IStructuredSelection) selection;
			for (Iterator<Person> iterator = sel.iterator(); iterator.hasNext();) {
				Person person = iterator.next();
				personList.add(person);
			}
		}
		StringBuilder sb = new StringBuilder();
		for (Person person : personList) {
			sb.append(personToString(person));
		}
		TextTransfer textTransfer = TextTransfer.getInstance();
		cb.setContents(new Object[] { sb.toString() },
				new Transfer[] { textTransfer });

		return null;
	}

	private String personToString(Person person) {
		return person.getFirstName() + "\t" + person.getLastName() + "\t"
				+ person.getGender() + "\t" + person.isMarried()
				+ System.getProperty("line.separator");
	}

}

			

Run your application, select a few persons, run your command and paste the result in a text editor, e.g. notepad.

9. Thank you

Please help me to support this article:

Flattr this

10. Questions and Discussion

Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.com Google Group. I have created a short list how to create good questions which might also help you.

11. Download

http://www.vogella.com/articles/EclipseJFaceTable/download/checkedpics.zip The checkbox pictures for the JFace Labelprovider

12. Links and Literature

12.1. Source Code

Source Code of Examples

12.2. JFace Resources

http://www.eclipse.org/articles/Article-Table-viewer/table_viewer.html Building and delivering a table editor with SWT/JFace

http://wiki.eclipse.org/index.php/JFaceSnippets JFace snippets, e.g. small code examples

12.3. vogella Resources

Eclipse RCP Training (German) Eclipse RCP Training with Lars Vogel

Android Tutorial Introduction to Android Programming

GWT Tutorial Program in Java and compile to JavaScript and HTML

Eclipse RCP Tutorial Create native applications in Java

JUnit Tutorial Test your application

Git Tutorial Put everything you have under distributed version control system