/*****************************************************************************
* Copyright (c) 2010 CEA
*
*
* 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:
* Atos Origin - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.sequence.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.papyrus.uml.diagram.sequence.part.Messages;
import org.eclipse.papyrus.uml.diagram.sequence.providers.UMLElementTypes;
import org.eclipse.swt.SWT;
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.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.forms.FormDialog;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Signal;
import org.eclipse.uml2.uml.Type;
import org.eclipse.uml2.uml.UMLFactory;
/**
* This class provides a generic dialog to select or create an element.
*/
public class SelectOrCreateDialog extends FormDialog {
private EObject selectedElement = null;
private EClass selectedType = null;
private String selectedName = null;
private EObject selectedParent = null;
private Collection<EObject> existingElements;
private LinkedHashMap<EClass, List<EObject>> mapTypesPossibleParents;
private Button nothingRadio;
private Button selectionRadio;
private Text selectionText;
private Button selectionButton;
private Button creationRadio;
private Text creationNameText;
private ComboViewer typeComboViewer = null;
private Combo creationTypeCombo = null;
private Button creationParentButton;
private Text creationParentText;
private String title;
private ILabelProvider typeLabelProvider;
private ILabelProvider elementLabelProvider;
private final TransactionalEditingDomain transactionalEditingDomain;
private Button filterSignalButton;
private List<Type> types;
/**
*
* @param shell
* parent shell
* @param title
* The title of the dialog
* @param typeLabelProvider
* The label provider to generate type label
* @param elementLabelProvider
* The label provider to generate elements label
* @param transactionalEditingDomain
* The domain where to create the element if necessary.
* @param existingElements
* The list of existing elements that can be selected.
* @param mapTypesPossibleParents
* The map of possible types for the element
* and the possible parents where the element
* can be created.
* @param types
*/
public SelectOrCreateDialog(Shell shell, String title, ILabelProvider typeLabelProvider, ILabelProvider elementLabelProvider, TransactionalEditingDomain transactionalEditingDomain, Collection<EObject> existingElements, LinkedHashMap<EClass, List<EObject>> mapTypesPossibleParents, List<Type> types) {
super(shell);
this.typeLabelProvider = typeLabelProvider;
this.elementLabelProvider = elementLabelProvider;
this.existingElements = existingElements;
this.mapTypesPossibleParents = mapTypesPossibleParents;
this.title = title;
this.transactionalEditingDomain = transactionalEditingDomain;
this.types = types;
}
/**
* Adds buttons to this dialog's button bar.
*
* @param parent
* the button bar composite
*/
@Override
protected void createButtonsForButtonBar(Composite parent) {
super.createButtonsForButtonBar(parent);
refreshOkButton();
}
/**
* Create the form to :
*
* - ask the user to select or create an element.
*
* - decide whether the action is synchronous.
*
* @see org.eclipse.ui.forms.FormDialog#createFormContent(org.eclipse.ui.forms.IManagedForm)
*/
@Override
protected void createFormContent(IManagedForm pForm) {
ScrolledForm scrolledForm = pForm.getForm();
scrolledForm.setText(title);
FormToolkit toolkit = pForm.getToolkit();
Composite parent = scrolledForm.getBody();
parent.setLayout(new GridLayout());
// Create the selection section
createSelectionSection(scrolledForm.getBody(), toolkit);
// Create the creation section
createCreationSection(scrolledForm.getBody(), toolkit);
// Create the nothing section
createNothingSection(scrolledForm.getBody(), toolkit);
refreshSectionsEnable(selectionRadio);
hookListeners();
// name is set after listeners, since we count on listener to update it properly
setName(null);
scrolledForm.reflow(true);
}
private void createNothingSection(Composite pParent, FormToolkit pToolkit) {
// create the section
Section lSection = pToolkit.createSection(pParent, Section.EXPANDED | Section.TITLE_BAR);
lSection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
ScrolledForm lInsideScrolledForm = pToolkit.createScrolledForm(lSection);
lInsideScrolledForm.setExpandHorizontal(true);
lInsideScrolledForm.setExpandVertical(true);
Composite lBody = lInsideScrolledForm.getBody();
lBody.setLayout(new GridLayout(1, false));
// content of the section
nothingRadio = pToolkit.createButton(lBody, Messages.SelectOrCreateDialog_NothingLabel, SWT.RADIO);
nothingRadio.setSelection(true);
nothingRadio.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
lInsideScrolledForm.reflow(true);
lSection.setClient(lInsideScrolledForm);
}
/**
* Create the section to ask the user to select or create an element.
*
* @param pParent
* the section's parent widget
* @param pToolkit
* the form toolkit
*/
private void createSelectionSection(Composite pParent, FormToolkit pToolkit) {
// create the section
Section lSection = pToolkit.createSection(pParent, Section.EXPANDED | Section.TITLE_BAR);
lSection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
ScrolledForm lInsideScrolledForm = pToolkit.createScrolledForm(lSection);
lInsideScrolledForm.setExpandHorizontal(true);
lInsideScrolledForm.setExpandVertical(true);
Composite lBody = lInsideScrolledForm.getBody();
lBody.setLayout(new GridLayout(3, false));
// content of the section
selectionRadio = pToolkit.createButton(lBody, Messages.SelectOrCreateDialog_SelectLabel, SWT.RADIO);
selectionRadio.setSelection(true);
selectionRadio.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
selectionText = pToolkit.createText(lBody, "", SWT.BORDER | SWT.READ_ONLY); //$NON-NLS-1$
selectionText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
selectionButton = pToolkit.createButton(lBody, "...", SWT.FLAT); //$NON-NLS-1$
// find the first non null eobject and use its associated image
if(existingElements != null) {
Iterator<EObject> it = existingElements.iterator();
while(it.hasNext()) {
EObject next = it.next();
if(next != null) {
selectionButton.setImage(UMLElementTypes.getImage(next.eClass()));
break;
}
}
}
selectionButton.setLayoutData(new GridData(SWT.NONE));
filterSignalButton = pToolkit.createButton(lBody, "filter out all signals which are not receivable", SWT.CHECK);
lInsideScrolledForm.reflow(true);
lSection.setClient(lInsideScrolledForm);
}
/**
* Create the section to ask the user to select or create an element.
*
* @param pParent
* the section's parent widget
* @param pToolkit
* the form toolkit
*/
private void createCreationSection(Composite pParent, FormToolkit pToolkit) {
// create the section
Section lSection = pToolkit.createSection(pParent, Section.EXPANDED | Section.TITLE_BAR);
lSection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
ScrolledForm lInsideScrolledForm = pToolkit.createScrolledForm(lSection);
lInsideScrolledForm.setExpandHorizontal(true);
lInsideScrolledForm.setExpandVertical(true);
Composite lBody = lInsideScrolledForm.getBody();
lBody.setLayout(new GridLayout(3, false));
// content of the section
creationRadio = pToolkit.createButton(lBody, Messages.SelectOrCreateDialog_CreateLabel, SWT.RADIO);
creationRadio.setSelection(false);
creationRadio.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1));
EClass defaultType = null;
Set<EClass> possibleTypes = mapTypesPossibleParents.keySet();
// only create the type selection buttons if more than one type
// is possible
if(possibleTypes.size() == 1) {
defaultType = (EClass)possibleTypes.toArray()[0];
} else {
pToolkit.createLabel(lBody, Messages.SelectOrCreateDialog_TypeLabel, SWT.NONE);
creationTypeCombo = new Combo(lBody, SWT.DROP_DOWN | SWT.READ_ONLY);
typeComboViewer = new ComboViewer(creationTypeCombo);
pToolkit.adapt(creationTypeCombo);
creationTypeCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
typeComboViewer.setLabelProvider(typeLabelProvider);
typeComboViewer.add(possibleTypes.toArray());
if(possibleTypes.size() > 1) {
// initialize selection
defaultType = (EClass)possibleTypes.toArray()[0];
typeComboViewer.setSelection(new StructuredSelection(defaultType));
}
}
pToolkit.createLabel(lBody, Messages.SelectOrCreateDialog_NameLabel, SWT.NONE);
creationNameText = pToolkit.createText(lBody, "", SWT.BORDER); //$NON-NLS-1$
creationNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
pToolkit.createLabel(lBody, Messages.SelectOrCreateDialog_OwnerLabel + ":", SWT.NONE); //$NON-NLS-1$
creationParentText = pToolkit.createText(lBody, elementLabelProvider.getText(selectedParent), SWT.BORDER | SWT.READ_ONLY);
creationParentText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
creationParentButton = pToolkit.createButton(lBody, "...", SWT.FLAT); //$NON-NLS-1$
creationParentButton.setLayoutData(new GridData(SWT.NONE));
setType(defaultType);
lInsideScrolledForm.reflow(true);
lSection.setClient(lInsideScrolledForm);
}
/**
* Add listeners to widgets
*/
private void hookListeners() {
// listener to choose active section
SelectionListener radioListener = new SelectionAdapter() {
/**
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
refreshSectionsEnable(e.getSource());
refreshOkButton();
}
};
selectionRadio.addSelectionListener(radioListener);
creationRadio.addSelectionListener(radioListener);
nothingRadio.addSelectionListener(radioListener);
// listener to select existing element
SelectionListener selectBtnListener = new SelectionAdapter() {
/**
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
handleSelectElement();
}
};
selectionButton.addSelectionListener(selectBtnListener);
if(creationTypeCombo != null && typeComboViewer != null) {
// listener to select invocation type
ModifyListener lTypeListener = new ModifyListener() {
/**
* @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
*/
public void modifyText(ModifyEvent e) {
ISelection sel = typeComboViewer.getSelection();
if(sel instanceof StructuredSelection) {
Object type = ((StructuredSelection)sel).getFirstElement();
if(type instanceof EClass) {
setType((EClass)type);
} else {
setType(null);
}
// reset name
setName(null);
}
}
};
creationTypeCombo.addModifyListener(lTypeListener);
}
// listener to invocation element name
ModifyListener lNameListener = new ModifyListener() {
/**
* @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent)
*/
public void modifyText(ModifyEvent e) {
selectedName = creationNameText.getText();
refreshOkButton();
}
};
creationNameText.addModifyListener(lNameListener);
// listener to select new element parent
SelectionListener selectParentBtnListener = new SelectionAdapter() {
/**
* @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
*/
@Override
public void widgetSelected(SelectionEvent e) {
handleSelectParent();
}
};
creationParentButton.addSelectionListener(selectParentBtnListener);
}
/**
* Refresh the OK button activation
*/
private void refreshOkButton() {
Button okButton = getButton(IDialogConstants.OK_ID);
if(okButton != null && !okButton.isDisposed()) {
if(selectionRadio.getSelection()) {
okButton.setEnabled(selectedElement != null);
} else if(nothingRadio.getSelection()) {
okButton.setEnabled(true);
} else {
okButton.setEnabled(selectedType != null && selectedParent != null && selectedName != null && !"".equals(selectedName)); //$NON-NLS-1$
}
}
}
/**
* Add the created element to its selected parent
*/
protected void addElementInParent(EObject selectedParent, EObject createdElement) {
// Let the command find the relation on its own.
Command addCmd = AddCommand.create(transactionalEditingDomain, selectedParent, null, Collections.singleton(createdElement));
if(addCmd.canExecute()) {
addCmd.execute();
}
}
/**
* Set correctly the element, by creating it if needed.
* Then, notifies that the ok button of this dialog has been pressed.
*
* @see org.eclipse.jface.dialogs.Dialog#okPressed()
*
*/
@Override
protected void okPressed() {
// create element if needed
if(creationRadio.getSelection()) {
selectedElement = UMLFactory.eINSTANCE.create(selectedType);
if(selectedElement instanceof NamedElement) {
((NamedElement)selectedElement).setName(selectedName);
}
addElementInParent(selectedParent, selectedElement);
} else if(nothingRadio.getSelection()) {
selectedElement = null;
}
super.okPressed();
}
/**
* Open the dialog to choose the existing element to select
*
*/
private void handleSelectElement() {
ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), elementLabelProvider);
dialog.setMessage(Messages.SelectOrCreateDialog_SelectLabel);
dialog.setMultipleSelection(false);
dialog.setElements(filterElements(existingElements));
if(dialog.open() == Window.OK) {
setElementSelection((EObject)dialog.getFirstResult());
}
}
private Set<Signal> getAllSignals(List<Type> types){
Set<Signal> accept = new HashSet<Signal>();
Set<Classifier> collected = new HashSet<Classifier>();
for(Type t: types)
if(t instanceof Classifier){
Classifier c = (Classifier)t;
collectSignals(c, accept, collected);
}
return accept;
}
protected void collectSignals(Classifier c, Set<Signal> accept, Set<Classifier> collected) {
if(collected.contains(c))
return;
collected.add(c);
EList<Property> attrs = c.getAllAttributes();
for(Property p : attrs)
if(p.getType() instanceof Signal){
accept.add((Signal)p.getType());
}else if(p.getType() instanceof Classifier){
collectSignals((Classifier)p.getType(), accept, collected);
}
}
private Object[] filterElements(Collection<EObject> elements) {
if(!filterSignalButton.getSelection() || types == null || types.isEmpty())
return elements.toArray(new EObject[elements.size()]);
Set<Signal> accept = getAllSignals(types);
List<EObject> result = new ArrayList<EObject>();
for(EObject o:elements)
if(!(o instanceof Signal))
result.add(o);
else if( accept.contains(o))
result.add(o);
return result.toArray(new EObject[result.size()]);
}
/**
* Open the dialog to choose the parent of element to create
*
*/
private void handleSelectParent() {
ElementListSelectionDialog dialog = new ElementListSelectionDialog(getShell(), elementLabelProvider);
dialog.setMessage(Messages.SelectOrCreateDialog_OwnerLabel);
dialog.setMultipleSelection(false);
List<EObject> possibleParents = mapTypesPossibleParents.get(selectedType);
if(possibleParents != null) {
dialog.setElements(possibleParents.toArray());
}
if(dialog.open() == Window.OK) {
setParent((EObject)dialog.getFirstResult());
}
}
/**
* Define the object in which element will be created (if creation mode is chosen)
*
* @param parent
* the selected parent
*/
private void setParent(EObject parent) {
selectedParent = parent;
if(selectedParent instanceof NamedElement) {
creationParentText.setText(elementLabelProvider.getText(selectedParent));
creationParentButton.setImage(UMLElementTypes.getImage(parent.eClass()));
} else {
creationParentText.setText(""); //$NON-NLS-1$
}
refreshOkButton();
}
/**
* Define the name of the object that will be created by the action
*
* @param name
* the name of the element
*/
private void setName(String name) {
selectedName = name;
if(name != null) {
creationNameText.setText(name);
} else {
creationNameText.setText(""); //$NON-NLS-1$
}
refreshOkButton();
}
/**
* Define the type of the object that will be created by the action
*
* @param type
* the type of the element
*/
private void setType(EClass type) {
selectedType = type;
List<EObject> possibleParents = mapTypesPossibleParents.get(type);
if(possibleParents != null && possibleParents.size() > 0) {
setParent(possibleParents.get(0));
} else {
setParent(null);
}
refreshOkButton();
}
/**
* Define the object that will be returned by the action (if selection mode is chosen)
*
* @param element
* the selected element
*/
private void setElementSelection(EObject element) {
selectedElement = element;
if(selectedElement instanceof NamedElement) {
selectionText.setText(elementLabelProvider.getText(selectedElement));
} else {
selectionText.setText(""); //$NON-NLS-1$
}
refreshOkButton();
}
/**
* Refresh the enabled and disabled elements in various sections
*
*/
private void refreshSectionsEnable(Object radioObject) {
boolean nothingSelected = false;
boolean selectionSelected = false;
boolean creationSelected = false;
if(selectionRadio.equals(radioObject)) {
selectionSelected = true;
} else if(creationRadio.equals(radioObject)) {
creationSelected = true;
} else {
nothingSelected = true;
}
// Selection widgets
selectionRadio.setSelection(selectionSelected);
selectionText.setEnabled(selectionSelected);
selectionButton.setEnabled(selectionSelected);
// Creation widgets
creationRadio.setSelection(creationSelected);
if(creationTypeCombo != null) {
creationTypeCombo.setEnabled(creationSelected);
}
creationNameText.setEnabled(creationSelected);
creationParentText.setEnabled(creationSelected);
creationParentButton.setEnabled(creationSelected);
// Nothing widgets
nothingRadio.setSelection(nothingSelected);
}
/**
* Get the object that have been selected or created.
*
* @return the object to use.
*/
public EObject getSelected() {
return selectedElement;
}
}