/**
*
*/
package org.objectstyle.wolips.wodclipse.action;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.objectstyle.wolips.wodclipse.core.completion.WodCompletionProposal;
import org.objectstyle.wolips.wodclipse.core.completion.WodCompletionUtils;
public class ComponentLiveSearch implements ModifyListener, SelectionListener {
private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows");
private IJavaProject _project;
private IProgressMonitor _progressMonitor;
private String _lastSearch;
private Point _lastSelection;
private String _lastValue;
private Boolean _ignoreModify = false;
private Object _completionLock = new Object();
public ComponentLiveSearch(IJavaProject project, IProgressMonitor progressMonitor) {
_project = project;
_progressMonitor = progressMonitor;
}
public void attachTo(Combo combo) {
if (combo != null) {
combo.addModifyListener(this);
combo.addSelectionListener(this);
}
}
public void detachFrom(Combo combo) {
if (combo != null) {
combo.removeModifyListener(this);
combo.removeSelectionListener(this);
}
}
public void widgetDefaultSelected(SelectionEvent e) {
widgetSelected(e);
}
public void widgetSelected(SelectionEvent e) {
Combo componentNameCombo = (Combo) e.getSource();
Point selection = componentNameCombo.getSelection();
if (_lastSelection != null && selection.x == 0) {
selection.x = _lastSelection.y;
synchronized (_ignoreModify) {
_ignoreModify = true;
componentNameCombo.setSelection(selection);
_ignoreModify = false;
}
componentNameCombo.notifyListeners(SWT.Modify, new Event());
}
}
public void modifyText(ModifyEvent e) {
if (_ignoreModify)
return;
final Combo componentNameCombo = (Combo) e.getSource();
Point selection = componentNameCombo.getSelection();
final String partialName;
boolean _deleted = false;
/* Handle deletion of type ahead selection */
if (selection.x > 0 && _lastSelection != null
&& selection.x == selection.y
&& selection.x == _lastSelection.x
&& _lastSelection.y >= selection.x
&& _lastSelection.y >= componentNameCombo.getText().length()) {
_deleted = true;
String text = componentNameCombo.getText();
synchronized (_ignoreModify) {
_ignoreModify = true;
componentNameCombo.setText(text.substring(0, selection.x-1));
selection.x = text.length()-1;
selection.y = selection.x;
componentNameCombo.setSelection(selection);
_ignoreModify = false;
}
}
if (isWindows
&& selection.x == 0
&& selection.x != selection.y
&& selection.y == componentNameCombo.getText().length())
return;
if (selection != null && selection.x != selection.y) {
partialName = componentNameCombo.getText().substring(0, selection.x);
} else {
partialName = componentNameCombo.getText();
_lastSelection = selection;
}
if (_lastValue != null && !_deleted) {
_deleted = partialName.length() < _lastValue.length() && selection.x != 0 ;
}
_lastValue = partialName;
if (_lastSearch == null || !_lastSearch.toLowerCase().equals(partialName.toLowerCase())) {
final boolean wasDeleted = _deleted;
_lastSearch = partialName;
_progressMonitor.setCanceled(true);
WorkspaceJob job = new WorkspaceJob("Searching for components ...") {
public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
synchronized (_completionLock) {
_progressMonitor.setCanceled(false);
Comparator<WodCompletionProposal> nameOrder = new Comparator<WodCompletionProposal>() {
public int compare(WodCompletionProposal p1, WodCompletionProposal p2) {
return p1.getDisplayString().compareTo(p2.getDisplayString());
}
};
try {
final Set<WodCompletionProposal> proposals = new TreeSet<WodCompletionProposal>(nameOrder);
WodCompletionUtils.fillInElementTypeCompletionProposals(_project, partialName, 0, partialName.length(), proposals, false, _progressMonitor);
if (!_progressMonitor.isCanceled()) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (!componentNameCombo.isDisposed()) {
componentNameCombo.remove(0, componentNameCombo.getItemCount() - 1);
boolean exactMatch = false;
for (WodCompletionProposal elementName : proposals) {
String displayString = elementName.getDisplayString();
if (displayString != null && displayString.equals(_lastSearch)) {
exactMatch = true;
}
componentNameCombo.add(displayString);
}
try {
Method setListVisible = componentNameCombo.getClass().getDeclaredMethod("setListVisible", boolean.class);
setListVisible.setAccessible(true);
if (!isWindows) {
setListVisible.invoke(componentNameCombo, false);
}
Point _selection = componentNameCombo.getSelection();
if (!(componentNameCombo.getItemCount() <= 1)) {
String text = componentNameCombo.getItem(0);
_selection.y = text.length();
synchronized (_ignoreModify) {
String partialText = componentNameCombo.getText();
if (!text.toLowerCase().startsWith(partialText.toLowerCase())) {
return;
}
_ignoreModify = true;
setListVisible.invoke(componentNameCombo, true);
componentNameCombo.setText(text);
componentNameCombo.setSelection(_selection);
_ignoreModify = false;
}
} else {
if (componentNameCombo.getItemCount() == 1) {
setListVisible.invoke(componentNameCombo, false);
String text = componentNameCombo.getItem(0);
if (_selection.x == _selection.y && !wasDeleted) {
_selection.y = text.length();
synchronized (_ignoreModify) {
_ignoreModify = true;
componentNameCombo.setText(text);
componentNameCombo.setSelection(_selection);
_ignoreModify = false;
}
_lastSelection = _selection;
}
}
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
}
});
}
} catch (OperationCanceledException t) {
// ignore
} catch (Throwable t) {
t.printStackTrace();
}
return Status.OK_STATUS;
}
}
};
job.schedule();
}
}
}