blob: df70b19428b3e308fabc159ef57f02b81e8c3f9a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 SAP AG, Walldorf.
* 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:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.platform.discovery.ui.internal.selector;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.platform.discovery.core.internal.selectors.IItemSelector;
import org.eclipse.platform.discovery.runtime.api.IDestinationsProvider;
import org.eclipse.platform.discovery.runtime.api.ISearchDestination;
import org.eclipse.platform.discovery.runtime.internal.model.descriptions.IDestinationCategoryDescription;
import org.eclipse.platform.discovery.ui.internal.plugin.DiscoveryUIMessages;
import org.eclipse.platform.discovery.ui.internal.view.IGetControlObject;
import org.eclipse.platform.discovery.ui.internal.view.SearchConsoleView;
import org.eclipse.platform.discovery.ui.internal.view.impl.DestinationsContentProvider;
import org.eclipse.platform.discovery.ui.internal.view.impl.DestinationsLabelProvider;
import org.eclipse.platform.discovery.util.internal.property.IPropertyAttributeListener;
import org.eclipse.platform.discovery.util.internal.property.Property;
import org.eclipse.platform.discovery.util.internal.property.PropertyAttributeChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.forms.widgets.FormToolkit;
/**
* The UI component for selecting search destinations
*
* @author Danail Branekov
*
*/
public abstract class SearchDestinationsSelector implements IItemSelector<ISearchDestination, IDestinationCategoryDescription>, IGetControlObject<Control>
{
private final TreeViewer destinationsTreeViewer;
private final Property<IStructuredSelection> lastKnownSelection;
public SearchDestinationsSelector(final Composite parent, final FormToolkit formToolkit, final int secondColumnPosition, final Control topNeighbourControl)
{
lastKnownSelection = new Property<IStructuredSelection>();
lastKnownSelection.set(new StructuredSelection());
lastKnownSelection.registerValueListener(new SelectionChangedListener(), false);
Label destinationLabel = new Label(parent, SWT.FLAT);
destinationLabel.setText(DiscoveryUIMessages.SEARCH_IN_LIST_VIEWER_LABEL);
final FormData labelSearchedInFormData = new FormData();
labelSearchedInFormData.left = new FormAttachment(0, SearchConsoleView.UI_IN_CONTROL_SPACING);
labelSearchedInFormData.top = new FormAttachment(topNeighbourControl, SearchConsoleView.UI_IN_CONTROL_SPACING);
destinationLabel.setLayoutData(labelSearchedInFormData);
destinationsTreeViewer = createDestinationsTreeViewer(parent);
formToolkit.adapt(destinationsTreeViewer.getControl(), true, true);
final FormData searchInFormData = new FormData();
searchInFormData.top = new FormAttachment(topNeighbourControl, SearchConsoleView.UI_IN_CONTROL_SPACING);
searchInFormData.left = new FormAttachment(0, secondColumnPosition + 2*SearchConsoleView.UI_IN_CONTROL_SPACING);
searchInFormData.right = new FormAttachment(100, -SearchConsoleView.UI_IN_CONTROL_SPACING);
searchInFormData.height = Math.max(destinationsTreeViewer.getControl().computeSize(SWT.DEFAULT, SWT.DEFAULT).y, treeViewerMinHeight());
destinationsTreeViewer.getControl().setLayoutData(searchInFormData);
}
public ISearchDestination getSelectedItem()
{
final Object selectedObject = ((IStructuredSelection) destinationsTreeViewer.getSelection()).getFirstElement();
if (selectedObject instanceof IDestinationCategoryDescription)
{
return null;
}
return (ISearchDestination) selectedObject;
}
private TreeViewer createDestinationsTreeViewer(final Composite parent)
{
final TreeViewer viewer = new TreeViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
viewer.setContentProvider(new DestinationsContentProvider()
{
@Override
protected List<ISearchDestination> getSearchDestinationsForCategory(final IDestinationCategoryDescription category)
{
final List<ISearchDestination> result = new ArrayList<ISearchDestination>();
for(IDestinationsProvider destinationsProvider : getDestinationProvidersForCategory(category))
{
result.addAll(getSearchDestinations(category, destinationsProvider));
}
return result;
}
});
viewer.setLabelProvider(new DestinationsLabelProvider());
viewer.addSelectionChangedListener(new ISelectionChangedListener()
{
@Override
public void selectionChanged(final SelectionChangedEvent event)
{
// filter out empty selection events - registered listeners have to be notified, but the selection property needs to remain unchanged
// Also, empty selection can happen only via API - the user is not capable of delesecting everything
if(event.getSelection().isEmpty())
{
handleSelectionChange(null);
return;
}
lastKnownSelection.set((IStructuredSelection) event.getSelection());
}
});
return viewer;
}
@Override
public Control getControl() {
return destinationsTreeViewer.getControl();
}
@Override
public void setInput(List<IDestinationCategoryDescription> input)
{
updateInternal(input);
}
@SuppressWarnings("unchecked")
public void update()
{
updateInternal((List<IDestinationCategoryDescription>) destinationsTreeViewer.getInput());
}
private void updateInternal(final List<IDestinationCategoryDescription> input)
{
destinationsTreeViewer.setInput(input);
refreshAndExpandTreeViewer(destinationsTreeViewer);
if(!input.isEmpty())
{
restoreLastKnownSelection(destinationsTreeViewer);
}
if (input.size() == 1)
{
final Set<ISearchDestination> result = new HashSet<ISearchDestination>();
for(IDestinationsProvider destinationsProvider : getDestinationProvidersForCategory(input.iterator().next()))
{
result.addAll(getSearchDestinations(input.iterator().next(), destinationsProvider));
}
if (result.size() == 1)
{
destinationsTreeViewer.setSelection(new StructuredSelection(result.iterator().next()));
}
}
}
private void refreshAndExpandTreeViewer(TreeViewer viewer)
{
viewer.refresh();
viewer.expandAll();
}
/**
* Restores the last known selection
*/
private void restoreLastKnownSelection(final TreeViewer viewer)
{
viewer.setSelection(lastKnownSelection.get(), true);
lastKnownSelection.set((IStructuredSelection)viewer.getSelection());
}
public void setEnabled(boolean enabled)
{
destinationsTreeViewer.getControl().setEnabled(enabled);
}
@Override
public boolean isEnabled()
{
return destinationsTreeViewer.getControl().isEnabled();
}
/**
* Retrieves a set of destination providers which are capable of providing destinations for the category specified
*
* @param category
* the destination category
* @return a set of destination provider descriptions
*/
protected abstract Set<IDestinationsProvider> getDestinationProvidersForCategory(final IDestinationCategoryDescription category);
/**
* Retrieves a list of search destinations which are relevant to the destination category and destination provider specified
*
* @param category
* the destination category
* @param destinationsProvider
* the destinations provider
* @return a set of search destinations
*/
protected abstract List<ISearchDestination> getSearchDestinations(final IDestinationCategoryDescription category,
final IDestinationsProvider destinationsProvider);
private class SelectionChangedListener implements IPropertyAttributeListener<IStructuredSelection>
{
@Override
public void attributeChanged(final PropertyAttributeChangedEvent<IStructuredSelection> event)
{
final IStructuredSelection selection = event.getNewAttribute();
if (selection.isEmpty() || !(selection.getFirstElement() instanceof ISearchDestination))
{
handleSelectionChange(null);
return;
}
handleSelectionChange((ISearchDestination)selection.getFirstElement());
}
}
protected int treeViewerMinHeight()
{
// TODO: Maybe take a percentage of screen size!?
return 80;
}
public IDestinationCategoryDescription getActiveDestinationCategory() {
IStructuredSelection selection = lastKnownSelection.get();
if(!(selection instanceof TreeSelection)) {
//this means initial empty selection
return null;
}
TreeSelection treeSelection = (TreeSelection) selection;
Object selectedElement = treeSelection.getFirstElement();
if (selectedElement instanceof IDestinationCategoryDescription) {
return (IDestinationCategoryDescription)selectedElement;
}
if(selectedElement instanceof ISearchDestination) {
TreePath pathToSelected = treeSelection.getPathsFor(selectedElement)[0];
assert pathToSelected.getFirstSegment() instanceof IDestinationCategoryDescription;
return (IDestinationCategoryDescription) pathToSelected.getFirstSegment() ;
}
return null;
}
}