| /******************************************************************************* |
| * 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) { |
| } |
| } |