/******************************************************************************* | |
* Copyright (c) 2010, 2015 Mia-Software, CEA-LIST, and Soft-Maint. | |
* 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: | |
* Nicolas Bros (Mia-Software) - Bug 370806 - [table] rewrite the "allowed contents" query selection dialog for v0.2 | |
* Vincent Lorenzo (CEA-LIST) - Bug 372644 - Create Customizable tooltips for the TreeViewer using a CustomizableLabelProvider | |
* Gregoire Dupe (Mia-Software) - Bug 372644 - Create Customizable tooltips for the TreeViewer using a CustomizableLabelProvider | |
* Gregoire Dupe (Mia-Software) - Bug 373078 - API Cleaning | |
* Vincent Lorenzo (CEA-LIST) - Bug 357621 - Improve the label displayed for Customization and Facets | |
* Gregoire Dupe (Mia-Software) - Bug 372626 - Aggregates | |
* Olivier Remaud (Soft-Maint) - Bug 377615 - Query View filtering | |
* Vincent Lorenzo (CEA-LIST) - Bug 357621 - Improve the label displayed for Customization and Facets | |
* Vincent Lorenzo (CEA-LIST) - Bug 377909 - The elements displayed in the ETypedElement dialog are not sorted by alphabetic order | |
* Thomas Cicognani (Mia-Software) - Bug 472075 - Update API to create UI shortcuts to activate FacetSets | |
*******************************************************************************/ | |
package org.eclipse.emf.facet.efacet.ui.internal.widget; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.HashSet; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Set; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.emf.common.util.TreeIterator; | |
import org.eclipse.emf.ecore.EObject; | |
import org.eclipse.emf.ecore.ETypedElement; | |
import org.eclipse.emf.facet.efacet.ui.internal.Activator; | |
import org.eclipse.emf.facet.efacet.ui.internal.Messages; | |
import org.eclipse.emf.facet.efacet.ui.internal.utils.ImageProvider; | |
import org.eclipse.emf.facet.util.core.internal.exported.IFilter; | |
import org.eclipse.emf.facet.util.emf.ui.internal.exported.IEmfLabelProviderFactory; | |
import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; | |
import org.eclipse.jface.viewers.IBaseLabelProvider; | |
import org.eclipse.jface.viewers.IContentProvider; | |
import org.eclipse.jface.viewers.ILabelProvider; | |
import org.eclipse.jface.viewers.IOpenListener; | |
import org.eclipse.jface.viewers.ISelectionChangedListener; | |
import org.eclipse.jface.viewers.IStructuredSelection; | |
import org.eclipse.jface.viewers.ITreeContentProvider; | |
import org.eclipse.jface.viewers.OpenEvent; | |
import org.eclipse.jface.viewers.SelectionChangedEvent; | |
import org.eclipse.jface.viewers.StructuredSelection; | |
import org.eclipse.jface.viewers.StructuredViewer; | |
import org.eclipse.jface.viewers.Viewer; | |
import org.eclipse.jface.viewers.ViewerComparator; | |
import org.eclipse.jface.viewers.ViewerFilter; | |
import org.eclipse.osgi.util.NLS; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.events.SelectionAdapter; | |
import org.eclipse.swt.events.SelectionEvent; | |
import org.eclipse.swt.graphics.Image; | |
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.ui.dialogs.FilteredTree; | |
import org.eclipse.ui.dialogs.PatternFilter; | |
/** | |
* A control that displays a tree of ETypedElements under a list of containers (EPackages for example), with a filter | |
* text field. | |
*/ | |
public class ETypedElementSelectionControl implements IETypedElementTabSelectionWidgetInternal { | |
public static final String TAB_ID = "flat.view.id"; //$NON-NLS-1$ | |
public static final String TITLE = Messages.ETypedElementSelectionControl_FlatView; | |
/** An OK status, but without the "OK" message, because we don't want "OK" to appear in the status bar */ | |
private static final IStatus OK_STATUS = new Status(IStatus.OK, Activator.PLUGIN_ID, ""); //$NON-NLS-1$ | |
private FilteredTree filteredTree; | |
protected Collection<? extends ETypedElement> available; | |
private final int selectionMaxSize; | |
private final boolean allowEmpty; | |
private Button rbNoSelection; | |
private Button rbSelection; | |
private IStatus validationStatus; | |
private final IBaseLabelProvider labelProvider; | |
private final Composite cParent; | |
private final Collection<? extends EObject> knownEPackage; | |
/** Create a tree with a filter text field to select ETypedElements. */ | |
public ETypedElementSelectionControl(final Composite parentComposite, | |
final int selectionMaxSize, final boolean allowEmpty, | |
final Collection<? extends EObject> knownEPackage) { | |
this.cParent = parentComposite; | |
this.knownEPackage = knownEPackage; | |
this.selectionMaxSize = selectionMaxSize; | |
this.allowEmpty = allowEmpty; | |
this.labelProvider = IEmfLabelProviderFactory.DEFAULT.createLabelProvider(); | |
} | |
public void createContents() { | |
final Composite composite = new Composite(this.cParent, SWT.NONE); | |
composite.setLayout(new GridLayout()); | |
composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); | |
if (this.allowEmpty) { | |
this.rbNoSelection = new Button(composite, SWT.RADIO); | |
this.rbNoSelection.setText(Messages.ETypedElementSelectionDialog_radioButtonNoSelection); | |
this.rbNoSelection.setSelection(true); | |
this.rbNoSelection.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(final SelectionEvent event) { | |
final boolean selected = getRadioButtonNoSelection().getSelection(); | |
setETypedElementSelectionEnabled(!selected); | |
if (selected) { | |
setSelection(StructuredSelection.EMPTY); | |
} | |
} | |
}); | |
this.rbSelection = new Button(composite, SWT.RADIO); | |
if (this.selectionMaxSize > 1) { | |
this.rbSelection.setText(Messages.ETypedElementSelectionDialog_radioButtonMultiSelection); | |
} else { | |
this.rbSelection.setText(Messages.ETypedElementSelectionDialog_radioButtonSingleSelection); | |
} | |
this.rbSelection.setSelection(false); | |
this.rbSelection.addSelectionListener(new SelectionAdapter() { | |
@Override | |
public void widgetSelected(final SelectionEvent event) { | |
final boolean selected = getRadioButtonSelection().getSelection(); | |
setETypedElementSelectionEnabled(selected); | |
if (!selected) { | |
setSelection(StructuredSelection.EMPTY); | |
} | |
} | |
}); | |
} | |
createFilteredTree(composite); | |
this.filteredTree.getViewer().addSelectionChangedListener(new ISelectionChangedListener() { | |
public void selectionChanged(final SelectionChangedEvent event) { | |
updateValidationStatus(); | |
} | |
}); | |
if (this.allowEmpty) { | |
setETypedElementSelectionEnabled(false); | |
} | |
updateValidationStatus(); | |
} | |
public void addOpenListener(final IOpenListener openListener) { | |
// double-click or enter to choose the selected typed element | |
this.filteredTree.getViewer().addOpenListener(new IOpenListener() { | |
public void open(final OpenEvent event) { | |
// only if the selection is valid | |
if (!isErrorStatus()) { | |
openListener.open(event); | |
} | |
} | |
}); | |
} | |
public void addSelectionChangedListener(final ISelectionChangedListener listener) { | |
this.filteredTree.getViewer().addSelectionChangedListener(listener); | |
} | |
private void createFilteredTree(final Composite parentComposite) { | |
this.filteredTree = new FilteredTree(parentComposite, selectionStyle(this.selectionMaxSize) | SWT.H_SCROLL | SWT.V_SCROLL | |
| SWT.BORDER, createPatternFilter(), true); | |
// allow tooltip for this viewer | |
ColumnViewerToolTipSupport.enableFor(this.filteredTree.getViewer()); | |
this.filteredTree.getViewer().setContentProvider(createContentProvider()); | |
this.filteredTree.getViewer().setLabelProvider(this.labelProvider); | |
this.filteredTree.getViewer().setFilters(new ViewerFilter[] { this.filteredTree.getPatternFilter() }); | |
this.filteredTree.getViewer().setComparator(new ViewerComparator()); | |
} | |
protected void updateValidationStatus() { | |
if (this.filteredTree == null) { | |
return; | |
} | |
final Status errorElements = new Status(IStatus.ERROR, Activator.PLUGIN_ID, | |
Messages.ETypedElementSelectionControl_invalidSelectionETypedElementsOnly); | |
final Status errorSelection = new Status(IStatus.ERROR, Activator.PLUGIN_ID, Messages.ETypedElementSelectionControl_invalidSelection); | |
final IStructuredSelection selection = (IStructuredSelection) this.filteredTree.getViewer().getSelection(); | |
IStatus newStatus = ETypedElementSelectionControl.OK_STATUS; | |
if (!this.allowEmpty && selection.isEmpty()) { | |
newStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.ETypedElementSelectionControl_invalidSelectionAtLeastOneElement, | |
Integer.valueOf(this.selectionMaxSize))); | |
} else if (selection.size() > this.selectionMaxSize) { | |
newStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, NLS.bind(Messages.ETypedElementSelectionControl_invalidSelectionAtMostXElements, | |
Integer.valueOf(this.selectionMaxSize))); | |
} else { | |
final Iterator<?> iterator = selection.iterator(); | |
while (iterator.hasNext()) { | |
final Object selectedElement = iterator.next(); | |
if (!(selectedElement instanceof ETypedElement)) { | |
newStatus = errorElements; | |
break; | |
} | |
} | |
} | |
setValidationStatus(newStatus); | |
} | |
protected void setValidationStatus(final IStatus status) { | |
this.validationStatus = status; | |
} | |
public IStatus getValidationStatus() { | |
return this.validationStatus; | |
} | |
public boolean isErrorStatus() { | |
return this.validationStatus == null || this.validationStatus.getSeverity() >= IStatus.ERROR; | |
} | |
public void setAvailableETypedElements(final Collection<? extends ETypedElement> newAvailable) { | |
this.available = newAvailable; | |
final Set<EObject> containers = new HashSet<EObject>(); | |
for (final ETypedElement eTypedElement : newAvailable) { | |
final EObject container = getTopmostContainer(eTypedElement); | |
if (container != null && containsETypedElement(container)) { | |
containers.add(container); | |
} | |
} | |
for (EObject eObject : this.knownEPackage) { | |
if (containsETypedElement(eObject)) { | |
containers.add(eObject); | |
} | |
} | |
setInput(containers); | |
} | |
protected static EObject getTopmostContainer(final EObject eObject) { | |
EObject container = eObject.eContainer(); | |
EObject result = container; | |
while (container != null) { | |
result = container; | |
container = container.eContainer(); | |
} | |
return result; | |
} | |
public List<ETypedElement> getSelectedETypedElements() { | |
final List<ETypedElement> currentlySelected = new ArrayList<ETypedElement>(); | |
if (this.filteredTree.isDisposed()) { | |
throw new IllegalStateException("Cannot get the selection since the viewer is disposed."); //$NON-NLS-1$ | |
} | |
final IStructuredSelection selection = (IStructuredSelection) this.filteredTree.getViewer() | |
.getSelection(); | |
final Iterator<?> iterator = selection.iterator(); | |
while (iterator.hasNext()) { | |
final Object element = iterator.next(); | |
if (element instanceof ETypedElement) { | |
final ETypedElement eTypedElement = (ETypedElement) element; | |
currentlySelected.add(eTypedElement); | |
} | |
} | |
return currentlySelected; | |
} | |
public void setSelectedETypedElements(final Collection<? extends ETypedElement> newSelection) { | |
if (newSelection == null) { | |
throw new IllegalArgumentException(); | |
} | |
final List<ETypedElement> filteredSelection = new ArrayList<ETypedElement>(); | |
filteredSelection.addAll(newSelection); | |
// only keep available elements | |
filteredSelection.retainAll(this.available); | |
setSelection(new StructuredSelection(filteredSelection)); | |
} | |
public void setSelection(final IStructuredSelection selection) { | |
setETypedElementSelectionEnabled(!selection.isEmpty()); | |
this.filteredTree.getViewer().setSelection(selection); | |
updateValidationStatus(); | |
} | |
protected void setETypedElementSelectionEnabled(final boolean enabled) { | |
getRadioButtonNoSelection().setSelection(!enabled); | |
getRadioButtonSelection().setSelection(enabled); | |
this.filteredTree.getViewer().getTree().setEnabled(enabled); | |
this.filteredTree.getFilterControl().setEnabled(enabled); | |
} | |
public void setInput(final Collection<? extends EObject> containers) { | |
this.filteredTree.getViewer().setInput(containers); | |
} | |
// @SuppressWarnings("unchecked") : this is the input type | |
@SuppressWarnings("unchecked") | |
public Collection<? extends EObject> getInput() { | |
return (Collection<? extends EObject>) this.filteredTree.getViewer().getInput(); | |
} | |
protected IContentProvider createContentProvider() { | |
return new ETypedElementSelectionControlContentProvider(new IFilter<EObject>() { | |
public boolean filter(final EObject eObject) { | |
return filterChild(eObject); | |
} | |
}); | |
} | |
protected boolean filterChild(final EObject eObject) { | |
boolean selected; | |
if (containsETypedElement(eObject)) { | |
selected = true; | |
} else if (eObject instanceof ETypedElement) { | |
selected = isAvailable(eObject); | |
} else { | |
selected = false; | |
} | |
return selected; | |
} | |
protected boolean isAvailable(final EObject eObject) { | |
return this.available.contains(eObject); | |
} | |
protected boolean containsETypedElement(final EObject eObject) { | |
boolean result = false; | |
final TreeIterator<EObject> eAllContents = eObject.eAllContents(); | |
while (eAllContents.hasNext()) { | |
final EObject element = eAllContents.next(); | |
if (element instanceof ETypedElement) { | |
final ETypedElement typedElement = (ETypedElement) element; | |
if (isAvailable(typedElement)) { | |
result = true; | |
break; | |
} | |
} else { | |
result = containsETypedElement(element); | |
if (result) { | |
break; | |
} | |
} | |
} | |
return result; | |
} | |
private static int selectionStyle(final int selectionMaxSize) { | |
int selectionStyle; | |
if (selectionMaxSize > 1) { | |
selectionStyle = SWT.MULTI; | |
} else { | |
selectionStyle = SWT.SINGLE; | |
} | |
return selectionStyle; | |
} | |
private static PatternFilter createPatternFilter() { | |
return new PatternFilter() { | |
@Override | |
protected boolean isLeafMatch(final Viewer viewer, final Object element) { | |
final String labelText = ((ILabelProvider) ((StructuredViewer) viewer).getLabelProvider()) | |
.getText(element); | |
if (labelText == null) { | |
return false; | |
} | |
return wordMatches(labelText) || parentMatches(viewer, element); | |
} | |
private boolean parentMatches(final Viewer viewer, final Object element) { | |
final ITreeContentProvider contentProvider = (ITreeContentProvider) ((StructuredViewer) viewer) | |
.getContentProvider(); | |
final Object parent = contentProvider.getParent(element); | |
if (parent == null) { | |
return false; | |
} | |
return isLeafMatch(viewer, parent); | |
} | |
}; | |
} | |
public IStructuredSelection getSelection() { | |
return (IStructuredSelection) this.filteredTree.getViewer().getSelection(); | |
} | |
protected Button getRadioButtonNoSelection() { | |
return this.rbNoSelection; | |
} | |
protected Button getRadioButtonSelection() { | |
return this.rbSelection; | |
} | |
public FilteredTree getFilteredTree() { | |
return this.filteredTree; | |
} | |
public boolean isDisposed() { | |
return this.filteredTree.isDisposed(); | |
} | |
public String getTitle() { | |
return ETypedElementSelectionControl.TITLE; | |
} | |
public String getToolTipText() { | |
return getTitle(); | |
} | |
public Image getImage() { | |
return ImageProvider.getInstance().getFlatViewIcon(); | |
} | |
public String getTabId() { | |
return ETypedElementSelectionControl.TAB_ID; | |
} | |
public Collection<? extends EObject> getKnownEPackage() { | |
return this.knownEPackage; | |
} | |
} |