/******************************************************************************* * Copyright (c) 2011, 2013 Wind River Systems, Inc. and others. All rights reserved. * This program and the accompanying materials are made available under the terms * of the Eclipse Public License v1.0 which accompanies this distribution, and is * available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.te.ui.search; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.TreePath; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.tcf.te.core.interfaces.IPropertyChangeProvider; import org.eclipse.tcf.te.ui.interfaces.ISchedulableEvent; /** * A quick filter is a viewer filter that selects elements, which has the specified name pattern, * under a certain tree path. Other elements outside of this tree path is ignored. */ public class QuickFilter extends TablePatternFilter implements PropertyChangeListener { // The tree viewer to filter. private TreeViewer viewer; // The root path to select from. private TreePath root; // If the current filtering is global private boolean global; /** * Create a quick filter for the specified viewer. */ public QuickFilter(TreeViewer viewer, TreePath root) { super((ILabelProvider) viewer.getLabelProvider()); this.viewer = viewer; this.root = root; this.addPropertyChangeListener(this); } /** * Show the pop up dialog for the specified root path. * * @param global If the filter is a global one. */ public void showFilterPopup(boolean global) { this.global = global; if (!isFiltering()) { viewer.addFilter(this); } QuickFilterPopup popup = new QuickFilterPopup(viewer, this); Point location = computePopupLocation(); popup.open(); popup.getShell().setLocation(location); } /** * Compute the best location of the pop up dialog. * * @return The best location of the pop up dialog. */ private Point computePopupLocation() { Point location = null; if (!global) { TreeItem[] items = viewer.getTree().getSelection(); if (items != null && items.length > 0) { for (TreeItem item : items) { viewer.getTree().showItem(item); } TreeItem item = items[0]; Rectangle bounds = item.getBounds(); location = new Point(bounds.x, bounds.y - bounds.height); } else { location = new Point(0, -viewer.getTree().getItemHeight()); } } else { location = new Point(0, -viewer.getTree().getItemHeight()); } location = viewer.getTree().toDisplay(location); return location; } /** * Adjust the position of the pop up when the tree viewer has changed. * * @param popshell The shell of the pop up dialog. */ void adjustPopup(Shell popshell) { if (!global) { Point location = computePopupLocation(); Point shellLocation = popshell.getLocation(); if (shellLocation != null && !shellLocation.equals(location)) { popshell.setLocation(location); } } } /** * Subclass PropertyChangeEvent and implement ISchedulable to provide an event which should be * scheduled when the key stroke pauses for a certain time. */ private static class QuickFilterEvent extends PropertyChangeEvent implements ISchedulableEvent { private static final long serialVersionUID = 1L; // Remember the last time of a property change event caused by a key stroke private static long last_enqueue; // Maximum delay before the event should be scheduled. private static final long MAXIMUM_DELAY = 300L; // The effective tree viewer; private TreeViewer viewer; /** * Constructor inherited. * * @param source * @param propertyName * @param oldValue * @param newValue */ public QuickFilterEvent(TreeViewer viewer, Object source, String propertyName, Object oldValue, Object newValue) { super(source, propertyName, oldValue, newValue); this.viewer = viewer; } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.interfaces.ISchedulable#eventQueued() */ @Override public synchronized void eventQueued() { last_enqueue = System.currentTimeMillis(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.interfaces.ISchedulable#isSchedulable() */ @Override public boolean isSchedulable() { return System.currentTimeMillis() - last_enqueue > MAXIMUM_DELAY; } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.interfaces.ISchedulableEvent#isApplicable(org.eclipse.jface.viewers.TreeViewer) */ @Override public boolean isApplicable(TreeViewer viewer) { return this.viewer == viewer; } } /* (non-Javadoc) * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) */ @Override public void propertyChange(PropertyChangeEvent evt) { Object element = root.getLastSegment(); element = element == null ? viewer.getInput() : element; if (element != null) { IPropertyChangeProvider provider = getPropertyChangeProvider(element); if (provider != null) { provider.firePropertyChange(new QuickFilterEvent(viewer, element, evt.getPropertyName(), evt.getOldValue(), evt.getNewValue())); } else { viewer.refresh(element, true); } } } /** * Get an adapter of IPropertyChangeProvider from the specified element. * * @param element The element to get the adapter from. * @return The element's adapter or null if does not adapt to IPropertyChangeProvider. */ private IPropertyChangeProvider getPropertyChangeProvider(Object element) { IPropertyChangeProvider provider = null; if (element instanceof IPropertyChangeProvider) { provider = (IPropertyChangeProvider) element; } if (provider == null && element instanceof IAdaptable) { provider = (IPropertyChangeProvider) ((IAdaptable) element).getAdapter(IPropertyChangeProvider.class); } if (provider == null && element != null) { provider = (IPropertyChangeProvider) Platform.getAdapterManager().getAdapter(element, IPropertyChangeProvider.class); } return provider; } /** * Reset the tree viewer to the original view by removing this filter. */ public void resetViewer() { viewer.removeFilter(this); @SuppressWarnings("unchecked") Map<TreePath, QuickFilter> filters = (Map<TreePath, QuickFilter>) viewer.getData("quick.filter"); //$NON-NLS-1$ if (filters != null) { filters.remove(root); } setPattern(null); } /** * If the current viewer is being filtered. * * @return true if it has this filter. */ private boolean isFiltering() { ViewerFilter[] filters = viewer.getFilters(); if (filters != null) { for (ViewerFilter filter : filters) { if (filter == this) { return matcher != null; } } } return false; } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.internal.utils.TablePatternFilter#select(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object) */ @Override public boolean select(Viewer viewer, Object parentElement, Object element) { Assert.isNotNull(parentElement); return !shouldSelect(parentElement) || super.select(viewer, parentElement, element); } /** * If the current parent element should be selected for matching. * * @param parentElement The parent element. * @return true if it should continue matching. */ private boolean shouldSelect(Object parentElement) { Object rootElement = parentElement instanceof TreePath ? root : (root.getSegmentCount() == 0 ? viewer .getInput() : root.getLastSegment()); return parentElement.equals(rootElement); } /** * If the element is being filtered. * * @param path The element to be checked. * @return true if it is filtering. */ public boolean isFiltering(TreePath path) { return isFiltering() && matcher != null && root.equals(path); } }