Filtering tables in SWT/JFace
October 26, 2012 | 3 min ReadWorking with tables or trees with more than a handful of rows, you quickly find that you need a way to filter or search for content, otherwise they become unusable. The Eclipse workbench offers an out-of-the-box component named FilteredTree which adds an input field to an SWT tree where filter strings can be entered by the user. However, support for SWT tables is missing. This post shows how filter support can be added to tables using the existing capabilities in the workbench.
Convert the table (viewer) to a tree (viewer)
The SWT Tree
is a very powerful widget. Apart from the arrangement of data in a tree hierarchy, it supports multi-column display of tree items. Since the APIs of Tree(Viewer) and Table(Viewer) are quite similar, it is straightforward to convert your Table
into a Tree
.
Wrap the tree into a FilteredTree
Once your code uses a Tree
or a TreeViewer
, it can easily be wrapped into a FilteredTree
:
FilteredTree filteredTree = new FilteredTree(parent, SWT.BORDER, new PatternFilter(), true);
TreeViewer treeViewer = filteredTree.getViewer();
The PatternFilter parameter represents the algorithm that is applied to find matches for a given filter string.
Adapt the PatternFilter
Unfortunately, we’re not quite done yet. The default PatternFilter
is designed to search for matches in the text returned by the TreeViewer
’s label provider:
protected boolean isLeafMatch(Viewer viewer, Object element){
String labelText = ((ILabelProvider) ((StructuredViewer) viewer).getLabelProvider()).getText(element);
if(labelText == null) {
return false;
}
return wordMatches(labelText);
}
Obviously this cannot work for column-based TreeViewer
s since there is no single textual representation of an element but rather a text for each column.
If you are using TreeViewerColumns to define the columns of your tree, the PatternFilter
will need to request the ColumnLabelProviders for each of the single columns. To do this, simply subclass PatternFilter
and override isLeafMatch
:
protected boolean isLeafMatch(final Viewer viewer, final Object element) {
TreeViewer treeViewer = (TreeViewer)viewer;
int numberOfColumns = treeViewer.getTree().getColumnCount();
boolean isMatch = false;
for (int columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) {
ColumnLabelProvider labelProvider = (ColumnLabelProvider)treeViewer.getLabelProvider(columnIndex);
String labelText = labelProvider.getText(element);
isMatch |= wordMatches(labelText);
}
return isMatch;
}
If you are using TreeColumns to define the columns of your tree, the TreeViewer
will need to have an ITableLabelProvider. The isLeafMatch
method can then be adapted to loop over all columns :
protected boolean isLeafMatch(final Viewer viewer, final Object element) {
TreeViewer treeViewer = (TreeViewer)viewer;
int numberOfColumns = treeViewer.getTree().getColumnCount();
ITableLabelProvider labelProvider = (ITableLabelProvider)treeViewer.getLabelProvider();
boolean isMatch = false;
for (int columnIndex = 0; columnIndex < numberOfColumns; columnIndex++) {
String labelText = labelProvider.getColumnText(element, columnIndex);
isMatch |= wordMatches(labelText);
}
return isMatch;
}
By reusing the existing FilteredTree
to filter our tables, we saved the effort to implement, test and maintain our own FilteredTable
. And lazy programmers are good programmers, aren’t they?