diff --git a/bundles/org.eclipse.jface.text/META-INF/MANIFEST.MF b/bundles/org.eclipse.jface.text/META-INF/MANIFEST.MF index 146f03a2a43..6fbefbc2f46 100644 --- a/bundles/org.eclipse.jface.text/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.jface.text/META-INF/MANIFEST.MF @@ -2,13 +2,13 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jface.text -Bundle-Version: 3.26.100.qualifier +Bundle-Version: 3.27.0.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: org.eclipse.jface.contentassist, org.eclipse.jface.contentassist.images, - org.eclipse.jface.internal.text;x-internal:=true, + org.eclipse.jface.internal.text;x-friends:="org.eclipse.ui.workbench", org.eclipse.jface.internal.text.codemining;x-internal:=true, org.eclipse.jface.internal.text.html;x-friends:="org.eclipse.ant.ui, org.eclipse.jdt.ui, org.eclipse.ltk.ui.refactoring, org.eclipse.pde.ui, org.eclipse.ui.editors, org.eclipse.xtext.ui", org.eclipse.jface.internal.text.link.contentassist;x-internal:=true, diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/misc/TextMatcher.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TextMatcher.java similarity index 99% rename from bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/misc/TextMatcher.java rename to bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TextMatcher.java index 6b07140a2f1..59bb8e849aa 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/misc/TextMatcher.java +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/internal/text/TextMatcher.java @@ -8,7 +8,7 @@ * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ -package org.eclipse.ui.internal.misc; +package org.eclipse.jface.internal.text; import java.util.ArrayList; import java.util.Arrays; diff --git a/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractFilteredStructuredViewer.java b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractFilteredStructuredViewer.java new file mode 100644 index 00000000000..b9b8fbb1985 --- /dev/null +++ b/bundles/org.eclipse.jface.text/src/org/eclipse/jface/text/AbstractFilteredStructuredViewer.java @@ -0,0 +1,368 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + ******************************************************************************/ +package org.eclipse.jface.text; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.MouseAdapter; +import org.eclipse.swt.events.MouseEvent; +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.Text; + +import org.eclipse.core.runtime.jobs.Job; + +import org.eclipse.jface.viewers.StructuredViewer; + +/** + * The abstract base class for a composite containing both a column viewer and a text widget. The + * text drives the filter of the viewer. + * + * @since 3.27 + */ +public abstract class AbstractFilteredStructuredViewer extends Composite { + + /** + * The filter text widget to be used by this viewer. This value may be {@code null} if there is + * no filter widget, or if the controls have not yet been created. + */ + protected Text filterText; + + /** + * The Composite on which the filter controls are created. This is used to set the background + * color of the filter controls to match the surrounding controls. + */ + protected Composite filterComposite; + + /** + * The text to initially show in the filter text control. + */ + protected String initialText= ""; //$NON-NLS-1$ + + /** + * The job used to refresh the viewer. + */ + private Job refreshJob; + + /** + * The parent composite of the filtered viewer. + */ + protected Composite parent; + + /** + * Whether or not to show the filter controls (text and clear button). + */ + protected boolean showFilterControls; + + /** + * Tells whether this filtered viewer is used to make quick selections. In this mode the first + * match in the viewer is automatically selected while filtering and the 'Enter' key is not used + * to move the focus to the viewer. + */ + private boolean quickSelectionMode= false; + + /** + * Time for refresh job delay in terms of expansion in long value + */ + private final long refreshJobDelayInMillis; + + protected AbstractFilteredStructuredViewer(Composite parent, int style, long refreshJobDelayInMillis) { + super(parent, style); + this.parent= parent; + this.refreshJobDelayInMillis= refreshJobDelayInMillis; + } + + /** + * Create the filtered viewer. + * + * @param style the style bits for the {@link StructuredViewer}. + */ + protected void init(int style) { + showFilterControls= isShowFilterControls(); + createControl(parent, style); + createRefreshJob(); + setFont(parent.getFont()); + getViewer().getControl().addDisposeListener(e -> refreshJob.cancel()); + } + + /** + * Indicates whether the filter controls (text and clear button) should be shown. + * + * @return {@code true}, if the filter controls should be shown, otherwise {@code false}. + */ + protected abstract boolean isShowFilterControls(); + + /** + * Get the quick selection mode. + * + * @return {@code true}, if enabled otherwise {@code false}. + */ + protected boolean isQuickSelectionMode() { + return quickSelectionMode; + } + + /** + * Create the filtered viewer's controls. Subclasses should override. + * + * @param parentComposite the parent + * @param style SWT style bits used to create the viewer + */ + protected void createControl(Composite parentComposite, int style) { + GridLayout layout= new GridLayout(); + layout.marginHeight= 0; + layout.marginWidth= 0; + setLayout(layout); + + if (parentComposite.getLayout() instanceof GridLayout) { + setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + } + + if (showFilterControls) { + filterComposite= new Composite(this, SWT.NONE); + GridLayout filterLayout= new GridLayout(); + filterLayout.marginHeight= 0; + filterLayout.marginWidth= 0; + filterComposite.setLayout(filterLayout); + filterComposite.setFont(parentComposite.getFont()); + + createFilterControls(filterComposite); + filterComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); + } + } + + /** + * Create the filter controls. By default, a text and corresponding tool bar button that clears + * the contents of the text is created. Subclasses may override. + * + * @param parentComposite parent {@link Composite} of the filter controls + * @return the {@link Composite} that contains the filter controls + */ + protected Composite createFilterControls(Composite parentComposite) { + createFilterText(parentComposite); + return parentComposite; + } + + /** + * Create the refresh job for the receiver. + */ + private void createRefreshJob() { + refreshJob= doCreateRefreshJob(); + refreshJob.setSystem(true); + } + + /** + * Creates a workbench job that will refresh the viewer based on the current filter text. + * Subclasses may override. + * + * @return a workbench job that can be scheduled to refresh the viewer + */ + protected abstract Job doCreateRefreshJob(); + + /** + * 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 parentComposite {@link Composite} of the filter text + */ + protected void createFilterText(Composite parentComposite) { + filterText= doCreateFilterText(parentComposite); + + filterText.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + /* + * Running in an asyncExec because the selectAll() does not appear to work when + * using mouse to give focus to text. + */ + Display display= filterText.getDisplay(); + display.asyncExec(() -> { + if (!filterText.isDisposed()) { + if (getInitialText().equals(filterText.getText().trim())) { + filterText.selectAll(); + } + } + }); + } + + @Override + public void focusLost(FocusEvent e) { + if (filterText.getText().equals(initialText)) { + //We cannot call clearText() due to + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=260664 + doClearText(); + } + } + }); + + filterText.addMouseListener(new MouseAdapter() { + @Override + public void mouseDown(MouseEvent e) { + if (filterText.getText().equals(initialText)) { + //We cannot call clearText() due to + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=260664 + doClearText(); + } + } + }); + + filterText.addModifyListener(e -> textChanged()); + + GridData gridData= new GridData(SWT.FILL, SWT.CENTER, true, false); + filterText.setLayoutData(gridData); + } + + /** + * Creates the text control for entering the filter text. Subclasses may override. + * + * @param parentComposite the parent composite + * @return the text widget + */ + protected Text doCreateFilterText(Composite parentComposite) { + return new Text(parentComposite, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL); + } + + /** + * Update the receiver after the text has changed. + */ + protected void textChanged() { + // cancel currently running job first, to prevent unnecessary redraw + refreshJob.cancel(); + refreshJob.schedule(getRefreshJobDelay()); + } + + /** + * Return the time delay that should be used when scheduling the filter refresh job. Subclasses + * may override. + * + * @return a time delay in milliseconds before the job should run + */ + protected long getRefreshJobDelay() { + return refreshJobDelayInMillis; + } + + /** + * Clears the text in the filter text widget. + */ + protected void clearText() { + doClearText(); + } + + private void doClearText() { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=260664 + setFilterText(""); //$NON-NLS-1$ + textChanged(); + } + + /** + * Set the text in the filter control. + * + * @param filterText the text to set. + */ + protected void setFilterText(String filterText) { + if (this.filterText != null) { + this.filterText.setText(filterText); + selectAll(); + } + } + + /** + * Get the structured viewer of the receiver. + * + * @return the structured viewer + */ + public abstract StructuredViewer getViewer(); + + /** + * Get the filter text for the receiver, if it was created. Otherwise return {@code null}. + * + * @return the filter Text, or null if it was not created + */ + public Text getFilterControl() { + return filterText; + } + + /** + * Convenience method to return the text of the filter control. If the text widget is not + * created, then null is returned. + * + * @return String in the text, or null if the text does not exist + */ + protected String getFilterString() { + return filterText != null ? filterText.getText() : null; + } + + /** + * Set the text that will be shown until the first focus. A default value is provided, so this + * method only need be called if overriding the default initial text is desired. + * + * @param text initial text to appear in text field + */ + public void setInitialText(String text) { + initialText= text; + if (filterText != null) { + filterText.setMessage(text); + if (filterText.isFocusControl()) { + setFilterText(initialText); + textChanged(); + } else { + getDisplay().asyncExec(() -> { + if (!filterText.isDisposed() && filterText.isFocusControl()) { + setFilterText(initialText); + textChanged(); + } + }); + } + } else { + setFilterText(initialText); + textChanged(); + } + } + + /** + * Sets whether this filtered viewer is used to make quick selections. In this mode the first + * match in the viewer is automatically selected while filtering and the 'Enter' key is not used + * to move the focus to the viewer. + *
+ * By default, this is set to {@code false}. + *
+ * + * @param enabled {@code true} if this filtered viewer is used to make quick selections, + * {@code false} otherwise + */ + public void setQuickSelectionMode(boolean enabled) { + this.quickSelectionMode= enabled; + } + + /** + * Select all text in the filter text field. + */ + protected void selectAll() { + if (filterText != null) { + filterText.selectAll(); + } + } + + /** + * Get the initial text for the receiver. + * + * @return String + */ + protected String getInitialText() { + return initialText; + } +} \ No newline at end of file diff --git a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF index c0d826a9b67..fbef539e118 100644 --- a/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.ui.workbench/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.ui.workbench; singleton:=true -Bundle-Version: 3.134.100.qualifier +Bundle-Version: 3.135.0.qualifier Bundle-Activator: org.eclipse.ui.internal.WorkbenchPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName @@ -128,6 +128,8 @@ Import-Package: com.ibm.icu.util, org.eclipse.e4.ui.workbench, org.eclipse.e4.ui.workbench.modeling, org.eclipse.e4.ui.workbench.renderers.swt, + org.eclipse.jface.internal.text, + org.eclipse.jface.text, org.osgi.service.event;version="[1.2.0,2.0.0)", org.osgi.service.event.propertytypes;version="[1.4.0,2.0.0)", org.w3c.dom, diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java index 56e69c48c5f..ae69574e5fa 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredList.java @@ -23,6 +23,7 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; +import org.eclipse.jface.internal.text.TextMatcher; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; @@ -36,7 +37,6 @@ import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.ui.internal.WorkbenchMessages; -import org.eclipse.ui.internal.misc.TextMatcher; import org.eclipse.ui.internal.util.Util; import org.eclipse.ui.progress.WorkbenchJob; diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTable.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTable.java new file mode 100644 index 00000000000..1cab82fda22 --- /dev/null +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTable.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2024 Patrick Ziegler and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Patrick Ziegler - initial API and implementation + ******************************************************************************/ +package org.eclipse.ui.dialogs; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.internal.text.TextMatcher; +import org.eclipse.jface.text.AbstractFilteredStructuredViewer; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; +import org.eclipse.ui.IWorkbenchPreferenceConstants; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.internal.WorkbenchMessages; +import org.eclipse.ui.progress.WorkbenchJob; + +/** + * A simple control that provides a text widget and a table viewer. The contents + * of the text widget are used to drive a TextMatcher that is on the viewer. + * + * @since 3.135 + */ +public class FilteredTable extends AbstractFilteredStructuredViewer { + + /** + * Default time for refresh job delay in ms + */ + private static final long DEFAULT_REFRESH_TIME = 200; + + private TableViewer tableViewer; + private TextMatcher matcher; + + public FilteredTable(Composite parent, int style) { + this(parent, style, DEFAULT_REFRESH_TIME); + } + + public FilteredTable(Composite parent, int style, long refreshTime) { + super(parent, style, refreshTime); + init(style); + } + + @Override + protected void createControl(Composite parent, int treeStyle) { + super.createControl(parent, treeStyle); + + Composite tableComposite = new Composite(this, SWT.NONE); + GridLayout treeCompositeLayout = new GridLayout(); + treeCompositeLayout.marginHeight = 0; + treeCompositeLayout.marginWidth = 0; + tableComposite.setLayout(treeCompositeLayout); + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + tableComposite.setLayoutData(data); + createTableControl(tableComposite, treeStyle); + } + + @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(WorkbenchMessages.FilteredTree_AccessibleListenerFiltered, + new String[] { filterTextString, String.valueOf(getFilteredItemsCount()) }); + } + } + + private int getFilteredItemsCount() { + return getViewer().getTable().getItemCount(); + } + }); + + 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) { + getViewer().getTable().setFocus(); + return; + } + } + }); + + // enter key set focus to tree + filterText.addTraverseListener(e -> { + if (isQuickSelectionMode()) { + return; + } + if (e.detail == SWT.TRAVERSE_RETURN) { + e.doit = false; + updateTableSelection(true); + } + }); + } + + /** + * Updates the selection in the tree, based on the filter text. + * + * @param setFocus {@code true} if the focus should be set on the tree, + * {@code false} otherwise + */ + private void updateTableSelection(boolean setFocus) { + Table table = tableViewer.getTable(); + if (table.getItemCount() != 0) { + // if the initial filter text hasn't changed, do not try + // to match + boolean hasFocus = setFocus ? table.setFocus() : true; + boolean textChanged = !getInitialText().equals(filterText.getText().trim()); + if (hasFocus && textChanged && filterText.getText().trim().length() > 0) { + TableItem item; + if (table.getSelectionCount() > 0) { + item = getFirstMatchingItem(table.getSelection()); + } else { + item = getFirstMatchingItem(table.getItems()); + } + if (item != null) { + table.setSelection(new TableItem[] { item }); + tableViewer.setSelection(tableViewer.getSelection(), true); + } + } + } + } + + /** + * Return the first item in the tree that matches the filter pattern. + * + * @return the first matching TreeItem + */ + private TableItem getFirstMatchingItem(TableItem[] items) { + for (TableItem item : items) { + if (matcher == null) { + return item; + } + + ILabelProvider labelProvider = (ILabelProvider) getViewer().getLabelProvider(); + if (matcher.match(labelProvider.getText(item.getData()))) { + return item; + } + } + return null; + } + + /** + * 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 parentComposite
+ * @param style SWT style bits used to create the table
+ * @return the table
+ */
+ protected Table createTableControl(Composite parent, int style) {
+ tableViewer = doCreateTableViewer(parent, style);
+ tableViewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ tableViewer.addFilter(new ViewerFilter() {
+ @Override
+ public boolean select(Viewer viewer, Object parentElement, Object element) {
+ if (matcher == null) {
+ return true;
+ }
+ ILabelProvider labelProvider = (ILabelProvider) tableViewer.getLabelProvider();
+ return matcher.match(labelProvider.getText(element));
+ }
+ });
+ return tableViewer.getTable();
+ }
+
+ /**
+ * 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 TableViewer(parent, style);
+ }
+
+ @Override
+ protected WorkbenchJob doCreateRefreshJob() {
+ return new WorkbenchJob("Refresh Filter") {//$NON-NLS-1$
+ @Override
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (getViewer().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) {
+ matcher = null;
+ } else if (text != null) {
+ matcher = new TextMatcher(text + '*', true, false);
+ }
+
+ tableViewer.refresh(true);
+
+ if (isQuickSelectionMode()) {
+ updateTableSelection(false);
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ }
+
+ @Override
+ protected boolean isShowFilterControls() {
+ return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_FILTERED_TEXTS);
+ }
+
+ @Override
+ public TableViewer getViewer() {
+ return tableViewer;
+ }
+}
diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java
index 24e2d884217..30c71b3a0f8 100644
--- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java
+++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/FilteredTree.java
@@ -19,9 +19,9 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.text.AbstractFilteredStructuredViewer;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.TreeViewer;
@@ -29,20 +29,14 @@
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.AccessibleAdapter;
import org.eclipse.swt.accessibility.AccessibleEvent;
-import org.eclipse.swt.events.FocusAdapter;
-import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
-import org.eclipse.swt.events.MouseAdapter;
-import org.eclipse.swt.events.MouseEvent;
-import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
-import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IWorkbenchPreferenceConstants;
@@ -57,20 +51,16 @@
* @see org.eclipse.ui.dialogs.PatternFilter
* @since 3.2
*/
-public class FilteredTree extends Composite {
-
- /**
- * The filter text widget to be used by this tree. This value may be
- * null
if there is no filter widget, or if the controls have not
- * yet been created.
- */
- protected Text filterText;
+public class FilteredTree extends AbstractFilteredStructuredViewer {
/**
* * Note: As of 4.13 not used anymore *
+ * + * @deprecated As of 4.13 not used anymore. */ + @Deprecated(forRemoval = true, since = "2025-03") protected ToolBarManager filterToolBar; /** @@ -79,7 +69,9 @@ public class FilteredTree extends Composite { * * * @since 3.5 + * @deprecated As of 4.13 not used anymore. */ + @Deprecated(forRemoval = true, since = "2025-03") protected Control clearButtonControl; /** @@ -88,70 +80,22 @@ public class FilteredTree extends Composite { */ protected TreeViewer treeViewer; - /** - * The Composite on which the filter controls are created. This is used to set - * the background color of the filter controls to match the surrounding - * controls. - */ - protected Composite filterComposite; - /** * The pattern filter for the tree. This value must not benull
.
*/
private PatternFilter patternFilter;
- /**
- * The text to initially show in the filter text control.
- */
- protected String initialText = ""; //$NON-NLS-1$
-
- /**
- * The job used to refresh the tree.
- */
- private Job refreshJob;
-
- /**
- * The parent composite of the filtered tree.
- *
- * @since 3.3
- */
- protected Composite parent;
-
- /**
- * Whether or not to show the filter controls (text and clear button). The
- * default is to show these controls. This can be overridden by providing a
- * setting in the product configuration file. The setting to add to not show
- * these controls is:
- *
- * org.eclipse.ui/SHOW_FILTERED_TEXTS=false
- */
- protected boolean showFilterControls;
-
/**
* @since 3.3
*/
protected Composite treeComposite;
- /**
- * Tells whether this filtered tree is used to make quick selections. In this
- * mode the first match in the tree is automatically selected while filtering
- * and the 'Enter' key is not used to move the focus to the tree.
- *
- * @since 3.105
- */
- private boolean quickSelectionMode = false;
-
/**
* Maximum time spent expanding the tree after the filter text has been updated
* (this is only used if we were able to at least expand the visible nodes)
*/
private static final long SOFT_MAX_EXPAND_TIME = 200;
- /**
- * Time for refresh job delay in terms of expansion in long value
- */
- private final long refreshJobDelayInMillis;
-
/**
* Default time for refresh job delay in ms
*/
@@ -179,9 +123,7 @@ public class FilteredTree extends Composite {
* @since 3.116
*/
public FilteredTree(Composite parent, boolean useNewLook, boolean useFastHashLookup) {
- super(parent, SWT.NONE);
- this.parent = parent;
- this.refreshJobDelayInMillis = DEFAULT_REFRESH_TIME;
+ super(parent, SWT.NONE, DEFAULT_REFRESH_TIME);
if (treeViewer != null) {
treeViewer.setUseHashlookup(useFastHashLookup);
}
@@ -225,9 +167,7 @@ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boole
*/
public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boolean useNewLook,
boolean useFastHashLookup, long refreshJobDelayInMillis) {
- super(parent, SWT.NONE);
- this.parent = parent;
- this.refreshJobDelayInMillis = refreshJobDelayInMillis;
+ super(parent, SWT.NONE, refreshJobDelayInMillis);
init(treeStyle, filter);
if (treeViewer != null) {
treeViewer.setUseHashlookup(useFastHashLookup);
@@ -257,9 +197,7 @@ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boole
*/
@Deprecated
protected FilteredTree(Composite parent) {
- super(parent, SWT.NONE);
- this.refreshJobDelayInMillis = DEFAULT_REFRESH_TIME;
- this.parent = parent;
+ super(parent, SWT.NONE, DEFAULT_REFRESH_TIME);
}
/**
@@ -347,13 +285,13 @@ public FilteredTree(Composite parent, int treeStyle, PatternFilter filter, boole
*/
protected void init(int treeStyle, PatternFilter filter) {
patternFilter = filter;
- showFilterControls = PlatformUI.getPreferenceStore()
- .getBoolean(IWorkbenchPreferenceConstants.SHOW_FILTERED_TEXTS);
- createControl(parent, treeStyle);
- createRefreshJob();
+ super.init(treeStyle);
setInitialText(WorkbenchMessages.FilteredTree_FilterMessage);
- setFont(parent.getFont());
+ }
+ @Override
+ protected boolean isShowFilterControls() {
+ return PlatformUI.getPreferenceStore().getBoolean(IWorkbenchPreferenceConstants.SHOW_FILTERED_TEXTS);
}
/**
@@ -362,27 +300,9 @@ protected void init(int treeStyle, PatternFilter filter) {
* @param parent the parent
* @param treeStyle SWT style bits used to create the tree
*/
+ @Override
protected void createControl(Composite parent, int treeStyle) {
- GridLayout layout = new GridLayout();
- layout.marginHeight = 0;
- layout.marginWidth = 0;
- setLayout(layout);
-
- if (parent.getLayout() instanceof GridLayout) {
- setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
- }
-
- if (showFilterControls) {
- filterComposite = new Composite(this, SWT.NONE);
- GridLayout filterLayout = new GridLayout();
- filterLayout.marginHeight = 0;
- filterLayout.marginWidth = 0;
- filterComposite.setLayout(filterLayout);
- filterComposite.setFont(parent.getFont());
-
- createFilterControls(filterComposite);
- filterComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
- }
+ super.createControl(parent, treeStyle);
treeComposite = new Composite(this, SWT.NONE);
GridLayout treeCompositeLayout = new GridLayout();
@@ -394,19 +314,6 @@ protected void createControl(Composite parent, int treeStyle) {
createTreeControl(treeComposite, treeStyle);
}
- /**
- * Create the filter controls. By default, a text and corresponding tool bar
- * button that clears the contents of the text is created. Subclasses may
- * override.
- *
- * @param parent parent Composite
of the filter controls
- * @return the Composite
that contains the filter controls
- */
- protected Composite createFilterControls(Composite parent) {
- createFilterText(parent);
- return parent;
- }
-
/**
* Creates and set up the tree and tree viewer. This method calls
* {@link #doCreateTreeViewer(Composite, int)} to create the tree viewer.
@@ -421,7 +328,6 @@ protected Control createTreeControl(Composite parent, int style) {
treeViewer = doCreateTreeViewer(parent, style);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
treeViewer.getControl().setLayoutData(data);
- treeViewer.getControl().addDisposeListener(e -> refreshJob.cancel());
if (treeViewer instanceof NotifyingTreeViewer) {
patternFilter.setUseCache(true);
}
@@ -461,14 +367,6 @@ private TreeItem getFirstMatchingItem(TreeItem[] items) {
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 tree based on the current
* filter text. Subclasses may override.
@@ -477,6 +375,7 @@ private void createRefreshJob() {
*
* @since 3.4
*/
+ @Override
protected WorkbenchJob doCreateRefreshJob() {
return new WorkbenchJob("Refresh Filter") {//$NON-NLS-1$
@Override
@@ -536,7 +435,7 @@ && recursiveExpand(items, monitor, stopTime, new int[] { numVisibleItems })) {
if (items.length > 0 && getViewer().getTree().getSelectionCount() == 0) {
treeViewer.getTree().setTopItem(items[0]);
}
- if (quickSelectionMode)
+ if (isQuickSelectionMode())
updateTreeSelection(false);
redrawFalseControl.setRedraw(true);
}
@@ -583,7 +482,9 @@ private boolean recursiveExpand(TreeItem[] items, IProgressMonitor monitor, long
* override.
*
* @param visible boolean
+ * @deprecated Not used since 4.13
*/
+ @Deprecated(forRemoval = true, since = "2025-03")
protected void updateToolbar(boolean visible) {
// nothing to do
}
@@ -596,8 +497,9 @@ protected void updateToolbar(boolean visible) {
*
* @param parent Composite
of the filter text
*/
+ @Override
protected void createFilterText(Composite parent) {
- filterText = doCreateFilterText(parent);
+ super.createFilterText(parent);
filterText.getAccessible().addAccessibleListener(new AccessibleAdapter() {
@Override
public void getName(AccessibleEvent e) {
@@ -641,44 +543,6 @@ private int itemCount(TreeItem treeItem) {
}
});
- filterText.addFocusListener(new FocusAdapter() {
- @Override
- public void focusGained(FocusEvent e) {
- /*
- * Running in an asyncExec because the selectAll() does not appear to work when
- * using mouse to give focus to text.
- */
- Display display = filterText.getDisplay();
- display.asyncExec(() -> {
- if (!filterText.isDisposed()) {
- if (getInitialText().equals(filterText.getText().trim())) {
- filterText.selectAll();
- }
- }
- });
- }
-
- @Override
- public void focusLost(FocusEvent e) {
- if (filterText.getText().equals(initialText)) {
- setFilterText(""); //$NON-NLS-1$
- textChanged();
- }
- }
- });
-
- filterText.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseDown(MouseEvent e) {
- if (filterText.getText().equals(initialText)) {
- // XXX: We cannot call clearText() due to
- // https://bugs.eclipse.org/bugs/show_bug.cgi?id=260664
- setFilterText(""); //$NON-NLS-1$
- textChanged();
- }
- }
- });
-
filterText.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
@@ -693,7 +557,7 @@ public void keyPressed(KeyEvent e) {
// enter key set focus to tree
filterText.addTraverseListener(e -> {
- if (quickSelectionMode) {
+ if (isQuickSelectionMode()) {
return;
}
if (e.detail == SWT.TRAVERSE_RETURN) {
@@ -701,11 +565,6 @@ public void keyPressed(KeyEvent e) {
updateTreeSelection(true);
}
});
-
- filterText.addModifyListener(e -> textChanged());
-
- GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
- filterText.setLayoutData(gridData);
}
/**
@@ -740,19 +599,6 @@ protected void updateTreeSelection(boolean setFocus) {
}
}
- /**
- * Creates the text control for entering the filter text. Subclasses may
- * override.
- *
- * @param parent the parent composite
- * @return the text widget
- *
- * @since 3.3
- */
- protected Text doCreateFilterText(Composite parent) {
- return new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
- }
-
private String previousFilterText;
private boolean narrowingDown;
@@ -760,56 +606,13 @@ protected Text doCreateFilterText(Composite parent) {
/**
* Update the receiver after the text has changed.
*/
+ @Override
protected void textChanged() {
narrowingDown = previousFilterText == null
|| previousFilterText.equals(WorkbenchMessages.FilteredTree_FilterMessage)
|| getFilterString().startsWith(previousFilterText);
previousFilterText = getFilterString();
- // cancel currently running job first, to prevent unnecessary redraw
- refreshJob.cancel();
- refreshJob.schedule(getRefreshJobDelay());
- }
-
- /**
- * Return the time delay that should be used when scheduling the filter refresh
- * job. Subclasses may override.
- *
- * @return a time delay in milliseconds before the job should run
- *
- * @since 3.5
- */
- protected long getRefreshJobDelay() {
- return refreshJobDelayInMillis;
- }
-
- /**
- * 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);
- }
-
- /**
- * Clears the text in the filter text widget.
- */
- protected void clearText() {
- setFilterText(""); //$NON-NLS-1$
- textChanged();
- }
-
- /**
- * Set the text in the filter control.
- *
- * @param filterText the text to set.
- */
- protected void setFilterText(String filterText) {
- if (this.filterText != null) {
- this.filterText.setText(filterText);
- selectAll();
- }
+ super.textChanged();
}
/**
@@ -826,92 +629,11 @@ public final PatternFilter getPatternFilter() {
*
* @return the tree viewer
*/
+ @Override
public TreeViewer getViewer() {
return treeViewer;
}
- /**
- * Get the filter text for the receiver, if it was created. Otherwise return
- * null
.
- *
- * @return the filter Text, or null if it was not created
- */
- public Text getFilterControl() {
- return filterText;
- }
-
- /**
- * Convenience method to return the text of the filter control. If the text
- * widget is not created, then null is returned.
- *
- * @return String in the text, or null if the text does not exist
- */
- protected String getFilterString() {
- return filterText != null ? filterText.getText() : null;
- }
-
- /**
- * Set the text that will be shown until the first focus. A default value is
- * provided, so this method only need be called if overriding the default
- * initial text is desired.
- *
- * @param text initial text to appear in text field
- */
- public void setInitialText(String text) {
- initialText = text;
- if (filterText != null) {
- filterText.setMessage(text);
- if (filterText.isFocusControl()) {
- setFilterText(initialText);
- textChanged();
- } else {
- getDisplay().asyncExec(() -> {
- if (!filterText.isDisposed() && filterText.isFocusControl()) {
- setFilterText(initialText);
- textChanged();
- }
- });
- }
- } else {
- setFilterText(initialText);
- textChanged();
- }
- }
-
- /**
- * Sets whether this filtered tree is used to make quick selections. In this
- * mode the first match in the tree is automatically selected while filtering
- * and the 'Enter' key is not used to move the focus to the tree.
- *
- * By default, this is set to false
.
- *
true
if this filtered tree is used to make quick
- * selections, false
otherwise
- * @since 3.105
- */
- public void setQuickSelectionMode(boolean enabled) {
- this.quickSelectionMode = enabled;
- }
-
- /**
- * Select all text in the filter text field.
- */
- protected void selectAll() {
- if (filterText != null) {
- filterText.selectAll();
- }
- }
-
- /**
- * Get the initial text for the receiver.
- *
- * @return String
- */
- protected String getInitialText() {
- return initialText;
- }
-
/**
* 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
diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java
index b27c7d89b43..be0c5f73493 100644
--- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java
+++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/PatternFilter.java
@@ -17,13 +17,13 @@
import java.util.HashMap;
import java.util.Map;
+import org.eclipse.jface.internal.text.TextMatcher;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
-import org.eclipse.ui.internal.misc.TextMatcher;
/**
* A filter used in conjunction with FilteredTree
. In order to
diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java
index 0b219cab6f1..c783d16dc5c 100644
--- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java
+++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/dialogs/SearchPattern.java
@@ -13,8 +13,8 @@
*******************************************************************************/
package org.eclipse.ui.dialogs;
+import org.eclipse.jface.internal.text.TextMatcher;
import org.eclipse.jface.util.Util;
-import org.eclipse.ui.internal.misc.TextMatcher;
/**
* A search pattern defines how search results are found.
diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java
index f39c445e9b1..a1f3c531071 100644
--- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java
+++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/about/AboutPluginsPage.java
@@ -40,6 +40,7 @@
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.internal.text.TextMatcher;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.util.ConfigureColumns;
import org.eclipse.jface.viewers.ArrayContentProvider;
@@ -73,7 +74,6 @@
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.misc.StatusUtil;
-import org.eclipse.ui.internal.misc.TextMatcher;
import org.eclipse.ui.internal.util.BundleUtility;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.statushandlers.StatusManager;
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 @@ + + +