Version 3.3
Copyright © 2008, 2009, 2010, 2011, 2012, 2013 Lars Vogel
20.02.2013
| Revision History | |||
|---|---|---|---|
| Revision 0.1 | 01.07.2007 | Lars Vogel |
created |
| Revision 0.2 - 3.3 | 12.09.2007 - 20.02.2013 | Lars Vogel |
bug fixes and enhancements |
Table of Contents
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.
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.update(element, null); } }
The
EditingSupport
implementation defines how the content can be changed.
EditingSupport
returns an object of type
CellEditor
from the
getCellEditor()
method.
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));
From an application design perspective, editing within a table can be cumbersome for the user. If the end user has to edit a lot of data, you should also offer a dialog, wizard or part to edit the data.
JFace Viewer supports filtering of data via the
setFilters()
or
addFilter()
methods. These methods expect
ViewerFilter
objects
as arguments.
For each registered
ViewerFilter
object
the
select()
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
Viewer
to display the
data.
JFace supports sorting of the table content via the
setComparator()
method on the Viewer object. This method expects a
ViewerComparator
object. By 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()); }; });
With the
TableColumnLayout
class you can define the width of columns in the table. This can be
done based on a fixed value or on a percentage.
Using
TableColumnLayout
requires a
Composite
which only contains the table widget. This
Composite
gets the
TableColumnLayout
assigned.
Composite tableComposite = new Composite(parent, SWT.NONE); TableColumnLayout tableColumnLayout = new TableColumnLayout(); tableComposite.setLayout(tableColumnLayout); TableViewer tableViewer = new TableViewer(tableComposite, SWT.BORDER| SWT.FULL_SELECTION);
TableColumnLayout
requires that you define a fixed or relative size for all columns.
// Fixed size
tableColumnLayout.
setColumnData(viewerNameColumn.getColumn(),
new ColumnPixelData(50));
// Relative size
// Last parameter defines if the column is allowed
// to be resized
tableColumnLayout.
setColumnData(viewerNameColumn.getColumn(),
new ColumnWeightData(20, 200, true));
It is possible to use a
StyledCellLabelProvider
for a very flexible styling of your text.
StyledCellLabelProvider
extends
CellLabelProvider
and allows you to draw via its
update()
method.
The following example shows 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); } });
As a result the number "15" will be highlighted as presented in the following screenshot.

The
OwnerDrawLabelProvider
class is a label provider that handles custom draws.
The following example draws a text and an Image into the cell.
// ICON is an Image col.setLabelProvider(new OwnerDrawLabelProvider() { @Override protected void measure(Event event, Object element) { Rectangle rectangle = ICON.getBounds(); event. setBounds(new Rectangle(event.x, event.y, rectangle.width + 200 , rectangle.height)); } @Override protected void paint(Event event, Object element) { Rectangle bounds = event.getBounds(); event.gc.drawText("Hello", bounds.x, bounds.y); Point point = event.gc.stringExtent("Hello"); event.gc.drawImage(ICON, bounds.x + 5 + point.x, bounds.y); } });

You can add a
Menu
to your table. This
Menu
can get a
MenuItem
for each column and you can use it for example to hide and show
columns based on
the width setting.
// Define the menu and assign to the table headerMenu = new Menu(viewer.getTable()); 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); } } }); }
You can
use tooltips for the cells of the
Viewer. You have to activate tooltips for the Viewer to get this working.
// 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; //msec } @Override public int getToolTipTimeDisplayed(Object object) { return 5000; //msec } });
If you have a huge number of lines which you want to display in the table you can use a LazyContentProvider. The following code demonstrates its usage.
private class MyContentProvider implements ILazyContentProvider { private TableViewer viewer; private YourModel[] elements; public MyContentProvider(TableViewer viewer) { this.viewer = viewer; } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { this.elements = (YourModel[]) newInput; } public void updateElement(int index) { viewer.replace(elements[index], index); } }
// Create your table with the virtual flag final TableViewer v = new TableViewer(shell, SWT.VIRTUAL); // Create TableColumns and LabelProvider for them // .. // .. // Now the special settings for the lazy Content provider v.setContentProvider(new MyContentProvider(v)); v.setUseHashlookup(true); YourModel[] model = createModel(); v.setInput(model); // You must explizitly set the items count v.setItemCount(model.length);
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.
Create the following new classes
in the new
de.vogella.jface.tableviewer.edit
package.
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.update(element, null); } }
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.update(element, null); } }
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.update(element, null); } }
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.update(element, null); } }
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.
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
createPartControl()
method.
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.
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.
If you are not familiar with Eclipse Commands, please have a look at the following tutorial: Eclipse Commands Tutorial
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 the
de.vogella.jface.tableviewer.dialogs
package
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.
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".
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.
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.
http://www.vogella.com/articles/EclipseJFaceTable/download/checkedpics.zip The checkbox pictures for the JFace Labelprovider
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
vogella Training Android and Eclipse Training from the vogella team
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