/***************************************************************************** * Copyright (c) 2010 Atos Origin. * * * 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.activity.edit.dialogs; import java.util.Collections; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.edit.command.AddCommand; import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; import org.eclipse.emf.transaction.TransactionalEditingDomain; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.preference.IPreferenceStore; 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.papyrus.infra.core.sashwindows.di.Window; import org.eclipse.papyrus.infra.core.utils.EditorUtils; import org.eclipse.papyrus.infra.widgets.editors.TreeSelectorDialog; import org.eclipse.papyrus.uml.diagram.activity.part.Messages; import org.eclipse.papyrus.uml.diagram.activity.part.UMLDiagramEditorPlugin; import org.eclipse.papyrus.uml.diagram.activity.providers.UMLElementTypes; import org.eclipse.papyrus.uml.diagram.common.actions.LabelHelper; import org.eclipse.papyrus.uml.diagram.common.ui.helper.HelpComponentFactory; import org.eclipse.papyrus.uml.tools.providers.SemanticUMLContentProvider; import org.eclipse.papyrus.uml.tools.providers.UMLContainerContentProvider; import org.eclipse.papyrus.uml.tools.providers.UMLContentProvider; import org.eclipse.papyrus.uml.tools.providers.UMLLabelProvider; 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.graphics.Color; 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.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; 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.ImageHyperlink; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.uml2.uml.Activity; import org.eclipse.uml2.uml.InvocationAction; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.UMLFactory; import org.eclipse.uml2.uml.UMLPackage; /** * This class provides a dialog to initialize a CallAction at its creation. */ public abstract class CreateInvocationActionDialog extends FormDialog { private static final String MAP_FORMAT = "%s|%s"; private static final Pattern MAP_PATTERN = Pattern.compile("(.*?)\\|(.*)"); private Button creationRadio; private Button selectionRadio; private Text selectionText; private Button selectionButton; private Text creationNameText; private Text creationParentText; private Button creationParentButton; private EObject selectedInvoked = null; private EObject selectedParent = null; private Activity actionParent; private EClass selectedType = null; private String selectedName = null; private ComboViewer typeComboViewer = null; private Combo creationTypeCombo = null; private ILabelProvider labelProvider; /** * New Object about to be created */ private InvocationAction invocationAction; /** * Create a new dialog to initialize a CallAction. * * @param shell * parent shell * @param owner * the activity that owns the action */ public CreateInvocationActionDialog(Shell shell, Activity owner,InvocationAction newAction) { super(shell); actionParent = owner; selectedParent = getDefaultParent(owner); labelProvider = getCustomLabelProvider(); this.invocationAction = newAction; } /** * Invocation action about to be created * @return */ public InvocationAction getInvocationAction() { return invocationAction; } /** * Get the parent to propose as default choice for the element creation * * @param owner * the invocation action's owner * @return default parent to select */ private EObject getDefaultParent(Activity owner) { // try recovering last user choice from preferences IPreferenceStore prefStore = UMLDiagramEditorPlugin.getInstance().getPreferenceStore(); String ownerString = prefStore.getString(getCreationDefaultOwnerPreference()); if(ownerString != null && !"".equals(ownerString)) { Matcher match = MAP_PATTERN.matcher(ownerString); if(match.matches() && match.groupCount() >= 2) { String ressURI = match.group(1); String parentURI = match.group(2); if(owner.eResource().getURI().toString().equals(ressURI)) { EObject parent = owner.eResource().getEObject(parentURI); if(parent != null) { return parent; } } } } // recover default choice from the action's owner EObject parent = owner; while(!isPossibleInvokedParent(parent)) { parent = parent.eContainer(); } return parent; } /** * Create the form to : * * - ask the user to choose or create an invoked 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) { pForm.getForm().setText(getTitle()); ScrolledForm scrolledForm = pForm.getForm(); FormToolkit toolkit = pForm.getToolkit(); Composite parent = scrolledForm.getBody(); parent.setLayout(new GridLayout()); createInvocationCreationSection(scrolledForm.getBody(), toolkit); createInvocationSelectionSection(scrolledForm.getBody(), toolkit); createExtraSections(scrolledForm.getBody(), toolkit); refreshSectionsEnable(isSelectionDefault()); hookListeners(); // invoked name is set after listeners, since we count on listener to // update it properly setInvokedName(null); scrolledForm.reflow(true); } /** * Get whether the selection of an existing element is the default * * @return true if selection by default */ protected boolean isSelectionDefault() { IPreferenceStore prefStore = UMLDiagramEditorPlugin.getInstance().getPreferenceStore(); return prefStore.getBoolean(getSelectionIsDefaultPreference()); } /** * Get the id of the preference storing whether selection is the default * choice. * * @return preference id */ abstract protected String getSelectionIsDefaultPreference(); /** * Get the id of the preference storing the last selected owner. * * @return preference id */ abstract protected String getCreationDefaultOwnerPreference(); /** * Create the other needed sections. Subclasses can override this method. * * @param pParent * the section's parent widget * @param pToolkit * the form toolkit */ protected void createExtraSections(Composite pParent, FormToolkit pToolkit) { // do nothing } /** * 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 section to ask the user to choose or create an invoked * element. * * @param pParent * the section's parent widget * @param pToolkit * the form toolkit */ private void createInvocationSelectionSection(Composite pParent, FormToolkit pToolkit) { // create the section String lSectionTitle = getInvocationSelectionSectionTitle(); Section lSection = pToolkit.createSection(pParent, Section.EXPANDED | Section.TITLE_BAR); lSection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); if(lSectionTitle != null) { lSection.setText(lSectionTitle); } ScrolledForm lInsideScrolledForm = pToolkit.createScrolledForm(lSection); lInsideScrolledForm.setExpandHorizontal(true); lInsideScrolledForm.setExpandVertical(true); Composite lBody = lInsideScrolledForm.getBody(); GridLayout lLayout = new GridLayout(); lLayout.numColumns = 3; lBody.setLayout(lLayout); // content of the section selectionRadio = pToolkit.createButton(lBody, getSelectionLabel(), SWT.RADIO); // selectionRadio.setSelection(false); selectionRadio.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); pToolkit.createLabel(lBody, getInvokedObjectLabel(), SWT.NONE); selectionText = pToolkit.createText(lBody, "", SWT.BORDER | SWT.READ_ONLY); selectionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); selectionButton = pToolkit.createButton(lBody, "...", SWT.FLAT); Image image = UMLElementTypes.getImage(getInvocationFeature()); selectionButton.setImage(image); selectionButton.setLayoutData(new GridData(SWT.NONE)); lInsideScrolledForm.reflow(true); lSection.setClient(lInsideScrolledForm); } /** * Create the section to ask the user to choose or create an invoked * element. * * @param pParent * the section's parent widget * @param pToolkit * the form toolkit */ private void createInvocationCreationSection(Composite pParent, FormToolkit pToolkit) { // create the section String lSectionTitle = getInvocationCreationSectionTitle(); Section lSection = pToolkit.createSection(pParent, Section.EXPANDED | Section.TITLE_BAR); lSection.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); if(lSectionTitle != null) { lSection.setText(lSectionTitle); } ImageHyperlink componentHelp = HelpComponentFactory.createHelpComponent(lSection, pToolkit, getInvocationCreationSectionHelp(), true); lSection.setTextClient(componentHelp); ScrolledForm lInsideScrolledForm = pToolkit.createScrolledForm(lSection); lInsideScrolledForm.setExpandHorizontal(true); lInsideScrolledForm.setExpandVertical(true); Composite lBody = lInsideScrolledForm.getBody(); GridLayout lLayout = new GridLayout(); lLayout.numColumns = 3; lBody.setLayout(lLayout); // content of the section creationRadio = pToolkit.createButton(lBody, getCreationLabel(), SWT.RADIO); // creationRadio.setSelection(true); creationRadio.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1)); if(getPossibleInvokedTypes().length == 1) { selectedType = getPossibleInvokedTypes()[0]; } else { pToolkit.createLabel(lBody, getInvokedTypeLabel(), 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(labelProvider); typeComboViewer.add(getPossibleInvokedTypes()); if(getPossibleInvokedTypes().length > 1) { // initialize selection typeComboViewer.setSelection(new StructuredSelection(getPossibleInvokedTypes()[0])); selectedType = getPossibleInvokedTypes()[0]; } } pToolkit.createLabel(lBody, getInvokedNameLabel(), SWT.NONE); creationNameText = pToolkit.createText(lBody, "", SWT.BORDER); creationNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); creationNameText.setFocus(); pToolkit.createLabel(lBody, getInvokedParentLabel(), SWT.NONE); creationParentText = pToolkit.createText(lBody, labelProvider.getText(selectedParent), SWT.BORDER | SWT.READ_ONLY); creationParentText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); creationParentButton = pToolkit.createButton(lBody, "...", SWT.FLAT); Image image = getParentImage(); creationParentButton.setImage(image); creationParentButton.setLayoutData(new GridData(SWT.NONE)); lInsideScrolledForm.reflow(true); lSection.setClient(lInsideScrolledForm); } /** * Set correctly the invoked object, 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() { boolean isSelectionSelected = selectionRadio.getSelection(); // create element if needed if(!isSelectionSelected) { selectedInvoked = UMLFactory.eINSTANCE.create(selectedType); if(selectedInvoked instanceof NamedElement) { ((NamedElement)selectedInvoked).setName(selectedName); } addInvokedInParent(selectedParent, selectedInvoked); } // store user choices in preference IPreferenceStore prefStore = UMLDiagramEditorPlugin.getInstance().getPreferenceStore(); // store choice between selection and creation prefStore.setValue(getSelectionIsDefaultPreference(), isSelectionSelected); if(!isSelectionSelected) { // store the owner choice String ressUri = selectedParent.eResource().getURI().toString(); String parentURI = selectedParent.eResource().getURIFragment(selectedParent); String prefValue = String.format(MAP_FORMAT, ressUri, parentURI); prefStore.setValue(getCreationDefaultOwnerPreference(), prefValue); } super.okPressed(); } /** * Get the invoked object that have been selected or created. * * @return the invoked object to use. */ public EObject getSelectedInvoked() { return selectedInvoked; } /** * Add listeners to widgets */ private void hookListeners() { // listener to choose active section SelectionListener selectCreateListener = new SelectionAdapter() { /** * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ @Override public void widgetSelected(SelectionEvent e) { if(creationRadio.equals(e.getSource())) { refreshSectionsEnable(false); } else { refreshSectionsEnable(true); } refreshOkButton(); } }; selectionRadio.addSelectionListener(selectCreateListener); creationRadio.addSelectionListener(selectCreateListener); // 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) { handleChooseInvoked(); refreshOkButton(); } }; 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) { selectedType = (EClass)type; } else { selectedType = null; } // reset name setInvokedName(null); refreshOkButton(); } } }; 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) { setInvokedName(creationNameText.getText()); } }; 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) { handleChooseParent(); // reset name if not set if(selectedName == null) { setInvokedName(null); } refreshOkButton(); } }; creationParentButton.addSelectionListener(selectParentBtnListener); } /** * Set the name chosen for the invoked element * * @param text * the text string or null for auto-initialization */ private void setInvokedName(String text) { String name = text; if(text == null && selectedParent != null && selectedType != null) { name = LabelHelper.INSTANCE.findName(selectedParent, selectedType); // the name assignment will be performed by listener's call creationNameText.setText(name); } else if(name != null && !"".equals(name.trim())) { selectedName = name.trim(); Color black = creationNameText.getDisplay().getSystemColor(SWT.COLOR_BLACK); creationNameText.setForeground(black); refreshOkButton(); } else { selectedName = null; Color red = creationNameText.getDisplay().getSystemColor(SWT.COLOR_RED); creationNameText.setForeground(red); refreshOkButton(); } } /** * Open the dialog to choose the existing element to select * */ private void handleChooseInvoked() { TreeSelectorDialog dialog = new TreeSelectorDialog(Display.getDefault().getActiveShell()); dialog.setContentProvider(new UMLContentProvider(getInvocationAction(), getInvocationFeature())); dialog.setLabelProvider(new UMLLabelProvider()); // UMLMultiEClassifierTreeSelectorDialog dialog = new UMLMultiEClassifierTreeSelectorDialog(getShell(), actionParent, Collections.singleton(getInvocationFeature().getEType())); dialog.setMessage(Messages.UMLModelingAssistantProviderMessage); dialog.setTitle(Messages.UMLModelingAssistantProviderTitle); if(dialog.open() == org.eclipse.jface.window.Window.OK) { Object[] result = dialog.getResult(); if (result != null && result.length > 0 && result[0] instanceof EObject) setInvokedSelection((EObject)result[0]); } } /** * Define the object that will be invoked by the action (if selection mode * is chosen) * * @param invokedElement * the selected element */ protected void setInvokedSelection(EObject invokedElement) { selectedInvoked = invokedElement; if(selectedInvoked instanceof NamedElement) { selectionText.setText(labelProvider.getText(selectedInvoked)); } else { selectionText.setText(""); } } /** * Open the dialog to choose the parent of element to create * */ private void handleChooseParent() { /* * TODO BACKPORT */ // UMLMultiEClassifierTreeSelectorDialog dialog = new UMLMultiEClassifierTreeSelectorDialog(getShell(), actionParent, getPossibleInvokedParents(actionParent),true); // dialog.setMessage(Messages.UMLModelingAssistantProviderMessage); // dialog.setTitle(Messages.UMLModelingAssistantProviderTitle); // if(dialog.open() == Window.OK) { // setInvokedParent((EObject)dialog.getTheResult()); // } } /** * Define the object in which invoked object will be created (if creation * mode is chosen) * * @param invokedParent * the selected parent */ private void setInvokedParent(EObject invokedParent) { selectedParent = invokedParent; if(selectedParent instanceof NamedElement) { creationParentText.setText(labelProvider.getText(selectedParent)); } else { creationParentText.setText(""); } } /** * Refresh the enabled and disabled elements in various sections * * @param isSelectionSelected * true if we choose to select an existing element, false if we * choose to create an element */ protected void refreshSectionsEnable(boolean isSelectionSelected) { // handle radio button value if(isSelectionSelected) { creationRadio.setSelection(false); if(!selectionRadio.getSelection()) { selectionRadio.setSelection(true); } } else { selectionRadio.setSelection(false); if(!creationRadio.getSelection()) { creationRadio.setSelection(true); } } // handle disabled section selectionText.setEnabled(isSelectionSelected); selectionButton.setEnabled(isSelectionSelected); if(creationTypeCombo != null) { creationTypeCombo.setEnabled(!isSelectionSelected); } creationNameText.setEnabled(!isSelectionSelected); creationNameText.setFocus(); creationParentText.setEnabled(!isSelectionSelected); creationParentButton.setEnabled(!isSelectionSelected); } /** * Refresh the OK button activation */ private void refreshOkButton() { boolean isSelectionSelected = selectionRadio.getSelection(); if(getButton(IDialogConstants.OK_ID) != null && !getButton(IDialogConstants.OK_ID).isDisposed()) { if(isSelectionSelected) { getButton(IDialogConstants.OK_ID).setEnabled(selectedInvoked != null); } else { getButton(IDialogConstants.OK_ID).setEnabled(selectedParent != null && selectedType != null && selectedName != null); } } } /** * Add the created invoked object to its selected parent */ protected void addInvokedInParent(EObject selectedParent, EObject createdInvoked) { TransactionalEditingDomain editingdomain = EditorUtils.getTransactionalEditingDomain(); // Let the command find the relation on its own. Command addCmd = AddCommand.create(editingdomain, selectedParent, null, Collections.singleton(createdInvoked)); addCmd.execute(); } /** * Gets the custom label provider that parses label for EClass * * @return the custom label provider */ private ILabelProvider getCustomLabelProvider() { AdapterFactoryLabelProvider adapterFactory = new AdapterFactoryLabelProvider(UMLDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory()) { /** * Override label provider for EClass * * @see org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider#getText(java.lang.Object) */ @Override public String getText(Object object) { String text = super.getText(object); if(object instanceof EClass) { return text.substring(0, text.indexOf("[") - 1); } else { return text; } } }; return adapterFactory; } /** * Get the possible types for creating a new invoked element * * @return array of EClasses */ abstract protected EClass[] getPossibleInvokedTypes(); /** * Get the possible parents of the new invoked element * * @param actionParent * the parent of the action * @return collection of EObject possible owners */ abstract protected Set<? extends EClassifier> getPossibleInvokedParents(EObject actionParent); /** * Whether element can be parent of the new invoked element * * @param parent * the parent to test * @return true if parent is of an appropriate eclass */ abstract protected boolean isPossibleInvokedParent(EObject parent); /** * Get feature referencing the invoked object * * @return invocation feature */ abstract protected EReference getInvocationFeature(); /** * Get the image for the parent selection button * * @return image */ abstract protected Image getParentImage(); /** * Get the label to present the name of the invoked object * * @return label */ protected String getInvokedNameLabel() { return Messages.CreateCallActionDialog_NameLabel; } /** * Get the label to present the type of the invoked object * * @return label */ protected String getInvokedTypeLabel() { return Messages.CreateCallActionDialog_TypeLabel; } /** * Get the label to present parent of the invoked object * * @return label */ protected String getInvokedParentLabel() { return Messages.CreateCallActionDialog_ParentLabel; } /** * Get the label to present the invoked object * * @return label */ protected String getInvokedObjectLabel() { return getInvocationFeature().getEReferenceType().getName() + ":"; } /** * Get the title of the dialog * * @return dialog title */ abstract protected String getTitle(); /** * Get the title of the invocation creation section * * @return section title */ abstract protected String getInvocationCreationSectionTitle(); /** * Get the help description of the invocation creation section * * @return section title */ abstract protected String getInvocationCreationSectionHelp(); /** * Get the title of the invocation selection section * * @return section title */ abstract protected String getInvocationSelectionSectionTitle(); /** * Get the label to choose to select an existing element * * @return label */ abstract protected String getSelectionLabel(); /** * Get the label to choose to create a new element * * @return label */ abstract protected String getCreationLabel(); }