/*******************************************************************************
* Copyright (c) 2013 itemis AG (http://www.itemis.eu).
* 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
******************************************************************************/
package de.itemis.tooling.terminology.ui.search;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.emf.common.util.URI;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.internal.text.TableOwnerDrawSupport;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
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.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ListDialog;
import org.eclipse.ui.forms.events.ExpansionAdapter;
import org.eclipse.ui.forms.events.ExpansionEvent;
import org.eclipse.ui.forms.widgets.ExpandableComposite;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.ui.editor.IURIEditorOpener;
import org.eclipse.xtext.ui.label.AbstractLabelProvider;
import org.eclipse.xtext.ui.search.EObjectDescriptionContentProvider;
import de.itemis.tooling.terminology.terminology.TermStatus;
import de.itemis.tooling.terminology.terminology.TerminologyPackage;
import de.itemis.tooling.terminology.ui.internal.TerminologyActivator;
import de.itemis.tooling.terminology.ui.search.TerminologyEObjectSearch.TerminologySearchPattern;
/**
* adapted from {@Link org.eclipse.xtext.ui.search.XtextEObjectSearchDialog}
*/
@SuppressWarnings("restriction")
public class TerminologyEObjectSearchDialog extends ListDialog {
private static final String SETTINGS_SECTION = "Terminology_Search";
protected Text searchControl;
private String initialPatternText;
private Label messageLabel;
private Label searchStatusLabel;
private TerminologyIteratorJob sizeCalculationJob;
private Label matchingElementsLabel;
private TerminologyEObjectSearch searchEngine;
private final ILabelProvider labelProvider;
private boolean enableStyledLabels;
private IURIEditorOpener uriOpener;
private Button definitionControl;
private Button usageControl;
private Button useCustomerProductsControl;
private Map<TermStatus, Button> statusControls;
private EObjectDescriptionCheckboxTableViewer productViewer;
private EObjectDescriptionCheckboxTableViewer customerViewer;
private Iterable<IEObjectDescription> possibleProducs;
private Iterable<IEObjectDescription> possibleCustomers;
public TerminologyEObjectSearchDialog(Shell parent, TerminologyEObjectSearch searchEngine, ILabelProvider labelProvider, IURIEditorOpener uriOpener, IResourceDescriptions index) {
super(parent);
this.searchEngine = searchEngine;
this.labelProvider = new AbstractLabelProvider(labelProvider) {
@Override
protected Object doGetText(Object element) {
if(element instanceof IEObjectDescription){
return ((IEObjectDescription)element).getQualifiedName().getLastSegment();
}
return super.doGetText(element);
}
};
this.uriOpener = uriOpener;
this.possibleProducs=index.getExportedObjectsByType(TerminologyPackage.Literals.PRODUCT);
this.possibleCustomers=index.getExportedObjectsByType(TerminologyPackage.Literals.CUSTOMER);
setTitle("Terminology Search");
setMessage("Search for terms");
setAddCancelButton(true);
// super class needs an IStructuredContentProvider so we register this dummy and
// register the lazy one later
setContentProvider(new IStructuredContentProvider() {
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
public void dispose() {
}
public Object[] getElements(Object inputElement) {
return null;
}
});
setLabelProvider(this.labelProvider);
setHelpAvailable(false);
setDialogBoundsSettings(getDialogSettings(), DIALOG_PERSISTLOCATION|DIALOG_PERSISTSIZE);
}
public TerminologyEObjectSearchDialog(Shell parent, TerminologyEObjectSearch searchEngine, ILabelProvider labelProvider, boolean enableStyledLabels, IURIEditorOpener uriOpener, IResourceDescriptions index) {
this(parent, searchEngine, labelProvider,uriOpener, index);
setShellStyle(SWT.DIALOG_TRIM | SWT.MAX | SWT.RESIZE
| getDefaultOrientation());
this.enableStyledLabels = enableStyledLabels;
}
/**
* @since 2.0
*/
protected TerminologyEObjectSearch getSearchEngine() {
return searchEngine;
}
/**
* @since 2.0
*/
public void setInitialPattern(String text) {
this.initialPatternText = text;
}
/**
* @since 2.0
*/
protected String getInitialPattern() {
return initialPatternText;
}
@Override
public int open() {
if (getInitialPattern() == null) {
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
ISelection selection = window.getSelectionService().getSelection();
if (selection instanceof ITextSelection) {
String text = ((ITextSelection) selection).getText();
if (text != null) {
text = text.trim();
if (text.length() > 0) {
setInitialPattern(text);
}
}
}
}
}
return super.open();
}
@Override
protected int getTableStyle() {
return super.getTableStyle() | SWT.VIRTUAL;
}
@Override
protected Control createDialogArea(Composite container) {
Composite parent = (Composite) super.createDialogArea(container);
if (enableStyledLabels && labelProvider instanceof IStyledLabelProvider) {
final Table table = getTableViewer().getTable();
final IStyledLabelProvider styledLabelProvider = (IStyledLabelProvider) labelProvider;
TableOwnerDrawSupport.install(table);
Listener listener= new Listener() {
public void handleEvent(Event event) {
handleSetData(event);
}
protected void handleSetData(Event event) {
TableItem item= (TableItem) event.item;
IEObjectDescription description = (IEObjectDescription) item.getData();
if (description != null) {
StyledString styledString = styledLabelProvider.getStyledText(description);
String displayString = styledString.toString();
StyleRange[] styleRanges = styledString.getStyleRanges();
item.setText(displayString);
TableOwnerDrawSupport.storeStyleRanges(item, 0, styleRanges);
}
}
};
table.addListener(SWT.SetData, listener);
}
messageLabel = new Label(parent, SWT.NONE);
setDefaultGridData(messageLabel);
EObjectDescriptionContentProvider contentProvider = new EObjectDescriptionContentProvider();
getTableViewer().setContentProvider(contentProvider);
getTableViewer().addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection = (IStructuredSelection) selection;
if (!structuredSelection.isEmpty()) {
Object firstElement = structuredSelection.getFirstElement();
if (firstElement instanceof IEObjectDescription) {
IEObjectDescription eObjectDescription = (IEObjectDescription) firstElement;
URI resourceURI = eObjectDescription.getEObjectURI().trimFragment();
if (resourceURI.isPlatform()) {
messageLabel.setText(resourceURI.toPlatformString(true));
} else if (resourceURI.isFile()) {
messageLabel.setText(resourceURI.toFileString());
} else {
messageLabel.setText(resourceURI.toString());
}
return;
}
}
}
messageLabel.setText(""); //$NON-NLS-1$
}
});
getTableViewer().addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
try {
ISelection selection =event.getSelection();
if (selection instanceof StructuredSelection) {
Object o=((StructuredSelection) selection).getFirstElement();
if(o instanceof IEObjectDescription){
IEObjectDescription selectedObjectDescription = (IEObjectDescription) o;
uriOpener.open(selectedObjectDescription.getEObjectURI(), true);
}
}
} catch (Exception e) {
// LOG.error("Error opening editor", e);
// throw new ExecutionException("Error opening editor", e);
}
}
});
applyFilter();
return parent;
}
@Override
protected void okPressed() {
try {
//disable tableViewer's doubleClickListener, so that the dialog stays open
StackTraceElement traceElement = Thread.currentThread().getStackTrace()[2];
if(traceElement.getMethodName().contains("Click")){
return;
}
} catch (Exception e) {
}
storeSettings();
super.okPressed();
}
private void storeSettings() {
IDialogSettings settings = getDialogSettings();
settings.put("definition", definitionControl.getSelection());
settings.put("usage", usageControl.getSelection());
settings.put("enableCPsettings", useCustomerProductsControl.getSelection());
for (TermStatus status : TermStatus.values()) {
settings.put(status.toString(), statusControls.get(status).getSelection());
}
settings.put("products", productViewer.getCheckedNames());
settings.put("customers", customerViewer.getCheckedNames());
}
@Override
protected Label createMessageArea(Composite parent2) {
Label label = super.createMessageArea(parent2);
searchControl = new Text(parent2, SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
setDefaultGridData(searchControl);
SelectionAdapter selectionListener = new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
applyFilter();
}
};
initSearchScopeSettings(parent2, selectionListener);
initCustomersProductsSettings(parent2, selectionListener);
Composite labelComposite = new Composite(parent2, SWT.NONE);
setDefaultGridData(labelComposite);
GridLayout labelCompositeLayout = new GridLayout(2, true);
labelCompositeLayout.marginWidth = 0;
labelComposite.setLayout(labelCompositeLayout);
matchingElementsLabel = new Label(labelComposite, SWT.NONE);
matchingElementsLabel.setText("Searching...");
matchingElementsLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1));
searchStatusLabel = new Label(labelComposite, SWT.RIGHT);
searchStatusLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
ModifyListener textModifyListener = new ModifyListener() {
public void modifyText(ModifyEvent e) {
applyFilter();
}
};
searchControl.addModifyListener(textModifyListener);
searchControl.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ARROW_DOWN) {
definitionControl.setFocus();
}
}
});
definitionControl.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.keyCode == SWT.ARROW_DOWN) {
TableViewer tableViewer = getTableViewer();
tableViewer.getTable().setFocus();
if (tableViewer.getSelection().isEmpty()) {
Object firstElement = tableViewer.getElementAt(0);
if (firstElement != null) {
tableViewer.setSelection(new StructuredSelection(firstElement));
}
}
}
}
});
if (initialPatternText != null) {
searchControl.setText(initialPatternText);
searchControl.selectAll();
}
readSettings();
return label;
}
private void initCustomersProductsSettings(Composite parent, SelectionAdapter selectionListener){
ExpandableComposite exComposite = createStyleSection(parent, "product/customer settings", 2);
Composite composite=new Composite(exComposite, SWT.NONE);
exComposite.setClient(composite);
GridLayout leftRight=new GridLayout(2, true);
leftRight.marginWidth=0;
composite.setLayout(leftRight);
productViewer=getViewer(composite, "Terms for products", possibleProducs);
customerViewer=getViewer(composite, "Terms for customers", possibleCustomers);
}
private EObjectDescriptionCheckboxTableViewer getViewer(Composite parent, String label, Iterable<IEObjectDescription> values){
ICheckStateListener listener = new ICheckStateListener() {
public void checkStateChanged(CheckStateChangedEvent event) {
applyFilter();
}
};
Composite pc=new Composite(parent, SWT.NONE);
pc.setLayout(new GridLayout());
GridData layoutData = new GridData(GridData.VERTICAL_ALIGN_BEGINNING|GridData.FILL_HORIZONTAL);
pc.setLayoutData(layoutData);
Label l=new Label(pc, SWT.NONE);
l.setText(label);
EObjectDescriptionCheckboxTableViewer result=EObjectDescriptionCheckboxTableViewer.newCheckList(pc, SWT.BORDER);
result.addCheckStateListener(listener);
result.setDescriptions(values);
Table table = result.getTable();
GridData gd = new GridData(GridData.FILL_BOTH|GridData.GRAB_HORIZONTAL);
if(table.getItems().length>6){
gd.heightHint = convertHeightInCharsToPixels(6);
}
table.setLayoutData(gd);
return result;
}
private void makeCompact(GridLayout layout){
layout.marginWidth=0;
layout.verticalSpacing=0;
}
private void initSearchScopeSettings(Composite parent, SelectionAdapter selectionListener){
ExpandableComposite exComposite = createStyleSection(parent, "general settings", 3);
Composite composite=new Composite(exComposite, SWT.NONE);
exComposite.setClient(composite);
GridLayout compositeLayout=new GridLayout();
makeCompact(compositeLayout);
composite.setLayout(compositeLayout);
Composite leftRight=new Composite(composite, SWT.NONE);
GridLayout leftRightLayout=new GridLayout(2, false);
makeCompact(leftRightLayout);
leftRight.setLayout(leftRightLayout);
//left
Label searchIn=new Label(leftRight, SWT.NONE);
searchIn.setText("Also search in:");
//right
Label useCustomerProductSettings=new Label(leftRight, SWT.NONE);
useCustomerProductSettings.setText("Customer/product settings:");
//left
Composite checkboxComposite = new Composite(leftRight, SWT.NONE);
setDefaultGridData(checkboxComposite);
GridLayout checkboxCompositeLayout = new GridLayout(2, true);
makeCompact(checkboxCompositeLayout);
checkboxComposite.setLayout(checkboxCompositeLayout);
definitionControl=new Button(checkboxComposite, SWT.CHECK);
definitionControl.setText("Definition");
definitionControl.addSelectionListener(selectionListener);
usageControl=new Button(checkboxComposite, SWT.CHECK);
usageControl.setText("Usage");
usageControl.addSelectionListener(selectionListener);
//right
useCustomerProductsControl=new Button(leftRight, SWT.CHECK);
useCustomerProductsControl.setText("enabled");
useCustomerProductsControl.addSelectionListener(selectionListener);
Label searchStatus=new Label(composite, SWT.NONE);
searchStatus.setText("Terms with status:");
Composite checkboxComposite2 = new Composite(composite, SWT.NONE);
setDefaultGridData(checkboxComposite2);
GridLayout checkboxComposite2Layout = new GridLayout(5, true);
makeCompact(checkboxComposite2Layout);
checkboxComposite2.setLayout(checkboxComposite2Layout);
statusControls=new LinkedHashMap<TermStatus, Button>();
for (TermStatus status: TermStatus.values()) {
Button statusButton=new Button(checkboxComposite2, SWT.CHECK);
statusButton.setText(status.toString());
statusButton.addSelectionListener(selectionListener);
statusControls.put(status, statusButton);
}
}
protected ExpandableComposite createStyleSection(Composite parent, String label, int nColumns) {
ExpandableComposite excomposite = new ExpandableComposite(parent, ExpandableComposite.TWISTIE
| ExpandableComposite.CLIENT_INDENT);
excomposite.setText(label);
excomposite.setExpanded(false);
excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT));
excomposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, nColumns, 1));
excomposite.addExpansionListener(new ExpansionAdapter() {
@Override
public void expansionStateChanged(ExpansionEvent e) {
getShell().layout(true, true);
}
});
return excomposite;
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
super.createButtonsForButtonBar(parent);
getOkButton().setText("Save Settings and Close");
setButtonLayoutData(getOkButton());
getButton(IDialogConstants.CANCEL_ID).setText("Close");
}
private void readSettings() {
definitionControl.setSelection(getDialogSettings().getBoolean("definition"));
usageControl.setSelection(getDialogSettings().getBoolean("usage"));
useCustomerProductsControl.setSelection(getDialogSettings().getBoolean("enableCPsettings"));
for (TermStatus status : TermStatus.values()) {
statusControls.get(status).setSelection(getDialogSettings().getBoolean(status.toString()));
}
readEObjectSettings("products", productViewer);
readEObjectSettings("customers", customerViewer);
}
private void readEObjectSettings(String key, EObjectDescriptionCheckboxTableViewer viewer){
if(getDialogSettings().getArray(key)!=null){
viewer.setCheckedNames(getDialogSettings().getArray(key));
}
}
private IDialogSettings getDialogSettings(){
IDialogSettings settings = TerminologyActivator.getInstance().getDialogSettings();
if(settings.getSection(SETTINGS_SECTION)==null){
initDefaultSettings(settings);
}
return settings.getSection(SETTINGS_SECTION);
}
private void initDefaultSettings(IDialogSettings settings) {
IDialogSettings newSetting = settings.addNewSection(SETTINGS_SECTION);
for (TermStatus status : TermStatus.values()) {
newSetting.put(status.toString(), true);
}
}
private void setDefaultGridData(Control control) {
control.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL));
}
protected void applyFilter() {
boolean useCustomerProductSettings = useCustomerProductsControl.getSelection();
setProductCustomerSettingsEnabled(useCustomerProductSettings);
TerminologySearchPattern pattern=new TerminologySearchPattern();
pattern.setTextPattern(searchControl.getText());
pattern.setInDefinition(definitionControl.getSelection());
pattern.setUseCustomerProducts(useCustomerProductSettings);
pattern.setInUsage(usageControl.getSelection());
pattern.setStatus(statusControls);
pattern.setProducts(productViewer.getChecked());
pattern.setCustomers(customerViewer.getChecked());
// if (pattern.textPattern != null) {
Iterable<IEObjectDescription> matches = getSearchEngine().findMatches(pattern);
startSizeCalculation(matches);
// }
}
private void setProductCustomerSettingsEnabled(boolean enabled){
productViewer.getControl().setEnabled(enabled);
customerViewer.getControl().setEnabled(enabled);
}
public void updateMatches(final Collection<IEObjectDescription> matches, final boolean isFinished) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
if (getShell() != null) {
if (getTableViewer() != null) {
getTableViewer().setItemCount(matches.size());
getTableViewer().setInput(matches);
if (getTableViewer().getSelection().isEmpty() && matches.size() > 0)
getTableViewer().getTable().select(0);
}
searchStatusLabel
.setText((isFinished) ? "" : "Search"); //$NON-NLS-1$
matchingElementsLabel.setText(matches.size() + " hits"); //$NON-NLS-1$
}
}
});
}
/**
* Called by {@link #applyFilter()} and is responsible for calling {@link #updateMatches(Collection, boolean)} with
* an appropriately sorted list of matches.
*
* @since 2.0
*/
protected void startSizeCalculation(Iterable<IEObjectDescription> matches) {
if (getTableViewer() != null) {
if (sizeCalculationJob != null) {
sizeCalculationJob.cancel();
try {
sizeCalculationJob.join();
} catch (InterruptedException e) {
sizeCalculationJob = new TerminologyIteratorJob(this);
}
} else {
sizeCalculationJob = new TerminologyIteratorJob(this);
}
sizeCalculationJob.init(matches);
sizeCalculationJob.schedule();
}
}
}