blob: 3e1cbf98782fbde0f4e807723a04f8c90f3afc25 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 Red Hat, Inc.
* All rights reserved.
* This program is 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:
* Red Hat, Inc. - initial API and implementation
*
* @author Ivar Meikas
******************************************************************************/
package org.eclipse.bpmn2.modeler.core.features;
import java.io.IOException;
import org.eclipse.bpmn2.Association;
import org.eclipse.bpmn2.BaseElement;
import org.eclipse.bpmn2.EndEvent;
import org.eclipse.bpmn2.Lane;
import org.eclipse.bpmn2.MessageFlow;
import org.eclipse.bpmn2.Participant;
import org.eclipse.bpmn2.SequenceFlow;
import org.eclipse.bpmn2.StartEvent;
import org.eclipse.bpmn2.di.BPMNDiagram;
import org.eclipse.bpmn2.di.BPMNEdge;
import org.eclipse.bpmn2.di.BPMNShape;
import org.eclipse.bpmn2.di.BpmnDiFactory;
import org.eclipse.bpmn2.modeler.core.Activator;
import org.eclipse.bpmn2.modeler.core.ModelHandler;
import org.eclipse.bpmn2.modeler.core.ModelHandlerLocator;
import org.eclipse.bpmn2.modeler.core.di.DIImport;
import org.eclipse.bpmn2.modeler.core.di.DIUtils;
import org.eclipse.bpmn2.modeler.core.features.flow.AbstractCreateFlowFeature;
import org.eclipse.bpmn2.modeler.core.merrimac.dialogs.ObjectEditingDialog;
import org.eclipse.bpmn2.modeler.core.preferences.Bpmn2Preferences;
import org.eclipse.bpmn2.modeler.core.utils.AnchorUtil;
import org.eclipse.bpmn2.modeler.core.utils.BusinessObjectUtil;
import org.eclipse.bpmn2.modeler.core.utils.FeatureSupport;
import org.eclipse.bpmn2.modeler.core.utils.ModelUtil;
import org.eclipse.bpmn2.modeler.core.utils.Tuple;
import org.eclipse.dd.dc.DcFactory;
import org.eclipse.dd.dc.Point;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.graphiti.IExecutionInfo;
import org.eclipse.graphiti.datatypes.ILocation;
import org.eclipse.graphiti.datatypes.IRectangle;
import org.eclipse.graphiti.features.ICreateConnectionFeature;
import org.eclipse.graphiti.features.IFeatureAndContext;
import org.eclipse.graphiti.features.IFeatureProvider;
import org.eclipse.graphiti.features.IReconnectionFeature;
import org.eclipse.graphiti.features.context.IAddContext;
import org.eclipse.graphiti.features.context.IContext;
import org.eclipse.graphiti.features.context.ITargetContext;
import org.eclipse.graphiti.features.context.impl.AddContext;
import org.eclipse.graphiti.features.context.impl.CreateConnectionContext;
import org.eclipse.graphiti.features.context.impl.ReconnectionContext;
import org.eclipse.graphiti.features.impl.AbstractAddShapeFeature;
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.ContainerShape;
import org.eclipse.graphiti.mm.pictograms.FixPointAnchor;
import org.eclipse.graphiti.mm.pictograms.FreeFormConnection;
import org.eclipse.graphiti.mm.pictograms.Shape;
import org.eclipse.graphiti.services.Graphiti;
import org.eclipse.graphiti.services.ILayoutService;
import org.eclipse.graphiti.ui.editor.DiagramEditor;
public abstract class AbstractAddBPMNShapeFeature<T extends BaseElement>
extends AbstractAddShapeFeature
implements IBpmn2AddFeature<T> {
public AbstractAddBPMNShapeFeature(IFeatureProvider fp) {
super(fp);
}
protected BPMNShape findDIShape(BaseElement elem) {
try {
return (BPMNShape) ModelHandlerLocator.getModelHandler(getDiagram().eResource()).findDIElement(elem);
} catch (IOException e) {
Activator.logError(e);
}
return null;
}
protected BPMNShape createDIShape(Shape gShape, BaseElement elem, boolean applyDefaults) {
return createDIShape(gShape, elem, findDIShape(elem), applyDefaults);
}
protected BPMNShape createDIShape(Shape shape, BaseElement elem, BPMNShape bpmnShape, boolean applyDefaults) {
ILocation loc = Graphiti.getLayoutService().getLocationRelativeToDiagram(shape);
if (bpmnShape == null) {
int x = loc.getX();
int y = loc.getY();
int w = shape.getGraphicsAlgorithm().getWidth();
int h = shape.getGraphicsAlgorithm().getHeight();
bpmnShape = DIUtils.createDIShape(shape, elem, x, y, w, h, getFeatureProvider(), getDiagram());
}
else {
link(shape, new Object[] { elem, bpmnShape });
}
if (applyDefaults)
Bpmn2Preferences.getInstance(bpmnShape.eResource()).applyBPMNDIDefaults(bpmnShape, null);
return bpmnShape;
}
protected BPMNEdge createDIEdge(Connection connection, BaseElement conElement) {
try {
BPMNEdge edge = (BPMNEdge) ModelHandlerLocator.getModelHandler(getDiagram().eResource()).findDIElement(conElement);
return createDIEdge(connection, conElement, edge);
} catch (IOException e) {
Activator.logError(e);
}
return null;
}
// TODO: move this to DIUtils
protected BPMNEdge createDIEdge(Connection connection, BaseElement conElement, BPMNEdge edge) throws IOException {
ModelHandler modelHandler = ModelHandlerLocator.getModelHandler(getDiagram().eResource());
if (edge == null) {
EList<EObject> businessObjects = Graphiti.getLinkService().getLinkForPictogramElement(getDiagram())
.getBusinessObjects();
for (EObject eObject : businessObjects) {
if (eObject instanceof BPMNDiagram) {
BPMNDiagram bpmnDiagram = (BPMNDiagram) eObject;
edge = BpmnDiFactory.eINSTANCE.createBPMNEdge();
// edge.setId(EcoreUtil.generateUUID());
edge.setBpmnElement(conElement);
if (conElement instanceof Association) {
edge.setSourceElement(modelHandler.findDIElement(
((Association) conElement).getSourceRef()));
edge.setTargetElement(modelHandler.findDIElement(
((Association) conElement).getTargetRef()));
} else if (conElement instanceof MessageFlow) {
edge.setSourceElement(modelHandler.findDIElement(
(BaseElement) ((MessageFlow) conElement).getSourceRef()));
edge.setTargetElement(modelHandler.findDIElement(
(BaseElement) ((MessageFlow) conElement).getTargetRef()));
} else if (conElement instanceof SequenceFlow) {
edge.setSourceElement(modelHandler.findDIElement(
((SequenceFlow) conElement).getSourceRef()));
edge.setTargetElement(modelHandler.findDIElement(
((SequenceFlow) conElement).getTargetRef()));
}
ILocation sourceLoc = Graphiti.getPeService().getLocationRelativeToDiagram(connection.getStart());
ILocation targetLoc = Graphiti.getPeService().getLocationRelativeToDiagram(connection.getEnd());
Point point = DcFactory.eINSTANCE.createPoint();
point.setX(sourceLoc.getX());
point.setY(sourceLoc.getY());
edge.getWaypoint().add(point);
point = DcFactory.eINSTANCE.createPoint();
point.setX(targetLoc.getX());
point.setY(targetLoc.getY());
edge.getWaypoint().add(point);
DIUtils.addShape(edge, bpmnDiagram);
ModelUtil.setID(edge);
}
}
}
link(connection, new Object[] { conElement, edge });
return edge;
}
protected void prepareAddContext(IAddContext context, int width, int height) {
context.putProperty(ContextConstants.LABEL_CONTEXT, true);
context.putProperty(ContextConstants.WIDTH, width);
context.putProperty(ContextConstants.HEIGHT, height);
context.putProperty(ContextConstants.BUSINESS_OBJECT, getBusinessObject(context));
}
protected void adjustLocation(IAddContext context, int width, int height) {
if (context.getProperty(DIImport.IMPORT_PROPERTY) != null) {
return;
}
int x = context.getX();
int y = context.getY();
((AddContext)context).setWidth(width);
((AddContext)context).setHeight(height);
Connection connection = context.getTargetConnection();
if (connection!=null) {
// if the drop target is a connection line, adjust the context
// x or y so that the point lies on the line instead of just near it.
Anchor a0 = connection.getStart();
Anchor a1 = connection.getEnd();
double x0 = getRelativeLocationX(a0);
double y0 = getRelativeLocationY(a0);
double x1 = getRelativeLocationX(a1);
double y1 = getRelativeLocationY(a1);
if (x0 != x1) {
double m = (y1 - y0) / (x1 - x0);
double b = y0 - m * x0;
int y2 = (int)(m * x + b);
// because of roundoff errors when the slope is nearly vertical, the
// adjusted y may be way off; in this case, adjust the x coordinate instead
if (Math.abs(m) > 100) {
x = (int)((y - b) / m);
}
else {
y = y2;
}
// [x,y] is now the correct location on the connection line of the Activity's
// center point: calculate new location of the Activity figure.
}
else {
// vertical line: place drop x == line's x
x = (int)x0;
}
// TODO: do we want to keep the connection bendpoints?
if (connection instanceof FreeFormConnection) {
FreeFormConnection ffc = (FreeFormConnection)connection;
ffc.getBendpoints().clear();
DIUtils.updateDIEdge(connection);
}
}
y -= height/2;
x -= width / 2;
((AddContext)context).setY(y);
((AddContext)context).setX(x);
}
private double getRelativeLocationX(Anchor anchor) {
double result = 0.0;
if (anchor instanceof FixPointAnchor) {
FixPointAnchor fpa = (FixPointAnchor) anchor;
IRectangle gaBoundsForAnchor = Graphiti.getPeService().getGaBoundsForAnchor(anchor);
result = gaBoundsForAnchor.getX() + fpa.getLocation().getX();
AnchorContainer anchorContainer = anchor.getParent();
if (anchorContainer instanceof Shape) {
Shape shape = (Shape) anchorContainer;
result = result + shape.getGraphicsAlgorithm().getX();
}
}
return result;
}
private double getRelativeLocationY(Anchor anchor) {
double result = 0.0;
if (anchor instanceof FixPointAnchor) {
FixPointAnchor fpa = (FixPointAnchor) anchor;
IRectangle gaBoundsForAnchor = Graphiti.getPeService().getGaBoundsForAnchor(anchor);
result = gaBoundsForAnchor.getY() + fpa.getLocation().getY();
AnchorContainer anchorContainer = anchor.getParent();
if (anchorContainer instanceof Shape) {
Shape shape = (Shape) anchorContainer;
result = result + shape.getGraphicsAlgorithm().getY();
}
}
return result;
}
protected void splitConnection(IAddContext context, ContainerShape containerShape) {
if (context.getProperty(DIImport.IMPORT_PROPERTY) != null) {
return;
}
Object newObject = getBusinessObject(context);
Connection connection = context.getTargetConnection();
if (connection!=null) {
// determine how to split the line depending on where the new object was dropped:
// the longer segment will remain the original connection, and a new connection
// will be created for the shorter segment
ILayoutService layoutService = Graphiti.getLayoutService();
Anchor a0 = connection.getStart();
Anchor a1 = connection.getEnd();
double x0 = layoutService.getLocationRelativeToDiagram(a0).getX();
double y0 = layoutService.getLocationRelativeToDiagram(a0).getY();
double x1 = layoutService.getLocationRelativeToDiagram(a1).getX();
double y1 = layoutService.getLocationRelativeToDiagram(a1).getY();
double dx = x0 - context.getX();
double dy = y0 - context.getY();
double len0 = Math.sqrt(dx*dx + dy*dy);
dx = context.getX() - x1;
dy = context.getY() - y1;
double len1 = Math.sqrt(dx*dx + dy*dy);
AnchorContainer oldSourceContainer = connection.getStart().getParent();
AnchorContainer oldTargetContainer = connection.getEnd().getParent();
BaseElement baseElement = BusinessObjectUtil.getFirstElementOfType(connection, BaseElement.class);
ILocation targetLocation = layoutService.getLocationRelativeToDiagram(containerShape);
ReconnectionContext rc;
Tuple<FixPointAnchor, FixPointAnchor> anchors;
if (newObject instanceof StartEvent || len0 < len1) {
anchors = AnchorUtil.getSourceAndTargetBoundaryAnchors(containerShape, oldTargetContainer, connection);
rc = new ReconnectionContext(connection, connection.getStart(), anchors.getFirst(), targetLocation);
rc.setReconnectType(ReconnectionContext.RECONNECT_SOURCE);
rc.setTargetPictogramElement(containerShape);
}
else {
anchors = AnchorUtil.getSourceAndTargetBoundaryAnchors(oldSourceContainer, containerShape, connection);
rc = new ReconnectionContext(connection, connection.getEnd(), anchors.getSecond(), targetLocation);
rc.setReconnectType(ReconnectionContext.RECONNECT_TARGET);
rc.setTargetPictogramElement(containerShape);
}
IReconnectionFeature rf = getFeatureProvider().getReconnectionFeature(rc);
rf.reconnect(rc);
if (!(newObject instanceof EndEvent) && !(newObject instanceof StartEvent)) {
// connection = get create feature, create connection
CreateConnectionContext ccc = new CreateConnectionContext();
if (len0 < len1) {
ccc.setSourcePictogramElement(oldSourceContainer);
ccc.setTargetPictogramElement(containerShape);
anchors = AnchorUtil.getSourceAndTargetBoundaryAnchors(oldSourceContainer, containerShape, connection);
ccc.setSourceAnchor(anchors.getFirst());
ccc.setTargetAnchor(anchors.getSecond());
}
else {
ccc.setSourcePictogramElement(containerShape);
ccc.setTargetPictogramElement(oldTargetContainer);
anchors = AnchorUtil.getSourceAndTargetBoundaryAnchors(containerShape, oldTargetContainer, connection);
ccc.setSourceAnchor(anchors.getFirst());
ccc.setTargetAnchor(anchors.getSecond());
}
Connection newConnection = null;
for (ICreateConnectionFeature cf : getFeatureProvider().getCreateConnectionFeatures()) {
if (cf instanceof AbstractCreateFlowFeature) {
AbstractCreateFlowFeature acf = (AbstractCreateFlowFeature) cf;
if (acf.getBusinessObjectClass().isInstance(baseElement)) {
newConnection = acf.create(ccc);
DIUtils.updateDIEdge(newConnection);
break;
}
}
}
}
DIUtils.updateDIEdge(connection);
}
}
protected int getHeight(IAddContext context) {
return context.getHeight() > 0 ? context.getHeight() :
(isHorizontal(context) ? getHeight() : getWidth());
}
protected int getWidth(IAddContext context) {
return context.getWidth() > 0 ? context.getWidth() :
(isHorizontal(context) ? getWidth() : getHeight());
}
protected boolean isHorizontal(ITargetContext context) {
if (context.getProperty(DIImport.IMPORT_PROPERTY) == null) {
// not importing - set isHorizontal to be the same as parent Pool
if (FeatureSupport.isTargetParticipant(context)) {
Participant targetParticipant = FeatureSupport.getTargetParticipant(context);
BPMNShape participantShape = findDIShape(targetParticipant);
if (participantShape!=null)
return participantShape.isIsHorizontal();
}
else if (FeatureSupport.isTargetLane(context)) {
Lane targetLane = FeatureSupport.getTargetLane(context);
BPMNShape laneShape = findDIShape(targetLane);
if (laneShape!=null)
return laneShape.isIsHorizontal();
}
}
return Bpmn2Preferences.getInstance().isHorizontalDefault();
}
public abstract int getHeight();
public abstract int getWidth();
@Override
public T getBusinessObject(IAddContext context) {
Object businessObject = context.getProperty(ContextConstants.BUSINESS_OBJECT);
if (businessObject instanceof BaseElement)
return (T)businessObject;
return (T)context.getNewObject();
}
@Override
public void putBusinessObject(IAddContext context, T businessObject) {
context.putProperty(ContextConstants.BUSINESS_OBJECT, businessObject);
}
@Override
public void postExecute(IExecutionInfo executionInfo) {
}
}