blob: 42b0438a705d6c313e37a7a4dd3e9427525fa9ad [file] [log] [blame]
/*******************************************************************************
* <copyright>
*
* Copyright (c) 2005, 2015 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 324859 Need Undo/Redo support for Non-EMF based domain objects
* mwenz - Bug 342874 CreateConnectionCommand overwriting "sourceLocation" with
* "targetLocation" on creation of a Connection
* mwenz - Bug 340627 - Features should be able to indicate cancellation
* mgorning - Bug 329517 - state call backs during creation of a connection
* Bug 336488 - DiagramEditor API
* fvelasco - Bug 396247 - ImageDescriptor changes
* pjpaulin - Bug 352120 - Now uses IDiagramContainerUI interface
* fvelasco - Bug 417577 - state call backs review
* Philip Alldredge - Bug 418676 - Undo is not disabled when canUndo is false for Palette features
* mwenz - Bug 424020 - Infinite loop when IConnectionCreateFeature returns true for the diagram
* mwenz - Bug 470455 - Difficulty in creating associations
*
* </copyright>
*
*******************************************************************************/
package org.eclipse.graphiti.ui.internal.command;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.graphiti.datatypes.ILocation;
import org.eclipse.graphiti.features.ICreateConnectionFeature;
import org.eclipse.graphiti.features.IFeature;
import org.eclipse.graphiti.features.IFeatureAndContext;
import org.eclipse.graphiti.features.context.IContext;
import org.eclipse.graphiti.features.context.impl.CreateConnectionContext;
import org.eclipse.graphiti.features.context.impl.CustomContext;
import org.eclipse.graphiti.features.custom.ICustomFeature;
import org.eclipse.graphiti.func.ICreateInfo;
import org.eclipse.graphiti.internal.DefaultFeatureAndContext;
import org.eclipse.graphiti.internal.command.CommandExec;
import org.eclipse.graphiti.internal.command.GenericFeatureCommandWithContext;
import org.eclipse.graphiti.internal.datatypes.impl.LocationImpl;
import org.eclipse.graphiti.mm.pictograms.Anchor;
import org.eclipse.graphiti.mm.pictograms.AnchorContainer;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.PictogramElement;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.ui.editor.IDiagramBehaviorUI;
import org.eclipse.graphiti.ui.internal.Messages;
import org.eclipse.graphiti.ui.internal.util.ui.PopupMenu;
import org.eclipse.graphiti.ui.platform.IConfigurationProvider;
import org.eclipse.graphiti.ui.services.GraphitiUi;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
/**
* @noinstantiate This class is not intended to be instantiated by clients.
* @noextend This class is not intended to be subclassed by clients.
*/
public class CreateConnectionCommand extends AbstractCommand {
/** The connection instance. */
// private Connection connection;
private List<IFeature> features;
// the location when connection is dropped on canvas
private Point location;
// the location where new connection starts
private ILocation sourceLocation;
/** Start endpoint for the connection. */
private final PictogramElement sourceObject;
/** Target endpoint for the connection. */
private PictogramElement targetObject;
private boolean createConnectionStarted = false;
private boolean createConnectionFinished = false;
/**
* Instantiate a command that can create a connection between two anchors.
*
* @param source
* the source endpoint (a non-null Shape instance)
* @param lineStyle
* the desired line style. See Connection#setLineStyle(int) for
* details
* @throws IllegalArgumentException
* if source is null
* @see Connection#setLineStyle(int)
*/
public CreateConnectionCommand(IConfigurationProvider configurationProvider, PictogramElement pe,
List<IFeature> features) {
super(configurationProvider);
setLabel(Messages.CreateConnectionCommand_0_xmsg);
this.features = features;
this.sourceObject = pe;
}
@Override
public boolean canExecute() {
// allow connections only from anchor to anchor
Anchor sourceAnchor = getAnchor(sourceObject);
Anchor targetAnchor = getAnchor(targetObject);
// if (sourceAnchor != null) {
CreateConnectionContext connectionContext = new CreateConnectionContext();
connectionContext.setSourceAnchor(sourceAnchor);
connectionContext.setTargetAnchor(targetAnchor);
connectionContext.setSourcePictogramElement(sourceObject);
connectionContext.setTargetPictogramElement(targetObject);
connectionContext.setTargetLocation(getCurrentLocation());
connectionContext.setSourceLocation(sourceLocation);
CustomContext customContext = new CustomContext();
customContext.setPictogramElements(new PictogramElement[] { sourceObject, targetObject });
customContext.setX(location.x);
customContext.setY(location.y);
IContext context = null;
for (IFeature feature : features) {
if (feature instanceof ICreateConnectionFeature)
context = connectionContext;
else
context = customContext;
GenericFeatureCommandWithContext ccc = new GenericFeatureCommandWithContext(feature, context);
if (ccc.canExecute())
return true;
}
return false;
}
@Override
public void execute() {
createConnectionFinished = true;
// create a new connection between source- and target-anchor
Anchor sourceAnchor = getAnchor(sourceObject);
Anchor targetAnchor = getAnchor(targetObject);
// if (sourceAnchor != null) {
CreateConnectionContext connectionContext = new CreateConnectionContext();
connectionContext.setSourceAnchor(sourceAnchor);
connectionContext.setTargetAnchor(targetAnchor);
connectionContext.setSourcePictogramElement(sourceObject);
connectionContext.setTargetPictogramElement(targetObject);
connectionContext.setTargetLocation(getCurrentLocation());
connectionContext.setSourceLocation(sourceLocation);
CustomContext customContext = new CustomContext();
customContext.setPictogramElements(new PictogramElement[] { sourceObject, targetObject });
IDiagramBehaviorUI diagramBehavior = (IDiagramBehaviorUI) getFeatureProvider().getDiagramTypeProvider()
.getDiagramBehavior();
Point newLocation = diagramBehavior.calculateRealMouseLocation(location);
customContext.setLocation(newLocation.x, newLocation.y);
List<GenericFeatureCommandWithContext> commands = new ArrayList<GenericFeatureCommandWithContext>();
IContext context = null;
for (IFeature feature : features) {
if (feature instanceof ICreateConnectionFeature)
context = connectionContext;
else
context = customContext;
GenericFeatureCommandWithContext ccc = new GenericFeatureCommandWithContext(feature, context);
if (ccc.canExecute())
commands.add(ccc);
}
if (commands.size() == 0)
return;
if (commands.size() == 1) {
try {
CommandExec.getSingleton().executeCommand(commands.get(0), getTransactionalEditingDomain());
} catch (Exception e) {
// Wrap in runtime exception (handled outside)
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
}
return;
}
PopupMenu popupMenu = new PopupMenu(commands, labelProvider);
boolean b = popupMenu.show(Display.getCurrent().getActiveShell());
if (b) {
GenericFeatureCommandWithContext result = (GenericFeatureCommandWithContext) popupMenu.getResult();
try {
commands.remove(result);
CommandExec.getSingleton().executeCommand(result, getTransactionalEditingDomain());
} catch (Exception e) {
// Wrap in runtime exception (handled outside)
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
}
}
for (GenericFeatureCommandWithContext command : commands) {
if (command.getFeature() instanceof ICreateConnectionFeature) {
((ICreateConnectionFeature) command.getFeature()).canceledAttaching(connectionContext);
}
}
}
public boolean canStartConnection() {
CreateConnectionContext connectionContext = createContext();
sourceLocation = connectionContext.getSourceLocation();
for (IFeature feature : features) {
if (feature instanceof ICreateConnectionFeature) {
ICreateConnectionFeature ccf = (ICreateConnectionFeature) feature;
if (ccf.canStartConnection(connectionContext)) {
return true;
}
} else
return true;
}
return false;
}
private CreateConnectionContext createContext() {
Anchor sourceAnchor = getAnchor(sourceObject);
Anchor targetAnchor = null;
CreateConnectionContext connectionContext = new CreateConnectionContext();
connectionContext.setSourceAnchor(sourceAnchor);
connectionContext.setTargetAnchor(targetAnchor);
connectionContext.setSourcePictogramElement(sourceObject);
connectionContext.setTargetPictogramElement(null);
connectionContext.setTargetLocation(null);
ILocation currentLocation = getCurrentLocation();
connectionContext.setSourceLocation(currentLocation);
return connectionContext;
}
@Override
public void redo() {
// connection.reconnect();
}
/**
* Set the target endpoint for the connection.
*
* @param target
* that target endpoint (a non-null Shape instance)
* @throws IllegalArgumentException
* if target is null
*/
public void setTarget(PictogramElement pe) {
targetObject = pe;
}
@Override
public boolean canUndo() {
final CreateConnectionContext context = createContext();
context.setTargetPictogramElement(targetObject);
context.setTargetAnchor(getAnchor(targetObject));
context.setTargetLocation(getCurrentLocation());
for (IFeature feature : features) {
if (feature instanceof ICreateConnectionFeature) {
if (feature.canUndo(context)) {
return true;
}
}
}
return false;
}
@Override
public void undo() {
// connection.disconnect();
}
private Anchor getAnchor(PictogramElement pe) {
Anchor ret = null;
if (pe instanceof Anchor) {
ret = (Anchor) pe;
} else if (pe instanceof AnchorContainer) {
ret = Graphiti.getPeService().getChopboxAnchor((AnchorContainer) pe);
}
return ret;
}
public PictogramElement getSourceObject() {
return sourceObject;
}
/**
* sets the location for the command when connection is dropped into nowhere
*
* @param location
*/
public void setLocation(Point location) {
this.location = location;
}
/**
* label provider for popup menu
*/
private static ILabelProvider labelProvider = new ILabelProvider() {
public void removeListener(ILabelProviderListener listener) {
}
public boolean isLabelProperty(Object element, String property) {
return false;
}
public void dispose() {
}
public void addListener(ILabelProviderListener listener) {
}
public String getText(Object element) {
GenericFeatureCommandWithContext command = (GenericFeatureCommandWithContext) element;
IFeature feature = command.getFeature();
if (feature instanceof ICreateInfo) // e.g. ICreateConnectionFeature
return ((ICreateInfo) feature).getCreateName();
if (feature instanceof ICustomFeature)
return ((ICustomFeature) feature).getName();
return null;
}
public Image getImage(Object element) {
GenericFeatureCommandWithContext command = (GenericFeatureCommandWithContext) element;
IFeature feature = command.getFeature();
String providerId = feature.getFeatureProvider().getDiagramTypeProvider().getProviderId();
if (feature instanceof ICreateInfo) // e.g. ICreateConnectionFeature
return GraphitiUi.getImageService().getImageForId(providerId,
((ICreateInfo) feature).getCreateImageId());
if (feature instanceof ICustomFeature)
return GraphitiUi.getImageService().getImageForId(providerId, ((ICustomFeature) feature).getImageId());
return null;
}
};
public IFeatureAndContext[] getFeaturesAndContexts() {
List<IFeatureAndContext> featureList = new ArrayList<IFeatureAndContext>(features.size());
CreateConnectionContext context = createContext();
for (Iterator<IFeature> iterator = features.iterator(); iterator.hasNext();) {
IFeature feature = iterator.next();
featureList.add(new DefaultFeatureAndContext(feature, context));
}
return featureList.toArray(new IFeatureAndContext[featureList.size()]);
}
private ILocation getCurrentLocation() {
if (location == null) {
return null;
}
IDiagramBehaviorUI diagramBehavior = (IDiagramBehaviorUI) getFeatureProvider().getDiagramTypeProvider()
.getDiagramBehavior();
Point realLocation = diagramBehavior.calculateRealMouseLocation(location);
ILocation currentLocation = new LocationImpl(realLocation.x, realLocation.y);
return currentLocation;
}
public void connectionStarted() {
createConnectionStarted = true;
// inform feature about attachment to source
CreateConnectionContext connectionContext = createContext();
connectionContext.setSourceLocation(sourceLocation);
for (IFeature feature : features) {
if (feature instanceof ICreateConnectionFeature) {
ICreateConnectionFeature ccf = (ICreateConnectionFeature) feature;
ccf.attachedToSource(connectionContext);
}
}
}
public void deactivate() {
if (createConnectionStarted && !createConnectionFinished) {
CreateConnectionContext connectionContext = createContext();
connectionContext.setSourceLocation(sourceLocation);
for (IFeature feature : features) {
if (feature instanceof ICreateConnectionFeature) {
ICreateConnectionFeature ccf = (ICreateConnectionFeature) feature;
ccf.canceledAttaching(connectionContext);
}
}
}
}
}