| /******************************************************************************* |
| * <copyright> |
| * |
| * Copyright (c) 2005, 2014 SAP AG. |
| * 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, implementation and documentation |
| * mwenz - Bug 347421 - Make setDoneChanges accessible to sub classes |
| * Benjamin Schmeling - mwenz - Bug 367483 - Support composite connections |
| * mgorning - Bug 376572 - Generic context buttons name changeable via getName() method |
| * mwenz - Bug 453553 - Provide an abort possibility for delete and remove features in case 'pre' methods fail |
| * |
| * </copyright> |
| * |
| *******************************************************************************/ |
| package org.eclipse.graphiti.ui.features; |
| |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.graphiti.features.IDeleteFeature; |
| import org.eclipse.graphiti.features.IFeatureProvider; |
| import org.eclipse.graphiti.features.IRemoveFeature; |
| import org.eclipse.graphiti.features.context.IContext; |
| import org.eclipse.graphiti.features.context.IDeleteContext; |
| import org.eclipse.graphiti.features.context.IMultiDeleteInfo; |
| import org.eclipse.graphiti.features.context.IRemoveContext; |
| import org.eclipse.graphiti.features.context.impl.RemoveContext; |
| import org.eclipse.graphiti.features.impl.AbstractFeature; |
| import org.eclipse.graphiti.mm.pictograms.CompositeConnection; |
| import org.eclipse.graphiti.mm.pictograms.Connection; |
| import org.eclipse.graphiti.mm.pictograms.PictogramElement; |
| import org.eclipse.graphiti.ui.internal.Messages; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.ui.PlatformUI; |
| |
| /** |
| * The Graphiti default implementation of the {@link IDeleteFeature}. It cares |
| * about removing the shape(s) representing the deleted domain object(s) from |
| * the diagram (by delegating to the {@link IRemoveFeature} provided by the |
| * {@link IFeatureProvider}. After that the EMF domain objects will be deleted |
| * using standard EMF functionality.<br> |
| * Before the deletion process starts the tool user will be asked if he really |
| * wants to delete; the popup appears only once for multi object deletions. |
| */ |
| public class DefaultDeleteFeature extends AbstractFeature implements IDeleteFeature { |
| |
| private static final String NAME = Messages.DefaultDeleteFeature_1_xfld; |
| private static final String DESC = org.eclipse.graphiti.internal.Messages.ContextEntryHelper_5_xfld; |
| |
| private boolean doneChanges = false; |
| |
| /** |
| * Creates a new {@link DefaultDeleteFeature}. |
| * |
| * @param fp |
| * the feature provider |
| */ |
| public DefaultDeleteFeature(IFeatureProvider fp) { |
| super(fp); |
| } |
| |
| /* |
| * @see |
| * org.eclipse.graphiti.func.IDelete#canDelete(org.eclipse.graphiti.features |
| * .context.IDeleteContext) |
| */ |
| public boolean canDelete(IDeleteContext context) { |
| PictogramElement pictogramElement = context.getPictogramElement(); |
| IRemoveContext rc = new RemoveContext(pictogramElement); |
| IRemoveFeature removeFeature = getFeatureProvider().getRemoveFeature(rc); |
| boolean ret = (removeFeature != null) && removeFeature.canRemove(rc); |
| return ret; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.graphiti.func.IDelete#delete(org.eclipse.graphiti.features |
| * .context.IDeleteContext) |
| */ |
| public void delete(IDeleteContext context) { |
| // we need this reset, since the an instance of this feature can be |
| // used multiple times, e.g. as a part of a pattern |
| setDoneChanges(false); |
| |
| IMultiDeleteInfo multiDeleteInfo = context.getMultiDeleteInfo(); |
| if (multiDeleteInfo != null && multiDeleteInfo.isDeleteCanceled()) { |
| return; |
| } |
| PictogramElement pe = context.getPictogramElement(); |
| Object[] businessObjectsForPictogramElement = getAllBusinessObjectsForPictogramElement(pe); |
| if (businessObjectsForPictogramElement != null && businessObjectsForPictogramElement.length > 0) { |
| if (multiDeleteInfo == null) { |
| if (!getUserDecision(context)) { |
| return; |
| } |
| } else { |
| if (multiDeleteInfo.isShowDialog()) { |
| boolean okPressed = getUserDecision(context); |
| if (okPressed) { |
| // don't show further dialogs |
| multiDeleteInfo.setShowDialog(false); |
| } else { |
| multiDeleteInfo.setDeleteCanceled(true); |
| return; |
| } |
| } |
| } |
| } |
| |
| preDelete(context); |
| if (isDeleteAbort()) { |
| throw new OperationCanceledException(); |
| } |
| if (pe instanceof CompositeConnection) { |
| // Find all domain objects for the children connections of the |
| // composite connection... |
| List<Object> compositeChildConnectionsBOs = collectCompositeConnectionsBOs((CompositeConnection) pe); |
| // ... and add them to the list of BOs to delete (no duplicates) |
| for (Object object : businessObjectsForPictogramElement) { |
| if (!compositeChildConnectionsBOs.contains(object)) { |
| compositeChildConnectionsBOs.add(object); |
| } |
| } |
| // Update BOs to delete |
| businessObjectsForPictogramElement = compositeChildConnectionsBOs |
| .toArray(new Object[compositeChildConnectionsBOs.size()]); |
| } |
| IRemoveContext rc = new RemoveContext(pe); |
| IFeatureProvider featureProvider = getFeatureProvider(); |
| IRemoveFeature removeFeature = featureProvider.getRemoveFeature(rc); |
| if (removeFeature != null) { |
| removeFeature.remove(rc); |
| // Bug 347421: Set hasDoneChanges flag only after first modification |
| setDoneChanges(true); |
| } |
| |
| deleteBusinessObjects(businessObjectsForPictogramElement); |
| |
| postDelete(context); |
| } |
| |
| private List<Object> collectCompositeConnectionsBOs(CompositeConnection composite) { |
| List<Object> result = new ArrayList<Object>(); |
| for (Connection childConnection : composite.getChildren()) { |
| Object[] allBusinessObjectsForChildConnection = getAllBusinessObjectsForPictogramElement(childConnection); |
| for (Object object : allBusinessObjectsForChildConnection) { |
| if (!result.contains(object)) { |
| result.add(object); |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Standard functionality to delete all given domain objects. |
| * |
| * @param businessObjects |
| * the domain objects to delete |
| */ |
| protected void deleteBusinessObjects(Object[] businessObjects) { |
| if (businessObjects != null) { |
| for (Object bo : businessObjects) { |
| deleteBusinessObject(bo); |
| } |
| } |
| } |
| |
| /** |
| * Standard functionality to delete one domain object. Will delegate for |
| * {@link EObject}s to {@link EcoreUtil#delete(EObject, boolean)}; non-EMF |
| * objects will be ignored. |
| * |
| * @param bo |
| * the domain object to delete |
| */ |
| protected void deleteBusinessObject(Object bo) { |
| if (bo instanceof EObject) { |
| EcoreUtil.delete((EObject) bo, true); |
| } |
| } |
| |
| /* |
| * @see |
| * org.eclipse.graphiti.func.IDelete#preDelete(org.eclipse.graphiti.features |
| * .context.IDeleteContext) |
| */ |
| public void preDelete(IDeleteContext context) { |
| } |
| |
| /** |
| * @since 0.12 |
| */ |
| @Override |
| public boolean isDeleteAbort() { |
| return false; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.graphiti.func.IDelete#postDelete(org.eclipse.graphiti.features |
| * .context.IDeleteContext) |
| */ |
| public void postDelete(IDeleteContext context) { |
| } |
| |
| /* |
| * @see |
| * org.eclipse.graphiti.features.IFeature#canExecute(org.eclipse.graphiti |
| * .features.context.IContext) |
| */ |
| public boolean canExecute(IContext context) { |
| boolean ret = false; |
| if (context instanceof IDeleteContext) { |
| ret = canDelete((IDeleteContext) context); |
| } |
| return ret; |
| } |
| |
| /* |
| * @see |
| * org.eclipse.graphiti.features.IFeature#execute(org.eclipse.graphiti.features |
| * .context.IContext) |
| */ |
| public void execute(IContext context) { |
| if (context instanceof IDeleteContext) { |
| delete((IDeleteContext) context); |
| } |
| } |
| |
| /** |
| * Hook to return the name of the delete operation e.g for display in the |
| * context menu. The standard implementation simply returns "Delete". |
| * |
| * @return The diplay name of the operation |
| */ |
| @Override |
| public String getName() { |
| return NAME; |
| } |
| |
| @Override |
| public String getDescription() { |
| return DESC; |
| } |
| |
| /* |
| * @see org.eclipse.graphiti.features.IFeature#hasDoneChanges() |
| */ |
| @Override |
| public boolean hasDoneChanges() { |
| return doneChanges; |
| } |
| |
| /** |
| * Can be called by users (in subclasses of {@link DefaultDeleteFeature}) to |
| * tell the framework that changes have been done that should reflect on the |
| * command stack. The method is called by the standard implementation in |
| * {@link #delete(IDeleteContext)} right after the first shape has been |
| * removed from the diagram. |
| * |
| * @param doneChanges |
| * <code>true</code>in case modifications have been made, |
| * <code>false</code> otherwise |
| * |
| * @since 0.9 |
| */ |
| protected void setDoneChanges(boolean doneChanges) { |
| this.doneChanges = doneChanges; |
| } |
| |
| /** |
| * Shows a dialog which asks the user to confirm the deletion of one or more |
| * elements. |
| * |
| * @param context |
| * delete context |
| * @return <code>true</code> to delete element(s); <code>false</code> to |
| * cancel delete |
| */ |
| protected boolean getUserDecision(IDeleteContext context) { |
| String msg; |
| IMultiDeleteInfo multiDeleteInfo = context.getMultiDeleteInfo(); |
| if (multiDeleteInfo != null) { |
| msg = MessageFormat.format(Messages.DefaultDeleteFeature_2_xmsg, multiDeleteInfo.getNumber()); |
| } else { |
| String deleteName = getDeleteName(context); |
| if (deleteName != null && deleteName.length() > 0) { |
| msg = MessageFormat.format(Messages.DefaultDeleteFeature_3_xmsg, deleteName); |
| } else { |
| msg = Messages.DefaultDeleteFeature_4_xmsg; |
| } |
| } |
| return MessageDialog.openQuestion(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), |
| Messages.DefaultDeleteFeature_5_xfld, msg); |
| } |
| |
| /** |
| * Returns the delete name which will be used for the delete dialog. E.g. |
| * "file test.java". The default implementation returns <code>null</code> to |
| * indicate that the popup text refers to "this object". |
| * |
| * @param context |
| * the delete context |
| * @return the delete name, or <code>null</code> to indicate no special name |
| * shall be used |
| * @since 0.8 |
| */ |
| protected String getDeleteName(IDeleteContext context) { |
| return null; |
| } |
| } |