Friday, August 28, 2009

Adding dynamic filter to trees in RCP (Eclipse 3.4 - Ganymede)

Introduction:

Dynamic filter provides a search box above the tree and filters elements on the tree as user types it. In this article, I will talk about how to add dynamic filtering capabilities to your tree. I will also explain about how to customize the default filter.

Adding dynamic filter:

Adding dynamic filter to the tree is very easy. Instead of creating treeviewer directly, you need to create FilteredTree (org.eclipse.ui.dialogs.FilteredTree.FilteredTree). The below code creates FilteredTree using PatterFilter.

PatternFilter filter = new PatternFilter();
FilteredTree tree = new FilteredTree(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL, filter);
this.treeViewer = tree.getViewer();

and that's all you need to do to have dynamic filtering in your tree. Isn't that amazing! I know how hard it is to do in Swing.

When your view initially comes up, it would have a text box above your tree. By default it says "type filter text". But you can change that by calling setInitialText(String text) method on the FilteredTree.

Customizing Filter:

If you see above, the constructor for FilteredTree takes PatternFilter. By default, it does word matching and it matches the beginning of every word in your text. In the previous article, we discussed about how to create filters. You would create a similar filter, but which extends PatternFilter (implement just the select method like in any filter) and pass it to FilteredTree.

To customize the pattern in PatternFilter, you could use the void setPattern(String pattern) in PatternFilter. You can set the pattern string using which this filter should select elements in the viewer. This is a public method.

You can also override methods like boolean isElementSelectable(Object element) (Answers whether the given element is a valid selection in the filtered tree), boolean isElementVisible(Viewer viewer, Object element) (Answers whether the given element in the given viewer matches the filter pattern), boolean isParentMatch(Viewer viewer, Object element), boolean isLeafMatch(Viewer viewer, Object element) and boolean wordMatches(String text) to customize the filter.

wordMatches tells if any of the words in the given text satisfy the filter criteria. You could override this method if you want your filter to behave differently. For example, instead of matching every word, you can override this method so that it matches the whole string or just the first word in your string etc.,

Conclusion:

We saw how to create a dynamic filter and attach it to the tree. Also, we saw how to customize the pattern filter.

4 comments:

Unknown said...

Hi Raja,

I'm working using a FilteredTree and extending the PatterFilter with my own filter. My goal is to only filter the expanded nodes and I was able to do that with something like:

public class NavigatorFilter extends PatternFilter {
@Override
protected boolean isLeafMatch(Viewer viewer, Object element) {

boolean expanded = false;
expanded = ((TreeViewer)viewer).getExpandedState(((Model)element).getParent());

if(!expanded) {
return false;
}

String name = ((Model) element).getName();
return wordMatches(name);
}
}


Although this gives me the correct results, there's a performance issue. This method still goes through each node in the tree to see if the parent is expanded, which can take a long time if you have a lot of leaf nodes. What I would like to do is only process nodes if the parent is expanded, and ignore the child nodes of non-expanded parents. Do you think this is possible by extending PatterFilter?

Thank you!

Amit said...

Hi,

I am developing an application in which I need to filter my treeviewer and that's why I have used the PatternFilter which pretty much provides everything I needed.

Now, I need to filter the treeviewer in a different way.

Let's say I have a treeviewer with the following items in it.


Parent1
--Child1
----ChildChild1
---------ChildChildChild1
Parent2
--Child2
----ChildChild2
---------ChildChildChild2


So now if I use filter text "Parent2", it is only displaying Parent2 but I want to view all its child nodes along with it.
That means while filtering using text "Parent2", I need to see the following in the treeviewer.


Parent2
--Child 2
----ChildChild2
---------ChildChildChild2


Awaiting for further assistance.

Unknown said...

Here onlooker is the blue plate or tree onlooker from your view. ParentElement is the child bearer node (for the node subsequent selected) in the tree and for blue plate it is the whole carefree of the snack principle is the avant-garde node (to be selected) in the tree and for table it is the advanced row Custom Literature Review Writing in the table. Element and parentElement are actually epitome objects which comprise the blithe in your cheerful provider. This approach needs to revive true if the node/row needs to be engaged based on the filter criteria.

Shramik said...

Hi Amit,
Answer for your question

You need to override the isElementVisible so that you can show all nested child element if parent element match.

for e.g

@Override
public boolean isElementVisible(Viewer viewer, Object element) {
KindOFAnyField child = (KindOFAnyField) element;
boolean parentMataches = isLeafMatch(viewer, child.getParent());

// child.getParent() you need to return parent of child it may be null if dont have parent or use StructuredViewer to find out the parent object.

if (parentMataches) {
return true;
}

// Default behavior if parent not match.
return isParentMatch(viewer, element) || isLeafMatch(viewer, element);
}