/***************************************************************************** * 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.editors; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.Set; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.papyrus.infra.widgets.Activator; import org.eclipse.papyrus.infra.widgets.creation.ReferenceValueFactory; import org.eclipse.papyrus.infra.widgets.messages.Messages; import org.eclipse.papyrus.infra.widgets.providers.CollectionContentProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.FillLayout; 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.Shell; import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.dialogs.SelectionDialog; /** * Object Chooser. Defines a standard popup for selecting * multiple values. * * @author Camille Letavernier * */ public class MultipleValueSelectorDialog extends SelectionDialog implements SelectionListener, IElementSelectionListener { public static int MANY = -1; /** * The object selector */ protected IElementSelector selector; /** * The SWT Composite in which the selector is drawn */ protected Composite selectorSection; /** * The add/remove/addAll buttons section */ protected Composite buttonsSection; /** * The up/down buttons section */ protected Composite rightButtonsSection; /** * The listViewer for chosen elements */ protected StructuredViewer selectedElementsViewer; /** * The list for chosen elements */ protected Tree selectedElements; /** * The add action button */ protected Button add; /** * The create action button */ protected Button create; /** * The delete action button */ protected Button delete; /** * The remove action button */ protected Button remove; /** * The add all action button */ protected Button addAll; /** * The remove all action button */ protected Button removeAll; /** * the up action button */ protected Button up; /** * the down action button */ protected Button down; /** * The label provider for the listViewer of chosen elements */ protected ILabelProvider labelProvider; /** * The currently chosen elements */ protected final Collection<Object> allElements; /** * Indicates if the values should be unique (according to Object.equals()) */ protected boolean unique; /** * Indicates if the list is ordered */ protected boolean ordered; /** * The factory for creating new elements */ protected ReferenceValueFactory factory; /** * The list of newly created objects */ protected Set<Object> newObjects = new HashSet<Object>(); /** * The maximum number of values selected. */ protected int upperBound; /** * Constructor. * * @param parentShell * The shell in which this dialog should be opened * @param selector * The element selector used by this dialog */ public MultipleValueSelectorDialog(Shell parentShell, IElementSelector selector) { this(parentShell, selector, null, false, false); } /** * Constructor. * * @param parentShell * The shell in which this dialog should be opened * @param selector * The element selector used by this dialog * @param title * The title of this dialog */ public MultipleValueSelectorDialog(Shell parentShell, IElementSelector selector, String title) { this(parentShell, selector, title, false, false); } /** * * Constructor. * * @param parentShell * The shell in which this dialog should be opened * @param selector * The element selector used by this dialog * @param unique * True if the values returned by this dialog should be unique */ public MultipleValueSelectorDialog(Shell parentShell, IElementSelector selector, boolean unique) { this(parentShell, selector, null, unique, false); } /** * * Constructor. * * @param parentShell * The shell in which this dialog should be opened * @param selector * The element selector used by this dialog * @param title * The title of this dialog * @param unique * True if the values returned by this dialog should be unique */ public MultipleValueSelectorDialog(Shell parentShell, IElementSelector selector, String title, boolean unique, boolean ordered) { this(parentShell, selector, null, unique, false, MANY); } /** * * Constructor. * * @param parentShell * The shell in which this dialog should be opened * @param selector * The element selector used by this dialog * @param title * The title of this dialog * @param unique * True if the values returned by this dialog should be unique * @param upperBound * The maximum number of values selected. */ public MultipleValueSelectorDialog(Shell parentShell, IElementSelector selector, String title, boolean unique, boolean ordered, int upperBound) { super(parentShell); Assert.isNotNull(selector, "The element selector should be defined"); //$NON-NLS-1$ this.selector = selector; allElements = unique ? new LinkedHashSet<Object>() : new LinkedList<Object>(); setHelpAvailable(false); setTitle(title); this.unique = unique; this.ordered = ordered; this.upperBound = upperBound; selector.addElementSelectionListener(this); } /** * {@inheritDoc} */ @Override public void create() { super.create(); Composite parent = getDialogArea(); GridLayout layout = (GridLayout)parent.getLayout(); layout.numColumns = 2; layout.makeColumnsEqualWidth = true; Composite selectorPane = new Composite(parent, SWT.NONE); selectorPane.setLayout(new GridLayout(2, false)); selectorPane.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); Composite selectedPane = new Composite(parent, SWT.NONE); selectedPane.setLayout(new GridLayout(2, false)); selectedPane.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); createSelectorSection(selectorPane); createControlsSection(selectorPane); createListSection(selectedPane); createRightButtonsSection(selectedPane); getShell().setSize(600, 400); getShell().layout(); super.getShell().setImage(Activator.getDefault().getImage("/icons/papyrus.png")); //$NON-NLS-1$ updateControls(); } /** * {@inheritDoc} */ @Override protected Composite getDialogArea() { return (Composite)super.getDialogArea(); } /** * Creates the selector section * * @param parent * The composite in which the section is created */ private void createSelectorSection(Composite parent) { selectorSection = new Composite(parent, SWT.NONE); selectorSection.setLayout(new FillLayout()); selectorSection.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); selector.createControls(selectorSection); } /** * Creates the main controls section (Add, remove, Add all, remove all) * * @param parent * The composite in which the section is created */ private void createControlsSection(Composite parent) { buttonsSection = new Composite(parent, SWT.NONE); buttonsSection.setLayout(new GridLayout(1, true)); add = new Button(buttonsSection, SWT.PUSH); add.setImage(Activator.getDefault().getImage("/icons/arrow_right.gif")); //$NON-NLS-1$ add.addSelectionListener(this); add.setToolTipText(Messages.MultipleValueSelectorDialog_AddSelectedElements); remove = new Button(buttonsSection, SWT.PUSH); remove.setImage(Activator.getDefault().getImage("/icons/arrow_left.gif")); //$NON-NLS-1$ remove.addSelectionListener(this); remove.setToolTipText(Messages.MultipleValueEditor_RemoveSelectedElements); addAll = new Button(buttonsSection, SWT.PUSH); addAll.setImage(Activator.getDefault().getImage("/icons/arrow_double.gif")); //$NON-NLS-1$ addAll.addSelectionListener(this); addAll.setToolTipText(Messages.MultipleValueSelectorDialog_AddAllElements); /* Disable the bouton 'addAll' if currently chosen elements is greater than the maximum number of values selected */ if(this.upperBound != MANY && allElements.size() > this.upperBound) { addAll.setEnabled(false); } removeAll = new Button(buttonsSection, SWT.PUSH); removeAll.setImage(Activator.getDefault().getImage("/icons/arrow_left_double.gif")); //$NON-NLS-1$ removeAll.addSelectionListener(this); removeAll.setToolTipText(Messages.MultipleValueSelectorDialog_RemoveAllElements); } /** * Creates the list displaying the currently selected elements * * @param parent * The composite in which the section is created */ private void createListSection(Composite parent) { selectedElements = new Tree(parent, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL); selectedElements.addSelectionListener(this); GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); selectedElements.setLayoutData(data); selectedElementsViewer = new TreeViewer(selectedElements); selectedElementsViewer.setContentProvider(CollectionContentProvider.instance); if(labelProvider != null) { selectedElementsViewer.setLabelProvider(labelProvider); } selectedElementsViewer.setInput(allElements); selector.setSelectedElements(allElements.toArray()); } /** * Creates the up/down controls section * * @param parent * The composite in which the section is created */ private void createRightButtonsSection(Composite parent) { rightButtonsSection = new Composite(parent, SWT.NONE); rightButtonsSection.setLayout(new GridLayout(1, true)); up = new Button(rightButtonsSection, SWT.PUSH); up.setImage(Activator.getDefault().getImage("/icons/Up_12x12.gif")); //$NON-NLS-1$ up.addSelectionListener(this); up.setToolTipText(Messages.MultipleValueEditor_MoveSelectedElementsUp); down = new Button(rightButtonsSection, SWT.PUSH); down.setImage(Activator.getDefault().getImage("/icons/Down_12x12.gif")); //$NON-NLS-1$ down.addSelectionListener(this); down.setToolTipText(Messages.MultipleValueEditor_MoveSelectedElementsDown); create = new Button(rightButtonsSection, SWT.PUSH); create.setImage(Activator.getDefault().getImage("/icons/Add_12x12.gif")); //$NON-NLS-1$ create.addSelectionListener(this); create.setToolTipText(Messages.MultipleValueSelectorDialog_CreateNewElement); delete = new Button(rightButtonsSection, SWT.PUSH); delete.setImage(Activator.getDefault().getImage("/icons/Delete_12x12.gif")); //$NON-NLS-1$ delete.addSelectionListener(this); delete.setToolTipText(Messages.MultipleValueSelectorDialog_DeleteNewElement); delete.setEnabled(false); } /** * Sets the label provider used to display the selected elements * * @param labelProvider */ public void setLabelProvider(ILabelProvider labelProvider) { this.labelProvider = labelProvider; } /** * {@inheritDoc} Handles the events on one of the control buttons * * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) * * @param e * The event that occurred */ public void widgetSelected(SelectionEvent e) { if(e.widget == add) { addAction(); } else if(e.widget == remove) { removeAction(); } else if(e.widget == addAll) { addAllAction(); } else if(e.widget == removeAll) { removeAllAction(); } else if(e.widget == up) { upAction(); } else if(e.widget == down) { downAction(); } else if(e.widget == create) { createAction(); } /* Disable the bouton 'add' if the upperBound is reached */ if(this.upperBound != MANY) { if(allElements.size() >= this.upperBound) { add.setEnabled(false); } else { add.setEnabled(true); } } } /** * Sets the {@link ReferenceValueFactory} for this editor. The {@link ReferenceValueFactory} is used to create * new instances and edit existing ones. * * @param factory * The {@link ReferenceValueFactory} to be used by this editor */ public void setFactory(ReferenceValueFactory factory) { this.factory = factory; updateControls(); } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public int open() { allElements.clear(); allElements.addAll(getInitialElementSelections()); return super.open(); } /** * Handles the "Add" action */ protected void addAction() { Object[] elements = selector.getSelectedElements(); addElements(elements); } /** * Handles the "Up" action */ protected void upAction() { IStructuredSelection selection = (IStructuredSelection)selectedElementsViewer.getSelection(); //We need a list to move objects. LinkedHashSet can't do that java.util.List<Object> list = new LinkedList<Object>(allElements); for(Object o : selection.toArray()) { int oldIndex = list.indexOf(o); if(oldIndex > 0) { move(list, oldIndex, oldIndex - 1); } } allElements.clear(); allElements.addAll(list); IStructuredSelection selectionCopy = new StructuredSelection(selection.toArray()); selectedElementsViewer.setSelection(selectionCopy); selectedElementsViewer.refresh(); } /** * Handles the "Down" action */ protected void downAction() { IStructuredSelection selection = (IStructuredSelection)selectedElementsViewer.getSelection(); //We need a list to move objects. LinkedHashSet can't do that java.util.List<Object> list = new LinkedList<Object>(allElements); int maxIndex = list.size() - 1; Object[] selectionArray = selection.toArray(); for(int i = selectionArray.length - 1; i >= 0; i--) { Object o = selectionArray[i]; int oldIndex = list.indexOf(o); if(oldIndex < maxIndex) { move(list, oldIndex, oldIndex + 1); } } allElements.clear(); allElements.addAll(list); IStructuredSelection selectionCopy = new StructuredSelection(selection.toArray()); selectedElementsViewer.setSelection(selectionCopy); selectedElementsViewer.refresh(); } /** * Handles the "Create" action */ protected void createAction() { if(factory == null) { return; } Object newObject = factory.createObject(this.create); if(newObject == null) { return; } newObjects.add(newObject); selector.newObjectCreated(newObject); Object[] createdObjects = new Object[]{ newObject }; addElements(createdObjects); selector.setSelectedElements(allElements.toArray()); } /** * Moves an element from oldIndex to newIndex * * @param list * The list in which to move the object * @param oldIndex * @param newIndex */ private void move(java.util.List<Object> list, int oldIndex, int newIndex) { int size = list.size(); if(oldIndex < 0 || oldIndex >= size) { throw new IndexOutOfBoundsException("oldIndex: " + oldIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ } if(newIndex < 0 || newIndex >= size) { throw new IndexOutOfBoundsException("newIndex: " + newIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ } Object element = list.remove(oldIndex); list.add(newIndex, element); } /** * Handles the "Remove" action */ protected void removeAction() { IStructuredSelection selection = (IStructuredSelection)selectedElementsViewer.getSelection(); if(selection.isEmpty()) { return; } for(Object element : selection.toArray()) { allElements.remove(element); } selector.setSelectedElements(allElements.toArray()); selectedElementsViewer.setSelection(null); selectedElementsViewer.refresh(); } /** * Handles the "Delete" action */ protected void deleteAction() { //TODO } /** * Handles the "Remove all" action */ protected void removeAllAction() { allElements.clear(); selector.setSelectedElements(new Object[0]); selectedElementsViewer.setSelection(null); selectedElementsViewer.refresh(); } /** * Handles the "Add All" action */ protected void addAllAction() { Object[] elements = selector.getAllElements(); addElements(elements); } /** * Adds the specified elements to the currently selected elements (For * "Add" and "Add all" actions) * * @param elements * The elements to be added */ public void addElements(Object[] elements) { if(elements != null) { allElements.addAll(Arrays.asList(elements)); selectedElementsViewer.refresh(); } } @Override protected void okPressed() { if(factory != null) { java.util.List<Object> objectsToValidate = new LinkedList<Object>(); for(Object object : newObjects) { if(allElements.contains(object)) { objectsToValidate.add(object); } } factory.validateObjects(objectsToValidate); selector.clearTemporaryElements(); } setResult(new LinkedList<Object>(allElements)); super.okPressed(); } public void widgetDefaultSelected(SelectionEvent e) { if(e.widget == selectedElements) { removeAction(); } } /** * Indicates if the selected values should be unique (According to Object.equals()) * * @param unique */ public void setUnique(boolean unique) { this.unique = unique; updateControls(); } /** * Indicates if the selected elements should be ordered * * @param ordered */ public void setOrdered(boolean ordered) { this.ordered = ordered; updateControls(); } private void updateControls() { updateControl(up, ordered); updateControl(down, ordered); updateControl(create, this.factory != null && this.factory.canCreateObject()); } private void updateControl(Control control, boolean enabled) { if(control != null) { control.setEnabled(enabled); } } public void setSelector(IElementSelector selector) { this.selector = selector; } /** * Set the maximum number of values selected. * * @param upperBound */ public void setUpperBound(int upperBound) { this.upperBound = upperBound; } }