package com.dubture.composer.ui.parts.composer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
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.forms.widgets.FormToolkit;
import com.dubture.composer.core.log.Logger;
import com.dubture.composer.ui.controller.IPackageCheckStateChangedListener;
import com.dubture.composer.ui.controller.PackageController;
import com.dubture.composer.ui.editor.FormLayoutFactory;
import com.dubture.composer.ui.utils.WidgetFactory;
import com.dubture.getcomposer.core.ComposerPackage;
import com.dubture.getcomposer.core.MinimalPackage;
import com.dubture.getcomposer.packages.AsyncPackageSearch;
import com.dubture.getcomposer.packages.AsyncPackagistSearch;
import com.dubture.getcomposer.packages.PackageSearchListenerInterface;
import com.dubture.getcomposer.packages.SearchResult;
public class PackageSearch implements PackageSearchListenerInterface, IPackageCheckStateChangedListener {
protected final static long QUERY_DELAY_MS = 300;
protected final static long RESET_QUERY_DELAY_MS = 1500;
protected FormToolkit toolkit;
protected WidgetFactory factory;
protected Text searchField;
protected CheckboxTableViewer searchResults;
protected PackageController searchController;
protected Composite body;
protected Composite pickedResults;
protected Map<String, PackageSearchPart> packageControls = new HashMap<String, PackageSearchPart>();
protected Button addButton;
protected AsyncPackageSearch downloader = new AsyncPackagistSearch();
protected String currentQuery;
protected String lastQuery;
protected String shownQuery;
protected String foundQuery;
protected Thread resetThread;
protected Thread queryThread;
protected boolean clearing = false;
protected SelectionAdapter addButtonListener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
notifyPackageSelectionFinishedListener();
}
};
protected boolean enabled = true;
protected ComposerPackage composerPackage;
protected Set<PackageSelectionFinishedListener> packageListeners = new HashSet<PackageSelectionFinishedListener>();
public PackageSearch (Composite parent, ComposerPackage composerPackage, FormToolkit toolkit, String buttonText) {
this.composerPackage = composerPackage;
create(parent, toolkit, buttonText);
}
public PackageSearch (Composite parent, ComposerPackage composerPackage, FormToolkit toolkit) {
this(parent, composerPackage, toolkit, null);
}
public PackageSearch (Composite parent, ComposerPackage composerPackage, String buttonText) {
this(parent, composerPackage, null, buttonText);
}
public PackageSearch (Composite parent, ComposerPackage composerPackage) {
this(parent, composerPackage, null, null);
}
public void addPackageCheckStateChangedListener(IPackageCheckStateChangedListener listener) {
if (searchController != null) {
searchController.addPackageCheckStateChangedListener(listener);
}
}
public void removePackageCheckStateChangedListener(IPackageCheckStateChangedListener listener) {
if (searchController != null) {
searchController.removePackageCheckStateChangedListener(listener);
}
}
public void addPackageSelectionFinishedListener(PackageSelectionFinishedListener listener) {
packageListeners.add(listener);
}
public void removePackageSelectionFinishedListener(PackageSelectionFinishedListener listener) {
packageListeners.remove(listener);
}
protected void create(Composite parent, FormToolkit toolkit, String buttonText) {
this.toolkit = toolkit;
factory = new WidgetFactory(toolkit);
body = factory.createComposite(parent);
body.setLayout(new GridLayout());
searchField = factory.createText(body, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
searchField.setEnabled(enabled);
searchField.setEditable(enabled);
searchField.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
searchField.addModifyListener(new ModifyListener() {
public void modifyText(ModifyEvent e) {
searchTextChanged();
}
});
// clicking the cancel icon sets content to null and invokes modifyText()
// ... no need to to it manually
//
// searchField.addSelectionListener(new SelectionAdapter() {
// public void widgetDefaultSelected(SelectionEvent e) {
// if (e.detail == SWT.ICON_CANCEL) {
// clearSearchText();
// }
// }
// });
// create search results viewer
int style = SWT.H_SCROLL | SWT.V_SCROLL;
if (toolkit == null)
style |= SWT.BORDER;
else
style |= toolkit.getBorderStyle();
searchController = getSearchResultsController();
searchController.addPackageCheckStateChangedListener(this);
searchResults = CheckboxTableViewer.newCheckList(body, style);
searchResults.getTable().setEnabled(enabled);
searchResults.getTable().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
searchResults.setCheckStateProvider(searchController);
searchResults.addCheckStateListener(searchController);
searchResults.setContentProvider(searchController);
searchResults.setLabelProvider(searchController);
searchResults.setInput(new ArrayList<ComposerPackage>());
pickedResults = factory.createComposite(body);
pickedResults.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
GridLayout layout = new GridLayout();
layout.marginTop = 0;
layout.marginRight = -5;
layout.marginBottom = 0;
layout.marginLeft = -5;
layout.verticalSpacing = FormLayoutFactory.SECTION_CLIENT_MARGIN_TOP;
layout.horizontalSpacing = 0;
pickedResults.setLayout(layout);
if (buttonText != null) {
addButton = factory.createButton(body);
addButton.setText(buttonText);
addButton.setEnabled(false);
addButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
addButton.addSelectionListener(addButtonListener);
}
// create downloader
downloader.addPackageSearchListener(this);
}
protected void notifyPackageSelectionFinishedListener() {
List<String> packages = getPackages();
for (PackageSelectionFinishedListener listener : packageListeners) {
listener.packagesSelected(packages);
}
clear();
}
protected void clearSearchText() {
if (clearing) {
return;
}
clearing = true;
searchResults.setInput(null);
searchField.setText("");
downloader.abort();
shownQuery = null;
queryThread.interrupt();
resetThread.interrupt();
clearing = false;
}
@Override
public void aborted(String url) {
}
@Override
public void packagesFound(final List<MinimalPackage> packages, String query, SearchResult result) {
foundQuery = query;
Logger.debug("Found Packages for: " + query + " => " + packages.size());
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (currentQuery.isEmpty()) {
return;
}
if (shownQuery == null ||
(!shownQuery.equals(foundQuery) && currentQuery.equals(foundQuery))) {
searchResults.setInput(packages);
shownQuery = foundQuery;
}
else if (shownQuery.equals(foundQuery)) {
searchController.addPackages(packages);
searchResults.refresh();
}
}
});
}
@Override
public void errorOccured(Exception e) {
// something happend during package search ...
e.printStackTrace();
}
protected void searchTextChanged() {
currentQuery = searchField.getText();
if (currentQuery.trim().isEmpty()) {
clearSearchText();
return;
}
// kill previous downloader
downloader.abort();
// run a new one
if (queryThread == null || !queryThread.isAlive() || queryThread.isInterrupted()) {
startQuery();
queryThread = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(QUERY_DELAY_MS);
synchronized(PackageSearch.this) {
startQuery();
queryThread.interrupt();
}
} catch (InterruptedException e) {
}
}
});
queryThread.start();
}
}
protected synchronized void startQuery() {
if (lastQuery == currentQuery) {
return;
}
downloader.search(currentQuery);
if (resetThread != null) {
resetThread.interrupt();
}
resetThread = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(RESET_QUERY_DELAY_MS);
synchronized(PackageSearch.this) {
if (currentQuery.equals(shownQuery)) {
shownQuery = null;
}
}
} catch (InterruptedException e) {
}
}
});
resetThread.start();
lastQuery = currentQuery;
}
protected PackageController getSearchResultsController() {
return new PackageController();
}
public Composite getBody() {
return body;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
searchField.setEnabled(enabled);
searchField.setEditable(enabled);
searchResults.getTable().setEnabled(enabled);
if (addButton != null) {
addButton.setEnabled(addButton.getEnabled() && enabled);
}
}
public boolean isEnabled() {
return enabled;
}
@Override
public void packageCheckStateChanged(String name, boolean checked) {
if (checked) {
packageControls.put(name, connectPackagePart(createPackagePart(pickedResults, name)));
} else {
packageControls.remove(name).dispose();
}
if (addButton != null) {
addButton.setEnabled(searchController.getCheckedPackagesCount() > 0 && enabled);
}
getBody().layout(true, true);
}
private PackageSearchPart connectPackagePart(final PackageSearchPart psp) {
psp.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
searchController.setChecked(psp.getName(), psp.isChecked());
searchResults.refresh();
}
});
return psp;
}
protected PackageSearchPart createPackagePart(Composite parent, final String name) {
return new PackageSearchPart(parent, composerPackage, toolkit, name);
}
public List<String> getPackages() {
return searchController.getCheckedPackages();
}
public void clear() {
clearSearchText();
searchController.clear();
packageControls.clear();
for (Control child : pickedResults.getChildren()) {
child.dispose();
}
if (addButton != null) {
addButton.setEnabled(false);
}
searchResults.refresh();
getBody().layout(true, true);
}
}