/*******************************************************************************
* Copyright (c) 2006-2013 The RCP Company 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:
* The RCP Company - initial API and implementation
*******************************************************************************/
package com.rcpcompany.uibindings.internal.utils;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
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.events.MouseMoveListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.SearchPattern;
import org.eclipse.ui.forms.widgets.FormToolkit;
import com.rcpcompany.uibindings.IColumnBinding;
import com.rcpcompany.uibindings.IManager;
import com.rcpcompany.uibindings.IViewerBinding;
import com.rcpcompany.uibindings.utils.IFilteringTableAdapter;
import com.rcpcompany.uibindings.utils.IManagerRunnable;
/**
* Column filter based on an {@link IViewerBinding}.
*
* TODO: SWTB
*
* @author Tonny Madsen, The RCP Company
*/
public class FilteringTableAdapter implements IFilteringTableAdapter, DisposeListener {
/**
* Initial text for filter Text widget.
*/
public static final String TYPE_FILTER_TEXT = "type filter text";
/**
* Image descriptor for enabled clear button.
*/
private static final String CLEAR_ICON = "org.eclipse.ui.internal.dialogs.CLEAR_ICON"; //$NON-NLS-1$
/**
* Image descriptor for disabled clear button.
*/
private static final String DCLEAR_ICON = "org.eclipse.ui.internal.dialogs.DCLEAR_ICON"; //$NON-NLS-1$
/**
* Constructs and initializes a new filter...
*
* @param viewer the binding
* @param filter the filter observable
* @param text the Text widget that holds the current filter
*/
public FilteringTableAdapter(IViewerBinding viewer, IObservableValue filter, Text text) {
myViewerBinding = viewer;
myFilter = filter;
myText = text;
Assert.isLegal(myViewerBinding.getControl() instanceof Table, "The filter only accepts tables");
myTableViewer = (TableViewer) myViewerBinding.getViewer();
myTable = myTableViewer.getTable();
init();
}
/**
* Initializes this filter.
*/
protected void init() {
myViewerBinding.registerService(this);
myFilter.addValueChangeListener(myFilterListener);
myFilterListener.handleValueChange(null);
myTable.addDisposeListener(this);
if (myText != null) {
// if we're using a field with built in cancel we need to listen for
// default selection changes (which tell us the cancel button has been
// pressed)
if ((myText.getStyle() & SWT.CANCEL) != 0) {
myText.addSelectionListener(myFilterClearListener);
}
myText.addFocusListener(myFilterFocusListener);
myText.addKeyListener(myFilterKeylistener);
myTable.addKeyListener(myTableKeylistener);
}
}
/**
* Removes the filtering functionality again.
*/
@Override
public void dispose() {
if (myText != null && !myText.isDisposed()) {
if ((myText.getStyle() & SWT.CANCEL) != 0) {
myText.removeSelectionListener(myFilterClearListener);
}
myText.removeFocusListener(myFilterFocusListener);
myText.removeKeyListener(myFilterKeylistener);
myTable.removeKeyListener(myTableKeylistener);
}
myFilter.removeValueChangeListener(myFilterListener);
myTableViewer.removeFilter(myViewerFilter);
myTable.removeDisposeListener(this);
myViewerBinding.unregisterService(this);
}
/**
* The binding.
*/
protected final IViewerBinding myViewerBinding;
/**
* The table viewer in play.
*/
protected final TableViewer myTableViewer;
/**
* The table itself.
*/
protected final Table myTable;
/**
* An observable value for the current filter.
*/
protected final IObservableValue myFilter;
/**
* The text field with the filter string - can be <code>null</code>.
*/
protected final Text myText;
/**
* A search pattern with the current value of the filter.
*/
protected SearchPattern mySearchPattern = new SearchPattern();
/**
* Viewer filter used for the table.
*/
protected ViewerFilter myViewerFilter = new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object element) {
for (final IColumnBinding cb : myViewerBinding.getColumns()) {
final String value = cb.getDisplayText(element);
if (mySearchPattern.matches(value)) return true;
}
return false;
}
};
/**
* Filter Focus listener: selects all text of the filter when it gets focus.
*/
protected FocusAdapter myFilterFocusListener = 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.
*/
IManagerRunnable.Factory.asyncExec(null, this, new Runnable() {
@Override
public void run() {
if (!myText.isDisposed()) {
if (TYPE_FILTER_TEXT.equals(myText.getText().trim())) {
myText.selectAll();
}
}
}
});
}
};
/**
* Filter Key listener: arrow down should go to the table itself...
*/
protected KeyAdapter myFilterKeylistener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if ((myTable.getItemCount() > 0) && (e.keyCode == SWT.ARROW_DOWN || e.character == SWT.CR)) {
myTable.setFocus();
if (myTable.getSelectionIndex() == -1) {
myTable.setSelection(0);
}
}
}
};
/**
* Table Key listener: arrow up in the first row should go to the filter.
*/
protected KeyAdapter myTableKeylistener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if ((myTable.getSelectionIndex() == 0) && (e.keyCode == SWT.ARROW_UP)) {
myText.setFocus();
}
}
};
/**
* Filter Clear listener: clears the filter when canceled.
*/
SelectionAdapter myFilterClearListener = new SelectionAdapter() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
if (e.detail == SWT.CANCEL) {
myText.setText("");
}
}
};
/**
* Listener used on the filter observable to track changes to this.
*/
protected IValueChangeListener myFilterListener = new IValueChangeListener() {
private boolean filterAdded = false;
@Override
public void handleValueChange(ValueChangeEvent event) {
String filterString = (String) myFilter.getValue();
if (filterString == null) {
filterString = "";
}
mySearchPattern.setPattern(filterString);
final boolean filterWanted = filterString.length() > 0;
if (filterWanted && !filterAdded) {
myTableViewer.addFilter(myViewerFilter);
} else if (!filterWanted && filterAdded) {
myTableViewer.removeFilter(myViewerFilter);
} else {
if (filterAdded) {
myTableViewer.refresh();
}
}
filterAdded = filterWanted;
}
};
/**
* @see IFilteringTableAdapter.Factory#createFilter(Composite)
*
* @param parent the parent composite
* @return the Text field
*/
public static Text createFilter(Composite parent) {
final FormToolkit toolkit = IManager.Factory.getManager().getFormToolkit(parent);
final Composite top = toolkit.createComposite(parent, SWT.BORDER);
top.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
final GridLayout filterLayout = new GridLayout(2, false);
filterLayout.marginHeight = 0;
filterLayout.marginWidth = 0;
top.setLayout(filterLayout);
// final Label label = toolkit.createLabel(top, "");
// label.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
// if (filterString == null) {
// filterString = "Filter:";
// }
// label.setText(filterString);
final Text filterText = new Text(top, SWT.SINGLE);
filterText.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
filterText.setMessage(TYPE_FILTER_TEXT);
// final Image inactiveImage =
// JFaceResources.getImageRegistry().get(ISharedImages.IMG_ETOOL_CLEAR_DISABLED);
// final Image activeImage =
// JFaceResources.getImageRegistry().get(ISharedImages.IMG_ETOOL_CLEAR);
final Image inactiveImage = PlatformUI.getWorkbench().getSharedImages()
.getImage(ISharedImages.IMG_ETOOL_CLEAR_DISABLED);
final Image activeImage = PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_ETOOL_CLEAR);
final Image pressedImage = new Image(parent.getDisplay(), activeImage, SWT.IMAGE_GRAY);
final Label clearButton = toolkit.createLabel(top, "");
clearButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
clearButton.setImage(inactiveImage);
clearButton.setBackground(parent.getBackground()); // getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND)
clearButton.setToolTipText("Clear filter");
clearButton.addMouseListener(new MouseAdapter() {
private MouseMoveListener fMoveListener;
@Override
public void mouseDown(MouseEvent e) {
clearButton.setImage(pressedImage);
fMoveListener = new MouseMoveListener() {
private boolean fMouseInButton = true;
@Override
public void mouseMove(MouseEvent e) {
final boolean mouseInButton = isMouseInButton(e);
if (mouseInButton != fMouseInButton) {
fMouseInButton = mouseInButton;
clearButton.setImage(mouseInButton ? pressedImage : inactiveImage);
}
}
};
clearButton.addMouseMoveListener(fMoveListener);
}
@Override
public void mouseUp(MouseEvent e) {
if (fMoveListener != null) {
clearButton.removeMouseMoveListener(fMoveListener);
fMoveListener = null;
final boolean mouseInButton = isMouseInButton(e);
clearButton.setImage(mouseInButton ? activeImage : inactiveImage);
if (mouseInButton) {
filterText.setText("");
filterText.setFocus();
}
}
}
private boolean isMouseInButton(MouseEvent e) {
final Point buttonSize = clearButton.getSize();
return 0 <= e.x && e.x < buttonSize.x && 0 <= e.y && e.y < buttonSize.y;
}
});
return filterText;
}
private static Boolean useNativeSearchField;
private static boolean useNativeSearchField(Composite composite) {
if (useNativeSearchField == null) {
useNativeSearchField = Boolean.FALSE;
Text testText = null;
try {
testText = new Text(composite, SWT.SEARCH | SWT.ICON_CANCEL);
useNativeSearchField = Boolean.valueOf((testText.getStyle() & SWT.ICON_CANCEL) != 0);
} finally {
if (testText != null) {
testText.dispose();
}
}
}
return useNativeSearchField.booleanValue();
}
@Override
public void widgetDisposed(DisposeEvent e) {
dispose();
}
@Override
public Text getText() {
return myText;
}
}