init(composite, int, PatternFilter)
method is
+ * called in the overriding constructor.
+ *
+ * @param parent
+ * the parent Composite
+ * @see #init(int, PatternFilter)
+ */
+ protected FilteredTable(Composite parent) {
+ super(parent, SWT.NONE, DEFAULT_REFRESH_TIME);
+ }
+
+ @Override
+ protected void init(int tableStyle, PatternFilter filter) {
+ setShowFilterControls(true);
+ super.init(tableStyle, filter);
+ createRefreshJob();
+ setInitialText(E4DialogMessages.FilteredTable_FilterMessage);
+ }
+
+ @Override
+ protected void createControl(Composite parent, int tableStyle) {
+ super.createControl(parent, tableStyle);
+
+ tableComposite = new Composite(this, SWT.NONE);
+ GridLayout tableCompositeLayout = new GridLayout();
+ tableCompositeLayout.marginHeight = 0;
+ tableCompositeLayout.marginWidth = 0;
+ tableComposite.setLayout(tableCompositeLayout);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ tableComposite.setLayoutData(data);
+ createTableControl(tableComposite, tableStyle);
+ }
+
+ /**
+ * Creates and set up the table and table viewer. This method calls
+ * {@link #doCreateTableViewer(Composite, int)} to create the table viewer.
+ * Subclasses should override {@link #doCreateTableViewer(Composite, int)}
+ * instead of overriding this method.
+ *
+ * @param parent parent {@code Composite}
+ * @param style SWT style bits used to create the table
+ * @return the table
+ */
+ protected Control createTableControl(Composite parent, int style) {
+ tableViewer = doCreateTableViewer(parent, style);
+ tableViewer.setUseHashlookup(true);
+ GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ tableViewer.getControl().setLayoutData(data);
+ tableViewer.getControl().addDisposeListener(e -> refreshJob.cancel());
+ if (tableViewer instanceof NotifyingTableViewer) {
+ getPatternFilter().setUseCache(true);
+ }
+ tableViewer.addFilter(getPatternFilter());
+ return tableViewer.getControl();
+ }
+
+ /**
+ * Creates the table viewer. Subclasses may override.
+ *
+ * @param parent the parent composite
+ * @param style SWT style bits used to create the table viewer
+ * @return the table viewer
+ */
+ protected TableViewer doCreateTableViewer(Composite parent, int style) {
+ return new NotifyingTableViewer(parent, style);
+ }
+
+ /**
+ * Return the first item in the table that matches the filter pattern.
+ *
+ * @return the first matching TableItem
+ */
+ private TableItem getFirstMatchingItem(TableItem[] items) {
+ for (TableItem item : items) {
+ if (getPatternFilter().isLeafMatch(tableViewer, item.getData())
+ && getPatternFilter().isElementSelectable(item.getData())) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Create the refresh job for the receiver.
+ */
+ private void createRefreshJob() {
+ refreshJob = doCreateRefreshJob();
+ refreshJob.setSystem(true);
+ }
+
+ /**
+ * Creates a workbench job that will refresh the table based on the current
+ * filter text. Subclasses may override.
+ *
+ * @return a job that can be scheduled to refresh the table
+ */
+ protected Job doCreateRefreshJob() {
+ return new BasicUIJob("Refresh Filter", getDisplay()) {//$NON-NLS-1$
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (tableViewer.getControl().isDisposed()) {
+ return Status.CANCEL_STATUS;
+ }
+
+ String text = getFilterString();
+ if (text == null) {
+ return Status.OK_STATUS;
+ }
+
+ boolean initial = initialText != null && initialText.equals(text);
+ if (initial) {
+ getPatternFilter().setPattern(null);
+ } else if (text != null) {
+ getPatternFilter().setPattern(text);
+ }
+
+ tableViewer.refresh(true);
+
+ return Status.OK_STATUS;
+ }
+ };
+ }
+
+ /**
+ * Creates the filter text and adds listeners. This method calls
+ * {@link #doCreateFilterText(Composite)} to create the text control. Subclasses
+ * should override {@link #doCreateFilterText(Composite)} instead of overriding
+ * this method.
+ *
+ * @param parent Composite
of the filter text
+ */
+ @Override
+ protected void createFilterText(Composite parent) {
+ super.createFilterText(parent);
+ filterText.getAccessible().addAccessibleListener(new AccessibleAdapter() {
+ @Override
+ public void getName(AccessibleEvent e) {
+ String filterTextString = filterText.getText();
+ if (filterTextString.isEmpty() || filterTextString.equals(initialText)) {
+ e.result = initialText;
+ } else {
+ e.result = NLS.bind(E4DialogMessages.FilteredTable_AccessibleListenerFiltered,
+ new String[] { filterTextString, String.valueOf(getFilteredItemsCount()) });
+ }
+ }
+
+ /**
+ * Return the number of filtered items
+ *
+ * @return int
+ */
+ private int getFilteredItemsCount() {
+ return getViewer().getTable().getItems().length;
+ }
+ });
+
+ filterText.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ // on a CR we want to transfer focus to the list
+ boolean hasItems = getViewer().getTable().getItemCount() > 0;
+ if (hasItems && e.keyCode == SWT.ARROW_DOWN) {
+ tableViewer.getTable().setFocus();
+ return;
+ }
+ }
+ });
+
+ // enter key set focus to table
+ filterText.addTraverseListener(e -> {
+ if (e.detail == SWT.TRAVERSE_RETURN) {
+ e.doit = false;
+ if (getViewer().getTable().getItemCount() == 0) {
+ Display.getCurrent().beep();
+ } else {
+ // if the initial filter text hasn't changed, do not try
+ // to match
+ boolean hasFocus = getViewer().getTable().setFocus();
+ boolean textChanged = !getInitialText().equals(filterText.getText().trim());
+ if (hasFocus && textChanged && filterText.getText().trim().length() > 0) {
+ Table table = getViewer().getTable();
+ TableItem item;
+ if (table.getSelectionCount() > 0) {
+ item = getFirstMatchingItem(table.getSelection());
+ } else {
+ item = getFirstMatchingItem(table.getItems());
+ }
+ if (item != null) {
+ table.setSelection(new TableItem[] { item });
+ ISelection sel = getViewer().getSelection();
+ getViewer().setSelection(sel, true);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ protected Text doCreateFilterText(Composite parent) {
+ return new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
+ }
+
+ @Override
+ protected void textChanged() {
+ // cancel currently running job first, to prevent unnecessary redraw
+ refreshJob.cancel();
+ refreshJob.schedule(getRefreshJobDelay());
+ }
+
+ /**
+ * Set the background for the widgets that support the filter text area.
+ *
+ * @param background background Color
to set
+ */
+ @Override
+ public void setBackground(Color background) {
+ super.setBackground(background);
+ if (filterComposite != null) {
+ filterComposite.setBackground(background);
+ }
+ }
+
+ @Override
+ public final PatternFilter getPatternFilter() {
+ return (PatternFilter) super.getPatternFilter();
+ }
+
+ @Override
+ public TableViewer getViewer() {
+ return tableViewer;
+ }
+
+ /**
+ * Return a bold font if the given element matches the given pattern. Clients
+ * can opt to call this method from a Viewer's label provider to get a bold font
+ * for which to highlight the given element in the table.
+ *
+ * @param element element for which a match should be determined
+ * @param table FilteredTable in which the element resides
+ * @param filter PatternFilter which determines a match
+ *
+ * @return bold font
+ */
+ public static Font getBoldFont(Object element, FilteredTable table, PatternFilter filter) {
+ String filterText = table.getFilterString();
+
+ if (filterText == null) {
+ return null;
+ }
+
+ // Do nothing if it's empty string
+ String initialText = table.getInitialText();
+ if (!filterText.isEmpty() && !filterText.equals(initialText)) {
+ if (table.getPatternFilter() != filter) {
+ boolean initial = initialText != null && initialText.equals(filterText);
+ if (initial) {
+ filter.setPattern(null);
+ } else if (filterText != null) {
+ filter.setPattern(filterText);
+ }
+ }
+ if (filter.isElementVisible(table.getViewer(), element) && filter.isLeafMatch(table.getViewer(), element)) {
+ return JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT);
+ }
+ }
+ return null;
+ }
+
+ public boolean isShowFilterControls() {
+ return showFilterControls;
+ }
+
+ public void setShowFilterControls(boolean showFilterControls) {
+ this.showFilterControls = showFilterControls;
+ if (filterComposite != null) {
+ Object filterCompositeLayoutData = filterComposite.getLayoutData();
+ if (filterCompositeLayoutData instanceof GridData) {
+ ((GridData) filterCompositeLayoutData).exclude = !isShowFilterControls();
+ } else if (filterCompositeLayoutData instanceof RowData) {
+ ((RowData) filterCompositeLayoutData).exclude = !isShowFilterControls();
+ }
+ filterComposite.setVisible(isShowFilterControls());
+ layout();
+ }
+ }
+
+ /**
+ * Custom table viewer subclass that clears the caches in patternFilter on any
+ * change to the table. See bug 187200.
+ */
+ class NotifyingTableViewer extends TableViewer {
+
+ public NotifyingTableViewer(Composite parent, int style) {
+ super(parent, style);
+ }
+
+ @Override
+ public void add(Object element) {
+ getPatternFilter().clearCaches();
+ super.add(element);
+ }
+
+ @Override
+ public void add(Object[] elements) {
+ getPatternFilter().clearCaches();
+ super.add(elements);
+ }
+
+ @Override
+ protected void inputChanged(Object input, Object oldInput) {
+ getPatternFilter().clearCaches();
+ super.inputChanged(input, oldInput);
+ }
+
+ @Override
+ public void insert(Object element, int position) {
+ getPatternFilter().clearCaches();
+ super.insert(element, position);
+ }
+
+ @Override
+ public void refresh() {
+ getPatternFilter().clearCaches();
+ super.refresh();
+ }
+
+ @Override
+ public void refresh(boolean updateLabels) {
+ getPatternFilter().clearCaches();
+ super.refresh(updateLabels);
+ }
+
+ @Override
+ public void refresh(Object element) {
+ getPatternFilter().clearCaches();
+ super.refresh(element);
+ }
+
+ @Override
+ public void refresh(Object element, boolean updateLabels) {
+ getPatternFilter().clearCaches();
+ super.refresh(element, updateLabels);
+ }
+
+ @Override
+ public void remove(Object element) {
+ getPatternFilter().clearCaches();
+ super.remove(element);
+ }
+
+ @Override
+ public void remove(Object[] elements) {
+ getPatternFilter().clearCaches();
+ super.remove(elements);
+ }
+
+ @Override
+ public void replace(Object element, int index) {
+ getPatternFilter().clearCaches();
+ super.replace(element, index);
+ }
+
+ @Override
+ public void setContentProvider(IContentProvider provider) {
+ getPatternFilter().clearCaches();
+ super.setContentProvider(provider);
+ }
+ }
+}
diff --git a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/PatternFilter.java b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/PatternFilter.java
index a1d2113beaa..377f52aad1b 100644
--- a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/PatternFilter.java
+++ b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/filteredtree/PatternFilter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2015 IBM Corporation and others.
+ * Copyright (c) 2004, 2025 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -274,11 +274,12 @@ public boolean isElementVisible(Viewer viewer, Object element) {
* text
*/
protected boolean isParentMatch(Viewer viewer, Object element) {
- Object[] children = ((ITreeContentProvider) ((AbstractTreeViewer) viewer)
- .getContentProvider()).getChildren(element);
+ if (viewer instanceof AbstractTreeViewer
+ && ((AbstractTreeViewer) viewer).getContentProvider() instanceof ITreeContentProvider) {
+ Object[] children = ((ITreeContentProvider) ((AbstractTreeViewer) viewer).getContentProvider())
+ .getChildren(element);
- if ((children != null) && (children.length > 0)) {
- return isAnyVisible(viewer, element, children);
+ return children != null && children.length > 0 && isAnyVisible(viewer, element, children);
}
return false;
}
diff --git a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/E4DialogMessages.java b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/E4DialogMessages.java
index 1acd9474aab..b0d0c1aa817 100644
--- a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/E4DialogMessages.java
+++ b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/E4DialogMessages.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2014 vogella GmbH and others.
+ * Copyright (c) 2014, 2025 vogella GmbH and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -21,6 +21,8 @@
public class E4DialogMessages extends NLS {
private static final String BUNDLE_NAME = "org.eclipse.e4.ui.dialogs.textbundles.messages";//$NON-NLS-1$
+ public static String FilteredTable_AccessibleListenerFiltered;
+ public static String FilteredTable_FilterMessage;
public static String FilteredTree_AccessibleListenerClearButton;
public static String FilteredTree_ClearToolTip;
public static String FilteredTree_FilterMessage;
diff --git a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/messages.properties b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/messages.properties
index 2a5a5d1a2e8..aa50092befa 100644
--- a/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/messages.properties
+++ b/bundles/org.eclipse.e4.ui.dialogs/src/org/eclipse/e4/ui/dialogs/textbundles/messages.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2000, 2014 IBM Corporation and others.
+# Copyright (c) 2000, 2025 IBM Corporation and others.
#
# This program and the accompanying materials
# are made available under the terms of the Eclipse Public License 2.0
@@ -19,6 +19,8 @@
# Based on /org.eclipse.ui.workbench/Eclipse UI/org/eclipse/ui/internal/messages.properties
+FilteredTable_AccessibleListenerFiltered={0} {1} matches.
+FilteredTable_FilterMessage=type filter text
FilteredTree_AccessibleListenerClearButton=Clear filter field
FilteredTree_ClearToolTip=Clear
FilteredTree_FilterMessage=type filter text
diff --git a/examples/org.eclipse.ui.examples.filter/.classpath b/examples/org.eclipse.ui.examples.filter/.classpath
new file mode 100644
index 00000000000..c5932f42c7e
--- /dev/null
+++ b/examples/org.eclipse.ui.examples.filter/.classpath
@@ -0,0 +1,11 @@
+
+November 30, 2017
++ The Eclipse Foundation makes available all content in this plug-in + ("Content"). Unless otherwise indicated below, the Content + is provided to you under the terms and conditions of the Eclipse + Public License Version 2.0 ("EPL"). A copy of the EPL is + available at http://www.eclipse.org/legal/epl-2.0. + For purposes of the EPL, "Program" will mean the Content. +
+ ++ If you did not receive this Content directly from the Eclipse + Foundation, the Content is being redistributed by another party + ("Redistributor") and different terms and conditions may + apply to your use of any object code in the Content. Check the + Redistributor's license that was provided with the Content. If no such + license exists, contact the Redistributor. Unless otherwise indicated + below, the terms and conditions of the EPL still apply to any source + code in the Content and such source code may be obtained at http://www.eclipse.org. +
+ + + \ No newline at end of file diff --git a/examples/org.eclipse.ui.examples.filter/build.properties b/examples/org.eclipse.ui.examples.filter/build.properties new file mode 100644 index 00000000000..adac29f50d5 --- /dev/null +++ b/examples/org.eclipse.ui.examples.filter/build.properties @@ -0,0 +1,8 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + plugin.properties,\ + plugin.xml,\ + about.html,\ + . +src.includes = about.html diff --git a/examples/org.eclipse.ui.examples.filter/plugin.properties b/examples/org.eclipse.ui.examples.filter/plugin.properties new file mode 100644 index 00000000000..9fa8ad8205c --- /dev/null +++ b/examples/org.eclipse.ui.examples.filter/plugin.properties @@ -0,0 +1,7 @@ +#Properties file for org.eclipse.ui.examples.filter +Bundle-Vendor = Eclipse.org +Bundle-Name = Eclipse Filtered Viewer + +view.tree.name = Filtered Tree View +view.table.name = Filtered Table View +view.table.virtual.name = Filtered (Virtual) Table View \ No newline at end of file diff --git a/examples/org.eclipse.ui.examples.filter/plugin.xml b/examples/org.eclipse.ui.examples.filter/plugin.xml new file mode 100644 index 00000000000..451dd217b11 --- /dev/null +++ b/examples/org.eclipse.ui.examples.filter/plugin.xml @@ -0,0 +1,26 @@ + + +