/*******************************************************************************
* Copyright (c) 2011 Fraunhofer IWU 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:
* Fraunhofer IWU - initial API and implementation
*******************************************************************************/
package net.enilink.komma.edit.ui.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.fieldassist.ContentProposal;
import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalListener;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
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.ui.dialogs.ElementListSelectionDialog;
import net.enilink.komma.edit.provider.ISearchableItemProvider;
import net.enilink.komma.edit.ui.KommaEditUIPlugin;
import net.enilink.komma.edit.ui.internal.IEditUIImages;
import net.enilink.komma.edit.ui.provider.AdapterFactoryContentProvider;
import net.enilink.komma.edit.ui.provider.ExtendedImageRegistry;
/**
* A simple control that provides a text widget for searching in viewers.
*/
public class SearchWidget {
protected ContentViewer viewer;
/**
* The search text widget to be used by this tree. This value may be
* <code>null</code> if there is no search widget, or if the controls have
* not yet been created.
*/
protected Text searchText;
/**
* The control representing the search button for the search text entry.
* This value may be <code>null</code> if no such button exists, or if the
* controls have not yet been created.
*/
protected ToolBarManager searchToolBar;
/**
* The Composite on which the search controls are created. This is used to
* set the background color of the search controls to match the surrounding
* controls.
*/
protected Composite searchComposite;
/**
* The text to initially show in the search text control.
*/
protected String initialText = ""; //$NON-NLS-1$
/**
* Create the controls. Subclasses should override.
*
* @param parent
* @param treeStyle
*/
public Control createControl(Composite parent) {
searchComposite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout(2, false);
layout.marginHeight = 0;
layout.marginWidth = 0;
searchComposite.setLayout(layout);
searchComposite.setFont(parent.getFont());
createSearchControls(searchComposite);
setInitialText("Search...");
return searchComposite;
}
public Control getControl() {
return searchComposite;
}
/**
* Create the search controls. By default, a text and corresponding tool bar
* button that executes the search is created. Subclasses may override.
*
* @param parent
* parent <code>Composite</code> of the search controls
* @return the <code>Composite</code> that contains the search controls
*/
protected Composite createSearchControls(Composite parent) {
createSearchText(parent);
createSearchButton(parent);
if (searchToolBar != null) {
searchToolBar.update(false);
}
return parent;
}
/**
* Creates the search text and adds listeners. This method calls
* {@link #doCreateSearchText(Composite)} to create the text control.
* Subclasses should override {@link #doCreateSearchText(Composite)} instead
* of overriding this method.
*
* @param parent
* <code>Composite</code> of the search text
*/
protected void createSearchText(Composite parent) {
searchText = doCreateSearchText(parent);
searchText.addFocusListener(new FocusAdapter() {
/*
* (non-Javadoc)
*
* @see
* org.eclipse.swt.events.FocusListener#focusLost(org.eclipse.swt
* .events.FocusEvent)
*/
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 = searchText.getDisplay();
display.asyncExec(new Runnable() {
public void run() {
if (!searchText.isDisposed()) {
if (getInitialText().equals(
searchText.getText().trim())) {
searchText.selectAll();
}
}
}
});
}
});
searchText.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetDefaultSelected(SelectionEvent e) {
doSearch(searchText.getText());
}
});
searchText.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true,
false));
// content proposals for searching
class ObjectProposal extends ContentProposal {
Object object;
ObjectProposal(Object object) {
super("");
this.object = object;
}
}
ContentProposalAdapter proposalAdapter = new ContentProposalAdapter(
searchText, new TextContentAdapter(),
new IContentProposalProvider() {
@Override
public IContentProposal[] getProposals(String contents,
int position) {
Collection<Object> results = findElements(contents);
List<IContentProposal> proposals = new ArrayList<IContentProposal>();
for (Object result : results) {
proposals.add(new ObjectProposal(result));
}
return proposals.toArray(new IContentProposal[proposals
.size()]);
}
}, null, null);
proposalAdapter.setAutoActivationDelay(750);
proposalAdapter
.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_IGNORE);
proposalAdapter
.addContentProposalListener(new IContentProposalListener() {
@Override
public void proposalAccepted(IContentProposal proposal) {
viewer.setSelection(new StructuredSelection(
((ObjectProposal) proposal).object), true);
}
});
proposalAdapter.setLabelProvider(new LabelProvider() {
@Override
public String getText(Object element) {
ILabelProvider labelProvider = getLabelProvider();
return labelProvider != null ? labelProvider
.getText(((ObjectProposal) element).object) : super
.getText(element);
}
@Override
public Image getImage(Object element) {
ILabelProvider labelProvider = getLabelProvider();
return labelProvider != null ? labelProvider
.getImage(((ObjectProposal) element).object) : super
.getImage(element);
}
});
}
/**
* Creates the text control for entering the search text. Subclasses may
* override.
*
* @param parent
* the parent composite
* @return the text widget
*/
protected Text doCreateSearchText(Composite parent) {
return new Text(parent, SWT.SINGLE | SWT.BORDER | SWT.SEARCH
| SWT.CANCEL);
}
/**
* Set the background for the widgets that support the search text area.
*
* @param background
* background <code>Color</code> to set
*/
public void setBackground(Color background) {
if (searchComposite != null) {
searchComposite.setBackground(background);
}
if (searchToolBar != null && searchToolBar.getControl() != null) {
searchToolBar.getControl().setBackground(background);
}
}
/**
* Create the button that clears the text.
*
* @param parent
* parent <code>Composite</code> of toolbar button
*/
private void createSearchButton(Composite parent) {
searchToolBar = new ToolBarManager(SWT.FLAT | SWT.HORIZONTAL);
searchToolBar.createControl(parent);
IAction searchAction = new Action("", IAction.AS_PUSH_BUTTON) {//$NON-NLS-1$
public void run() {
doSearch(searchText.getText());
}
};
searchAction.setImageDescriptor(ExtendedImageRegistry.getInstance()
.getImageDescriptor(
KommaEditUIPlugin.INSTANCE
.getImage(IEditUIImages.SEARCH)));
searchAction.setToolTipText("Search");
// clearTextAction.setImageDescriptor(JFaceResources
// .getImageRegistry().getDescriptor(
// ISharedImages.IMG_ETOOL_CLEAR));
// clearTextAction.setDisabledImageDescriptor(JFaceResources
// .getImageRegistry().getDescriptor(
// ISharedImages.));
searchToolBar.add(searchAction);
}
/**
* Clears the text in the search text widget.
*/
protected void clearText() {
setSearchText(""); //$NON-NLS-1$
}
/**
* Set the text in the search control.
*
* @param string
*/
protected void setSearchText(String string) {
if (searchText != null) {
searchText.setText(string);
selectAll();
}
}
/**
* Get the search text for the receiver, if it was created. Otherwise return
* <code>null</code>.
*
* @return the search Text, or null if it was not created
*/
public Text getSearchControl() {
return searchText;
}
/**
* Convenience method to return the text of the search 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 getSearchString() {
return searchText != null ? searchText.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;
setSearchText(initialText);
}
/**
* Select all text in the search text field.
*
*/
protected void selectAll() {
if (searchText != null) {
searchText.selectAll();
}
}
/**
* Get the initial text for the receiver.
*
* @return String
*/
protected String getInitialText() {
return initialText;
}
protected Collection<Object> findElements(String pattern) {
IContentProvider provider = viewer.getContentProvider();
Object input = viewer.getInput();
Collection<Object> results = new LinkedHashSet<Object>();
if (provider instanceof ISearchableItemProvider) {
results.addAll(((ISearchableItemProvider) provider).find(pattern,
input, 20).toList());
} else if (provider instanceof AdapterFactoryContentProvider) {
Object[] elements;
if (input instanceof Object[]) {
elements = (Object[]) input;
} else {
elements = new Object[] { input };
}
for (Object element : elements) {
ISearchableItemProvider searchableProvider = (ISearchableItemProvider) ((AdapterFactoryContentProvider) provider)
.getAdapterFactory().adapt(element,
ISearchableItemProvider.class);
if (searchableProvider != null) {
results.addAll(searchableProvider
.find(pattern, element, 20).toList());
}
}
}
return results;
}
protected ILabelProvider getLabelProvider() {
return viewer == null ? null : (ILabelProvider) viewer
.getLabelProvider();
}
protected void doSearch(String pattern) {
Collection<Object> results = findElements(pattern);
if (!results.isEmpty()) {
Object selected = null;
if (results.size() == 1) {
selected = results.iterator().next();
} else if (results.size() > 1) {
ElementListSelectionDialog selectionDialog = new ElementListSelectionDialog(
getControl().getShell(), getLabelProvider());
selectionDialog.setHelpAvailable(false);
selectionDialog.setElements(results.toArray(new Object[results
.size()]));
if (selectionDialog.open() == Window.OK) {
selected = selectionDialog.getFirstResult();
}
}
if (selected != null) {
viewer.setSelection(new StructuredSelection(selected), true);
}
}
}
public void setViewer(ContentViewer viewer) {
this.viewer = viewer;
}
}