/*****************************************************************************
* Copyright (c) 2010 CEA LIST.
*
* 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:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.infra.widgets.selectors;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.papyrus.infra.widgets.editors.IElementSelectionListener;
import org.eclipse.papyrus.infra.widgets.editors.IElementSelector;
import org.eclipse.papyrus.infra.widgets.providers.EncapsulatedContentProvider;
import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
/**
* A Selector for Multiple Reference values, with a filter
*
* This selector is compatible with {@link org.eclipse.papyrus.infra.widgets.providers.IAdaptableContentProvider}
*
* @author Camille Letavernier
*
*/
public class ReferenceSelector implements IElementSelector {
// /**
// * A Widget to enter a filter as a String, accepting wildcards
// */
// protected Filter filter;
/**
* The display tree
*/
protected FilteredTree fTree;
/**
* The content provider, returning the available reference values
*/
protected EncapsulatedContentProvider contentProvider;
/**
* The content provider, returning the available reference labels
*/
protected ILabelProvider labelProvider;
/**
* Indicates if the reference values should be unique
*/
protected boolean unique;
/**
* Indicates if this selector should be able to return more than one value
* at a time.
*/
protected boolean multiSelection;
private Set<IElementSelectionListener> elementSelectionListeners = new HashSet<IElementSelectionListener>();
/**
* The set of selected elements. If the selector is marked as "unique",
* these elements will be filtered in the Tree.
*
* The Elements are in their container form
*/
protected Set<Object> selectedElements = new HashSet<Object>();
/**
*
* Constructor.
*
* @param unique
* Indicates if the values are unique. If true, they are removed
* from the list when they are chosen
*/
public ReferenceSelector(boolean unique) {
this.unique = unique;
this.multiSelection = true;
}
/**
*
* Constructor.
* Builds a new ReferenceSelector for a single element
*
*/
public ReferenceSelector() {
this.unique = false;
this.multiSelection = false;
}
/**
* {@inheritDoc}
*/
public Object[] getSelectedElements() {
ISelection selection = fTree.getViewer().getSelection();
if(selection instanceof IStructuredSelection) {
Object[] containerElementsToMove = getElementsToMove(((IStructuredSelection)selection).toArray());
Object[] semanticElementsToMove = getSemanticElements(containerElementsToMove);
addSelectedElements(semanticElementsToMove);
return semanticElementsToMove;
}
return new Object[0];
}
// /**
// * This method is used for handling correctly the IAdaptableContentProvider
// * The objects can be in two different forms :
// * - The semantic element
// * - The container element
// *
// * This methods returns an array of container elements from an array of
// * semantic elements. This is useful when specifying a selection to a
// * viewer using an IAdaptableContentProvider
// *
// * @param semanticElements
// * The array of semantic elements to be converted
// * @return
// * The array of elements wrapped in their container
// *
// * @see #getSemanticElements(Object[])
// * @see org.eclipse.papyrus.infra.widgets.providers.IAdaptableContentProvider
// */
// private Object[] getContainerElements(Object[] semanticElements) {
// Object[] containerElements = new Object[semanticElements.length];
// int i = 0;
// for(Object semanticElement : semanticElements) {
// containerElements[i++] = contentProvider.getContainerValue(semanticElement);
// }
// return containerElements;
// }
/**
* This method is used for handling correctly the IAdaptableContentProvider
* The objects can be in two different forms :
* - The semantic element
* - The container element
*
* This methods returns an array of semantic elements from an array of
* container elements. This is useful for retrieving the semantic elements
* from a viewer's selection when the viewer uses an IAdaptableContentProvider
*
* @param containerElements
* The array of elements wrapped in their container
* @return
* The array of semantic elements to be converted
*
* @see #getContainerElements(Object[])
* @see org.eclipse.papyrus.infra.widgets.providers.IAdaptableContentProvider
*/
private Object[] getSemanticElements(Object[] containerElements) {
Object[] semanticElements = new Object[containerElements.length];
int i = 0;
for(Object containerElement : containerElements) {
semanticElements[i++] = contentProvider.getAdaptedValue(containerElement);
}
return semanticElements;
}
/**
* Filters the selection to return only the objects that can
* be selected, according to the content provider.
*
* @param selection
* The input array to filter
* @return
* The filtered array
*
* @see org.eclipse.papyrus.infra.widgets.providers.IHierarchicContentProvider#isValidValue(Object)
*/
protected Object[] getElementsToMove(Object[] selection) {
List<Object> elementsToMove = new LinkedList<Object>();
for(Object element : selection) {
if(contentProvider.isValidValue(element)) {
elementsToMove.add(element);
}
}
return elementsToMove.toArray();
}
/**
* Adds elements to the list of selected elements. If the values are
* unique, the specified elements won't be displayed
*
* @param elements
*/
private void addSelectedElements(Object[] semanticElements) {
if(semanticElements.length > 0) {
selectedElements.addAll(Arrays.asList(semanticElements));
fTree.getViewer().refresh();
}
}
/**
* Returns all the elements that are currently displayed, i.e. matching
* the filter
*
* {@link IElementSelector#getAllElements()}
*
* @return all elements matching the filter
*/
public Object[] getAllElements() {
//There is no way to retrieve the filteredElements on a FList
//We can only retrieve the selected ones
//Fix : we select everything, then we return the selection
if(contentProvider == null) {
return new Object[0];
}
Collection<Object> visibleElements = new LinkedList<Object>();
for(TreeItem rootItem : fTree.getViewer().getTree().getItems()) {
visibleElements.add(getElement(rootItem));
if(rootItem.getExpanded()) {
fillVisibleElements(rootItem, visibleElements);
}
}
// fTree.getViewer().refresh();
// fTree.getViewer().setSelection(new StructuredSelection(visibleElements));
// Object[] containerElementsToMove = getElementsToMove(((IStructuredSelection)fTree.getViewer().getSelection()).toArray());
Object[] containerElementsToMove = getElementsToMove(visibleElements.toArray());
Object[] semanticElementsToMove = getSemanticElements(containerElementsToMove);
addSelectedElements(semanticElementsToMove);
return semanticElementsToMove;
}
private void fillVisibleElements(TreeItem item, Collection<Object> visibleElements) {
for(TreeItem childItem : item.getItems()) {
visibleElements.add(getElement(childItem));
if(childItem.getExpanded()) {
fillVisibleElements(childItem, visibleElements);
}
}
}
private Object getElement(TreeItem item) {
return item.getData();
}
/**
* Sets the list of selected elements. If the values are
* unique, the specified elements won't be displayed
*
* @param elements
*/
public void setSelectedElements(Object[] semanticElements) {
selectedElements.clear();
selectedElements.addAll(Arrays.asList(semanticElements));
fTree.getViewer().refresh();
}
public void newObjectCreated(Object newObject) {
contentProvider.addTemporaryElement(newObject);
refresh();
}
public void clearTemporaryElements() {
contentProvider.clearTemporaryElements();
}
/**
* Refreshes this selector's {@link org.eclipse.swt.widgets.List}
*/
public void refresh() {
fTree.getViewer().refresh();
}
/**
* Sets this selector's label provider. The label provider is used
* to display the reference values
*
* @param labelProvider
*/
public void setLabelProvider(ILabelProvider labelProvider) {
this.labelProvider = labelProvider;
if(fTree != null) {
fTree.getViewer().setLabelProvider(labelProvider);
}
}
/**
* Sets this selector's content provider. The content provider
* is used to select the available values for this property
*
* @param staticContentProvider
*/
public void setContentProvider(IStaticContentProvider staticContentProvider) {
this.contentProvider = new EncapsulatedContentProvider(staticContentProvider);
if(fTree != null) {
fTree.getViewer().setContentProvider(contentProvider);
fTree.getViewer().setInput(""); //$NON-NLS-1$
}
}
/**
* {@inheritDoc}
*/
public void createControls(Composite parent) {
Composite content = new Composite(parent, SWT.BORDER);
content.setLayout(new GridLayout(1, true));
// filter = new Filter(content, SWT.BORDER);
// filter.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
final PatternFilter filter = new PatternFilter();
filter.setPattern("*"); //$NON-NLS-1$
fTree = new FilteredTree(content, SWT.MULTI | SWT.BORDER, new PatternFilter(), true);
//fList = new FilteredList(content, SWT.MULTI | SWT.BORDER, labelProvider, true, true, true);
fTree.getViewer().getTree().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
if(contentProvider != null) {
fTree.getViewer().setContentProvider(contentProvider);
fTree.getViewer().setInput(""); //$NON-NLS-1$
}
if(labelProvider != null) {
fTree.getViewer().setLabelProvider(labelProvider);
}
//
// this.filter.addChangeListener(new Listener() {
//
// public void handleEvent(Event event) {
// filter.setPattern(ReferenceSelector.this.filter.getFilter());
// }
// });
fTree.getViewer().addFilter(new ViewerFilter() {
@Override
public boolean select(Viewer viewer, Object parentElement, Object containerElement) {
if(unique) {
//TODO : check if the selected element has selectable children
return !selectedElements.contains(contentProvider.getAdaptedValue(containerElement));
} else {
return true;
}
}
});
//Adds double-click support
fTree.getViewer().getTree().addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
// Nothing
}
public void widgetDefaultSelected(SelectionEvent e) {
if(!elementSelectionListeners.isEmpty()) {
Object[] selectedElements = getSelectedElements();
for(IElementSelectionListener listener : elementSelectionListeners) {
listener.addElements(selectedElements);
}
}
}
});
}
public void setUnique(boolean unique) {
this.unique = unique;
}
public void addElementSelectionListener(IElementSelectionListener listener) {
elementSelectionListeners.add(listener);
}
}