blob: 951f24b89dafe7ab66ee6918856eaad4a4417780 [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2005, 2019 SAP SE
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* SAP SE - initial API, implementation and documentation
* mgorning - Bug 391523 - Revise getSelectionInfo...() in IToolBehaviorProvider
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.graphiti.ui.internal.policy;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.draw2d.AbsoluteBendpoint;
import org.eclipse.draw2d.Bendpoint;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.handles.BendpointHandle;
import org.eclipse.gef.requests.BendpointRequest;
import org.eclipse.graphiti.features.IAddBendpointFeature;
import org.eclipse.graphiti.features.IMoveBendpointFeature;
import org.eclipse.graphiti.features.IRemoveBendpointFeature;
import org.eclipse.graphiti.features.context.impl.AddBendpointContext;
import org.eclipse.graphiti.features.context.impl.MoveBendpointContext;
import org.eclipse.graphiti.features.context.impl.RemoveBendpointContext;
import org.eclipse.graphiti.internal.command.GenericFeatureCommandWithContext;
import org.eclipse.graphiti.mm.algorithms.styles.Point;
import org.eclipse.graphiti.mm.pictograms.Connection;
import org.eclipse.graphiti.mm.pictograms.FreeFormConnection;
import org.eclipse.graphiti.tb.IConnectionSelectionInfo;
import org.eclipse.graphiti.tb.IToolBehaviorProvider;
import org.eclipse.graphiti.ui.internal.command.GefCommandWrapper;
import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal;
import org.eclipse.graphiti.ui.internal.figures.GFPolylineConnection;
import org.eclipse.graphiti.ui.internal.util.draw2d.GFBendpointHandle;
/**
* @noinstantiate This class is not intended to be instantiated by clients.
* @noextend This class is not intended to be subclassed by clients.
*/
public class ConnectionBendpointEditPolicy extends BendpointEditPolicyFixed {
/** The tolerance for the snap-to adjacent bendpoint feedback. */
private static final int SNAP_TO_TOLERANCE = 15;
private IConnectionSelectionInfo connectionSelectionInfo;
public ConnectionBendpointEditPolicy(IConfigurationProviderInternal configurationProvider) {
super(configurationProvider);
}
@Override
public void activate() {
super.activate();
Object model = getHost().getModel();
if (model instanceof Connection) {
IToolBehaviorProvider tbp = getConfigurationProvider().getDiagramTypeProvider()
.getCurrentToolBehaviorProvider();
connectionSelectionInfo = tbp.getSelectionInfoForConnection((Connection) model);
}
}
@Override
protected Command getCreateBendpointCommand(BendpointRequest request) {
Command ret = null;
Object model = request.getSource().getModel();
if (model instanceof FreeFormConnection) {
FreeFormConnection freeFormConnection = (FreeFormConnection) model;
int bendpointIndex = request.getIndex();
org.eclipse.draw2d.geometry.Point location = request.getLocation();
// Enable snap-to-bendpoints support for adjacent bendpoints.
snapToAdjacentBendpoints(request, location);
// Need to call translateToRelative to make this work as seen at
// http://www.koders.com/java/fid363E5D238D7C1D6660EBA03C7944827E823C6B42.aspx
getConnection().translateToRelative(location);
AddBendpointContext context = new AddBendpointContext(freeFormConnection, location.x, location.y,
bendpointIndex);
IAddBendpointFeature addBendpointFeature = getFeatureProvider().getAddBendpointFeature(context);
if (addBendpointFeature != null) {
ret = new GefCommandWrapper(new GenericFeatureCommandWithContext(addBendpointFeature, context),
getConfigurationProvider().getDiagramBehavior().getEditingDomain());
}
}
// workaround: no snapping to original position
saveOriginalConstraint();
return ret;
}
@Override
protected Command getDeleteBendpointCommand(BendpointRequest request) {
Command ret = null;
Object model = request.getSource().getModel();
if (model instanceof FreeFormConnection) {
FreeFormConnection freeFormConnection = (FreeFormConnection) model;
int bendpointIndex = request.getIndex();
Point bendpoint = freeFormConnection.getBendpoints().get(bendpointIndex);
RemoveBendpointContext context = new RemoveBendpointContext(freeFormConnection, bendpoint);
context.setBendpointIndex(bendpointIndex);
IRemoveBendpointFeature removeBendpointFeature = getFeatureProvider().getRemoveBendpointFeature(context);
if (removeBendpointFeature != null) {
ret = new GefCommandWrapper(new GenericFeatureCommandWithContext(removeBendpointFeature, context),
getConfigurationProvider().getDiagramBehavior().getEditingDomain());
}
}
return ret;
}
@Override
protected Command getMoveBendpointCommand(BendpointRequest request) {
Command ret = null;
ConnectionEditPart source = request.getSource();
Object model = source.getModel();
if (model instanceof FreeFormConnection) {
FreeFormConnection freeFormConnection = (FreeFormConnection) model;
int bendpointIndex = request.getIndex();
Point bendpoint = freeFormConnection.getBendpoints().get(bendpointIndex);
org.eclipse.draw2d.geometry.Point location = request.getLocation();
// Enable snap-to-bendpoints support for adjacent bendpoints.
snapToAdjacentBendpoints(request, location);
// Need to call translateToRelative to make this work as seen at
// http://www.koders.com/java/fid363E5D238D7C1D6660EBA03C7944827E823C6B42.aspx
getConnection().translateToRelative(location);
int requestX = location.x;
int requestY = location.y;
MoveBendpointContext context = new MoveBendpointContext(bendpoint);
context.setBendpointIndex(bendpointIndex);
context.setConnection(freeFormConnection);
context.setX(requestX);
context.setY(requestY);
IMoveBendpointFeature moveBendpointFeature = getFeatureProvider().getMoveBendpointFeature(context);
if (moveBendpointFeature != null) {
ret = new GefCommandWrapper(new GenericFeatureCommandWithContext(moveBendpointFeature, context),
getConfigurationProvider().getDiagramBehavior().getEditingDomain());
}
}
// workaround: no snapping to original position
saveOriginalConstraint();
return ret;
}
@Override
protected void showCreateBendpointFeedback(BendpointRequest request) {
// ADDED: keep points and bendpoints in sync
refreshConnectionBendpoints();
org.eclipse.draw2d.geometry.Point p = new org.eclipse.draw2d.geometry.Point(request.getLocation());
List<Bendpoint> constraint;
getConnection().translateToRelative(p);
Bendpoint bp = new AbsoluteBendpoint(p);
if (originalConstraint == null) {
saveOriginalConstraint();
constraint = getConnectionRoutingConstraint();
constraint.add(request.getIndex(), bp);
} else {
constraint = getConnectionRoutingConstraint();
}
// ADDED: enable snap-to adjacent bendpoints.
snapToAdjacentBendpoints(request, bp.getLocation());
// <sw 23022009> add: "request.getIndex() < constraint.size()" to fix
// CSN 0120061532 0001148754 2009
if (constraint.size() > 0 && request.getIndex() < constraint.size()) {
constraint.set(request.getIndex(), bp);
} else {
constraint.add(bp);
}
getConnection().setRoutingConstraint(constraint);
}
@Override
protected void showMoveBendpointFeedback(BendpointRequest request) {
// ADDED: keep points and bendpoints in sync
refreshConnectionBendpoints();
// ADDED: enable snap-to adjacent bendpoints.
snapToAdjacentBendpoints(request, request.getLocation());
super.showMoveBendpointFeedback(request);
}
@Override
protected List<BendpointHandle> createHandlesForAutomaticBendpoints() {
List<BendpointHandle> list = new ArrayList<BendpointHandle>();
ConnectionEditPart connEP = (ConnectionEditPart) getHost();
PointList points = getConnection().getPoints();
for (int i = 0; i < points.size() - 1; i++) {
// CHANGED: create GFBendpointCreationHandle instead of
// BendpointCreationHandle
list.add(new GFBendpointHandle(connEP, 0, i, getConfigurationProvider(), GFBendpointHandle.Type.CREATE,
connectionSelectionInfo));
}
return list;
}
@Override
protected List<BendpointHandle> createHandlesForUserBendpoints() {
List<BendpointHandle> list = new ArrayList<BendpointHandle>();
ConnectionEditPart connEP = (ConnectionEditPart) getHost();
PointList points = getConnection().getPoints();
List<Bendpoint> bendPoints = getConnectionRoutingConstraint();
int bendPointIndex = 0;
org.eclipse.draw2d.geometry.Point currBendPoint = null;
if (bendPoints == null)
bendPoints = NULL_CONSTRAINT;
else if (!bendPoints.isEmpty())
currBendPoint = bendPoints.get(0).getLocation();
for (int i = 0; i < points.size() - 1; i++) {
// Put a create handle on the middle of every segment
// CHANGED: create GFBendpointCreationHandle instead of
// BendpointCreationHandle
list.add(new GFBendpointHandle(connEP, bendPointIndex, i, getConfigurationProvider(),
GFBendpointHandle.Type.CREATE, connectionSelectionInfo));
// If the current user bendpoint matches a bend location, show a
// move handle
if (i < points.size() - 1 && bendPointIndex < bendPoints.size()
&& currBendPoint.equals(points.getPoint(i + 1))) {
// CHANGED: create GFBendpointMoveHandle instead of
// BendpointMoveHandle
list.add(new GFBendpointHandle(connEP, bendPointIndex, i + 1, getConfigurationProvider(),
GFBendpointHandle.Type.MOVE, connectionSelectionInfo));
// Go to the next user bendpoint
bendPointIndex++;
if (bendPointIndex < bendPoints.size())
currBendPoint = bendPoints.get(bendPointIndex).getLocation();
}
}
return list;
}
@Override
protected void setReferencePoints(BendpointRequest request) {
PointList points = getConnection().getPoints();
// CHANGED: bpIndex is the position of the bendpoint in points
int bpIndex = request.getIndex() + 1;
points.getPoint(ref1, bpIndex - 1);
getConnection().translateToAbsolute(ref1);
points.getPoint(ref2, bpIndex + 1);
getConnection().translateToAbsolute(ref2);
}
private void snapToAdjacentBendpoints(BendpointRequest request, org.eclipse.draw2d.geometry.Point p) {
IFigure f = request.getSource().getFigure();
if (!(f instanceof GFPolylineConnection))
return;
GFPolylineConnection plc = (GFPolylineConnection) f;
PointList points = plc.getPoints();
// determine candidate points for snap-to
// .. the predecessor bendpoint
org.eclipse.draw2d.geometry.Point predecessor = points.getPoint(request.getIndex()).getCopy();
getConnection().translateToAbsolute(predecessor);
int deltaPredecessor = getDistanceBetweenPoints(p, predecessor);
// .. the successor bendpoint
// the successorIndex can be out of bounds, if the last bend-point is
// moved onto the line,
// so that the bendpoint is deleted. In that case just use the next
// point, which is the connections end-point.
int successorIndex = (request.getIndex() + 2 >= points.size()) ? request.getIndex() + 1
: request.getIndex() + 2;
org.eclipse.draw2d.geometry.Point successor = points.getPoint(successorIndex).getCopy();
getConnection().translateToAbsolute(successor);
int deltaSuccessor = getDistanceBetweenPoints(p, successor);
// Make sure not to snap to the handles of the connection (the first or
// last point)
// If we did we would no longer be able to reconnect the connection.
if (request.getIndex() == 0) {
deltaPredecessor = Integer.MAX_VALUE; // never snap because of
// huge distance
}
if (request.getIndex() == ((List<?>) getConnection().getRoutingConstraint()).size() - 1) {
deltaSuccessor = Integer.MAX_VALUE; // never snap because of huge
// distance
}
// first try to snap to predecessor or successor (whichever is nearest)
if (deltaPredecessor < SNAP_TO_TOLERANCE && deltaPredecessor <= deltaSuccessor) {
p.setLocation(predecessor);
return;
} else if (deltaSuccessor < SNAP_TO_TOLERANCE) {
p.setLocation(successor);
return;
}
// .. the deltas to the lines through the predecessor and successor
// bendpoint
int xDeltaPredecessor = Math.abs(predecessor.x - p.x);
int yDeltaPredecessor = Math.abs(predecessor.y - p.y);
int xDeltaSuccessor = Math.abs(successor.x - p.x);
int yDeltaSuccessor = Math.abs(successor.y - p.y);
// then try to snap to the rectangular points (whichever is nearest)
// snap to y-line and x-line can be done both (snap to rectangular
// point)
if (xDeltaPredecessor < SNAP_TO_TOLERANCE && xDeltaPredecessor <= xDeltaSuccessor) {
p.setLocation(predecessor.x, p.y);
} else if (xDeltaSuccessor < SNAP_TO_TOLERANCE) {
p.setLocation(successor.x, p.y);
}
if (yDeltaPredecessor < SNAP_TO_TOLERANCE && yDeltaPredecessor <= yDeltaSuccessor) {
p.setLocation(p.x, predecessor.y);
} else if (yDeltaSuccessor < SNAP_TO_TOLERANCE) {
p.setLocation(p.x, successor.y);
}
}
private int getDistanceBetweenPoints(org.eclipse.draw2d.geometry.Point p1, org.eclipse.draw2d.geometry.Point p2) {
// Determine the deltas between the coordinates
int dx = Math.abs(p1.x - p2.x);
int dy = Math.abs(p1.y - p2.y);
// calculate the complete distance: simplified instead of sqrt(x*x +
// y*y)
int distance = dx + dy;
return distance;
}
/**
* This method refreshes the points of the connection with the bendpoints of
* the connection-router. The reason for this is, that sometimes the router
* already has different bendpoints set, but the points of the connection
* were not updated yet. If now the implementation expects, that those are
* already in sync, this can lead to exceptions. This is especially a
* problem, if the number of bendpoints does not match the number of
* bendpoints in the connections => ArrayIndexOutOfBounds.
*/
private void refreshConnectionBendpoints() {
getConnection().getConnectionRouter().route(getConnection());
}
@Override
protected void showSelection() {
IMoveBendpointFeature moveBendpointFeature = getFeatureProvider().getMoveBendpointFeature(
new MoveBendpointContext(null));
if (moveBendpointFeature != null) {
super.showSelection();
}
}
}