/*****************************************************************************
* Copyright (c) (c) 2002, 2007 IBM Corporation, 2014 CEA LIST and others.
*
* 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:
* IBM Corporation - initial API and implementation
*
*****************************************************************************/
package org.eclipse.gmf.tooling.runtime.linklf.policies;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.draw2d.AbsoluteBendpoint;
import org.eclipse.draw2d.AutomaticRouter;
import org.eclipse.draw2d.Bendpoint;
import org.eclipse.draw2d.BendpointLocator;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.ConnectionAnchor;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.PrecisionPoint;
import org.eclipse.draw2d.geometry.PrecisionRectangle;
import org.eclipse.gef.AccessibleHandleProvider;
import org.eclipse.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.SelectionHandlesEditPolicy;
import org.eclipse.gef.handles.BendpointCreationHandle;
import org.eclipse.gef.handles.BendpointMoveHandle;
import org.eclipse.gef.requests.BendpointRequest;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.ConnectionBendpointEditPolicy;
import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg;
import org.eclipse.gmf.runtime.draw2d.ui.geometry.PointListUtilities;
import org.eclipse.gmf.runtime.draw2d.ui.internal.figures.FeedbackConnection;
import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil;
import org.eclipse.gmf.runtime.gef.ui.internal.editpolicies.LineMode;
import org.eclipse.gmf.runtime.gef.ui.internal.handles.BendpointCreationInvisibleHandle;
import org.eclipse.gmf.runtime.gef.ui.internal.handles.BendpointMoveHandleEx;
import org.eclipse.gmf.runtime.gef.ui.internal.handles.LineSegMoveInvisibleHandle;
/**
* [GMFRT] make protected in
* {@link org.eclipse.gmf.runtime.gef.ui.internal.editpolicies.ConnectionBendpointEditPolicy}
* <p/>
* This class is a temporary workaround for bunch of private methods in the
* hierarchy of {@link ConnectionBendpointEditPolicy} we would like to override.
* <p/>
* We would like to avoid the mix of the copy-pasted now-protected code in this
* class with the actual modifications made in the
* {@link LinksLFConnectionBendpointEditPolicy}.
* <p/>
* The changes made in this class are:
* <ul>
* <li>{@link #showMoveOrthogonalBenspointFeedback()} is made protected</li>
* <li>{@link #showOutsideSourceFeedback()} is made protected</li>
* <li>{@link #showOutsideTargetFeedback()} is made protected</li>
* </ul>
* This class as a whole should be removed when the GMFT runtime will make the
* corresponding methods protected.
* <p/>
* This class is intentionally package local and should be removed after patch
* for bug 331779 is merged.
*
* @since 3.3
* @see also {@link ConnectionBendpointEditPolicy3} which is parallel
* replacement for a similarly named
* {@link org.eclipse.gmf.runtime.diagram.ui.editpolicies.ConnectionBendpointEditPolicy}
* from gmf.runtime.diagram.ui
* @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=331779
*
*/
@Deprecated
abstract class ConnectionBendpointEditPolicy2 extends
SelectionHandlesEditPolicy implements PropertyChangeListener {
private static List NULL_CONSTRAINT = new ArrayList();
private static final int STICKY_TOLERANCE_DP = 6;
private LineMode lineSegMode = LineMode.OBLIQUE;
static private class FeedbackState {
public List originalConstraint;
public Point ref1 = new Point();
public Point ref2 = new Point();
public boolean isDeleting = false;
public boolean isOutsideSource = false;
public boolean isOutsideTarget = false;
public boolean init = false;
}
private FeedbackState feedbackState = null;
private FeedbackState getFeedbackState() {
if (feedbackState == null) {
feedbackState = new FeedbackState();
}
return feedbackState;
}
private boolean useRealtimeFeedback() {
return false;
}
/**
* Constructor for EditPolicy
*
* @param lineSegMode
*/
public ConnectionBendpointEditPolicy2(LineMode lineSegMode) {
super();
this.lineSegMode = lineSegMode;
}
/**
* @return Returns the lineSegMode.
*/
public LineMode getLineSegMode() {
return lineSegMode;
}
/**
* Adds a PropertyChangeListener to the Connection so we can react to point
* changes in the connection.
*
* @see SelectionHandlesEditPolicy#activate()
*/
public void activate() {
super.activate();
getConnection().addPropertyChangeListener(Connection.PROPERTY_POINTS,
this);
}
/**
* @return <code>Connection</code> representing drag source feedback
*/
protected Connection createDragSourceFeedbackConnection() {
if (useRealtimeFeedback()) {
// Use the actual figure for feedback
return getConnection();
} else {
// Use a ghost rectangle for feedback
FeedbackConnection r = new FeedbackConnection(getConnection());
addFeedback(r);
return r;
}
}
/**
* Adds selection handles to the connection for the bendpoints. In this
* class, this method just decides if it is appropriate to add the handles,
* and then calls on the superclass to do the dirty work.s
*/
protected void addSelectionHandles() {
if (handles == null)
super.addSelectionHandles();
else {
int count = handles.size();
int points = getConnection().getPoints().size();
if (count != points * 2 - 3)
super.addSelectionHandles();
}
}
/**
* @return list of manual handles
*/
protected List createManualHandles() {
List list = new ArrayList();
ConnectionEditPart connEP = (ConnectionEditPart) getHost();
PointList points = getConnection().getPoints();
for (int i = 1; i < points.size() - 1; i++) {
addInvisibleCreationHandle(list, connEP, i - 1);
list.add(new BendpointMoveHandleEx(connEP, i, new BendpointLocator(
getConnection(), i)));
}
addInvisibleCreationHandle(list, connEP, points.size() - 2);
return list;
}
/**
* Method addInvisibleCreationHandle. This handle is necessary for the
* accessibility feature to allow keyboard navigation to the add bendpoint
* feature.
*
* @param list
* @param connEP
* @param i
*/
protected void addInvisibleCreationHandle(List list,
ConnectionEditPart connEP, int i) {
if (getLineSegMode() != LineMode.OBLIQUE) {
list.add(new LineSegMoveInvisibleHandle(connEP, i));
} else {
list.add(new BendpointCreationInvisibleHandle(connEP, i));
}
}
/**
* Creates selection handles for the bendpoints. Explicit (user-defined)
* bendpoints will have {@link BendpointMoveHandle}s on them with a single
* {@link BendpointCreationHandle} between 2 consecutive explicit
* bendpoints. If implicit bendpoints (such as those created by the
* {@link AutomaticRouter}) are used, one {@link BendpointCreationHandle} is
* placed in the middle of the Connection.
*/
protected List createSelectionHandles() {
return createManualHandles();
}
/**
* Removes this from the Connection's list of PropertyChangeListeners.
*
* @see SelectionHandlesEditPolicy#deactivate()
*/
public void deactivate() {
getConnection().removePropertyChangeListener(
Connection.PROPERTY_POINTS, this);
super.deactivate();
}
/**
* Erases bendpoint feedback. Since the original figure is used for
* feedback, we just restore the original constraint that was saved before
* feedback started to show.
*/
protected void eraseConnectionFeedback(BendpointRequest request,
boolean removeFeedbackFigure) {
restoreOriginalConstraint();
getFeedbackState().originalConstraint = null;
if (removeFeedbackFigure)
feedbackState = null;
}
/**
* Erases feedback, when appropriate.
*
* @see #eraseConnectionFeedback(BendpointRequest, boolean)
*/
public void eraseSourceFeedback(Request request) {
if (REQ_MOVE_BENDPOINT.equals(request.getType())
|| REQ_CREATE_BENDPOINT.equals(request.getType()))
eraseConnectionFeedback((BendpointRequest) request, true);
}
/**
* Returns the appropriate Command for the request type given. Handles
* creating, moving and deleting bendpoints. The actual creation of the
* command is taken care of by subclasses implementing the appropriate
* methods.
*
* @see #getCreateBendpointCommand(BendpointRequest)
* @see #getMoveBendpointCommand(BendpointRequest)
* @see #getDeleteBendpointCommand(BendpointRequest)
*/
public Command getCommand(Request request) {
if (REQ_MOVE_BENDPOINT.equals(request.getType())) {
if (getLineSegMode() != LineMode.OBLIQUE) {
return getMoveLineSegCommand((BendpointRequest) request);
} else {
if (getFeedbackState().isDeleting)
return getDeleteBendpointCommand((BendpointRequest) request);
return getMoveBendpointCommand((BendpointRequest) request);
}
}
if (REQ_CREATE_BENDPOINT.equals(request.getType()))
return getCreateBendpointCommand((BendpointRequest) request);
return null;
}
/**
* Returns the Connection associated with this EditPolicy.
*/
protected Connection getConnection() {
return (Connection) ((ConnectionEditPart) getHost()).getFigure();
}
/**
* @return Point cached value representing the first reference point.
*/
private Point getFirstReferencePoint() {
return getFeedbackState().ref1;
}
/**
* @return Point cached value representing the second reference point.
*/
private Point getSecondReferencePoint() {
return getFeedbackState().ref2;
}
/**
* Utility method to determine if point p passes through the line segment
* defined by p1 and p2.
*
* @param p1
* Point that is the first point in the line segment to test
* against.
* @param p2
* Point that is the second point in the line segment to test
* against.
* @param p
* Point that is tested to see if it falls in the line segment
* defined by p1 and p2.
* @return true if line segment contains Point p, false otherwise.
*/
private boolean lineContainsPoint(Point p1, Point p2, Point p) {
LineSeg line = new LineSeg(p1, p2);
return line.containsPoint(p, getStickyTolerance() / 3);
}
/**
* Adds selection handles to the Connection, if it is selected, when the
* points property changes. Since we only listen for changes in the points
* property, this method is only called when the points of the Connection
* have changed.
*/
public void propertyChange(PropertyChangeEvent evt) {
if (getHost().getSelected() != EditPart.SELECTED_NONE) {
// int count = handles.size();
// int points = getConnection().getPoints().size();
// if (count != points * 2 - 3)
addSelectionHandles();
}
}
/**
* Restores the original constraint that was saved before feedback began to
* show.
*/
protected void restoreOriginalConstraint() {
if (getFeedbackState().originalConstraint != null) {
Assert.isTrue(getFeedbackState().originalConstraint.size() >= 2);
getConnection().setRoutingConstraint(
getFeedbackState().originalConstraint);
}
}
/**
* Since the original figure is used for feedback, this method saves the
* original constraint, so that is can be restored when the feedback is
* erased.
*/
protected void saveOriginalConstraint() {
getFeedbackState().originalConstraint = (List) getConnection()
.getRoutingConstraint();
if (getFeedbackState().originalConstraint == null)
getFeedbackState().originalConstraint = NULL_CONSTRAINT;
if (getLineSegMode() != LineMode.OBLIQUE && !getFeedbackState().init) {
// Update the constraint based on the current figure
List newConstraint = new ArrayList(
getFeedbackState().originalConstraint.size());
PointList pts = PointListUtilities.copyPoints(getConnection()
.getPoints());
// OrthogonalRouterUtilities.resetEndPointsToCenter(getConnection(),
// pts);
for (int i = 0; i < pts.size(); i++) {
Bendpoint abp = new AbsoluteBendpoint(pts.getPoint(i));
newConstraint.add(abp);
}
Assert.isTrue(getFeedbackState().originalConstraint.size() >= 2);
getConnection().setRoutingConstraint(newConstraint);
// reset booleans
getFeedbackState().isOutsideSource = false;
getFeedbackState().isOutsideTarget = false;
} else {
// if the constraint and the connection figure points list don't
// match then reset the constraint
// based on the connection figure list. This could happen in certain
// cases - sepcifically when
// fan router detects a collision or when a self relation is routed.
int nConstraintSize = getFeedbackState().originalConstraint.size();
PointList pts = getConnection().getPoints();
int nPointSize = pts.size();
if (!getFeedbackState().init && nConstraintSize != nPointSize) {
while (getFeedbackState().originalConstraint.size() > 0) {
getFeedbackState().originalConstraint.remove(0);
}
for (int i = 0; i < pts.size(); i++) {
Bendpoint bpNew = new AbsoluteBendpoint(pts.getPoint(i));
getFeedbackState().originalConstraint.add(i, bpNew);
}
}
Assert.isTrue(getFeedbackState().originalConstraint.size() >= 2);
getConnection().setRoutingConstraint(
new ArrayList(getFeedbackState().originalConstraint));
}
getFeedbackState().init = true;
}
/**
* Method setReferencePoints. This method will calculate the two end
* reference points for a point that is being moved or created. The
* reference points are used to determine if the request point can be
* deleted or not (for straight line tolerance).
*
* @param request
* BendpointRequest object containing index information.
*/
private void setReferencePoints(BendpointRequest request) {
if (getFeedbackState().originalConstraint == null) {
saveOriginalConstraint();
}
List constraint = (List) getConnection().getRoutingConstraint();
Bendpoint bp = (Bendpoint) constraint.get(Math.max(0,
request.getIndex() - 1));
getFeedbackState().ref1 = bp.getLocation();
bp = (Bendpoint) constraint.get(Math.min(request.getIndex() + 1,
constraint.size() - 1));
getFeedbackState().ref2 = bp.getLocation();
}
private void setNewFeedbackConstraint(List constraint) {
Assert.isTrue(constraint.size() >= 2);
getConnection().setRoutingConstraint(constraint);
}
/**
* Shows feedback when a bendpoint is being created. The original figure is
* used for feedback and the original constraint is saved, so that it can be
* restored when feedback is erased.
*/
protected void showCreateBendpointFeedback(BendpointRequest request) {
Point p = new Point(request.getLocation());
List constraint;
getConnection().translateToRelative(p);
Bendpoint bp = new AbsoluteBendpoint(p);
if (getFeedbackState().originalConstraint == null) {
saveOriginalConstraint();
constraint = (List) getConnection().getRoutingConstraint();
constraint.add(request.getIndex() + 1, bp);
} else {
constraint = (List) getConnection().getRoutingConstraint();
}
stickyStraightLineFeedback(constraint, request.getIndex() + 1, bp);
setNewFeedbackConstraint(constraint);
}
/**
* Shows feedback when a bendpoint is being deleted. This method is only
* called once when the bendpoint is first deleted, not every mouse move.
* The original figure is used for feedback and the original constraint is
* saved, so that it can be restored when feedback is erased.
*/
protected void showDeleteBendpointFeedback(BendpointRequest request) {
if (getFeedbackState().originalConstraint == null) {
saveOriginalConstraint();
List constraint = (List) getConnection().getRoutingConstraint();
constraint.remove(request.getIndex());
setNewFeedbackConstraint(constraint);
}
}
/**
* Shows feedback when a bendpoint is being moved. Also checks to see if the
* bendpoint should be deleted and then calls
* {@link #showDeleteBendpointFeedback(BendpointRequest)} if needed. The
* original figure is used for feedback and the original constraint is
* saved, so that it can be restored when feedback is erased.
*/
protected void showMoveBendpointFeedback(BendpointRequest request) {
Point p = new Point(request.getLocation());
if (!getFeedbackState().isDeleting) {
setReferencePoints(request);
}
getConnection().translateToRelative(p);
Bendpoint bp = new AbsoluteBendpoint(p);
if (getFeedbackState().originalConstraint == null) {
saveOriginalConstraint();
}
if (lineContainsPoint(getFirstReferencePoint(),
getSecondReferencePoint(), p)) {
if (!getFeedbackState().isDeleting) {
getFeedbackState().isDeleting = true;
eraseConnectionFeedback(request, false);
showDeleteBendpointFeedback(request);
}
return;
}
if (getFeedbackState().isDeleting) {
getFeedbackState().isDeleting = false;
eraseConnectionFeedback(request, false);
}
List constraint = (List) getConnection().getRoutingConstraint();
stickyStraightLineFeedback(constraint, request.getIndex(), bp);
setNewFeedbackConstraint(constraint);
}
/**
* This method will set the constraint with the given bendpoint, with the
* additional behavior of "sticking" the point around a tolerance to a
* straight line. If it's within a tolerance of the previous point, stick it
* to the horizontal or vertical coordinates that make it straight.
*/
protected void stickyStraightLineFeedback(List constraint, int nIndex,
Bendpoint bp) {
Point ptLoc = new Point(bp.getLocation());
int sticky_tolerance = getStickyTolerance();
if (nIndex > 0) {
Point ptPrev;
if ((nIndex - 1) == 0) {
ptPrev = getConnection().getSourceAnchor().getReferencePoint();
getConnection().translateToRelative(ptPrev);
} else
ptPrev = ((Bendpoint) constraint.get(nIndex - 1)).getLocation();
if (Math.abs(ptPrev.x - ptLoc.x) < sticky_tolerance)
ptLoc.x = ptPrev.x;
if (Math.abs(ptPrev.y - ptLoc.y) < sticky_tolerance)
ptLoc.y = ptPrev.y;
}
if (nIndex < constraint.size() - 1) {
Point ptNext;
if ((nIndex + 1) == (constraint.size() - 1)) {
ptNext = getConnection().getTargetAnchor().getReferencePoint();
getConnection().translateToRelative(ptNext);
} else
ptNext = ((Bendpoint) constraint.get(nIndex + 1)).getLocation();
if (Math.abs(ptNext.x - ptLoc.x) < sticky_tolerance)
ptLoc.x = ptNext.x;
if (Math.abs(ptNext.y - ptLoc.y) < sticky_tolerance)
ptLoc.y = ptNext.y;
}
if (!ptLoc.equals(bp.getLocation())) {
Bendpoint bpNew = new AbsoluteBendpoint(ptLoc);
constraint.set(nIndex, bpNew);
} else {
constraint.set(nIndex, bp);
}
}
private int getStickyTolerance() {
int sticky_tolerance = MapModeUtil.getMapMode(getConnection()).DPtoLP(
STICKY_TOLERANCE_DP);
return sticky_tolerance;
}
/**
* Shows feedback, when appropriate. Calls a different method depending on
* the request type.
*
* @see #showCreateBendpointFeedback(BendpointRequest)
* @see #showMoveBendpointFeedback(BendpointRequest)
*/
public void showSourceFeedback(Request request) {
if (getLineSegMode() != LineMode.OBLIQUE) {
if (REQ_CREATE_BENDPOINT.equals(request.getType())) {
showMoveLineSegFeedback((BendpointRequest) request);
} else if (REQ_MOVE_BENDPOINT.equals(request.getType())) {
showMoveOrthogonalBenspointFeedback((BendpointRequest) request);
}
} else {
if (REQ_MOVE_BENDPOINT.equals(request.getType()))
showMoveBendpointFeedback((BendpointRequest) request);
else if (REQ_CREATE_BENDPOINT.equals(request.getType()))
showCreateBendpointFeedback((BendpointRequest) request);
}
super.showSourceFeedback(request);
}
/**
* Method getBendpointsChangedCommand. This method will return a
* SetBendpointsCommand with the points retrieved from the user feedback in
* the figure.
*
* @param request
* BendpointRequest from the user gesture for moving / creating a
* bendpoint
* @return Command SetBendpointsCommand that contains the point changes for
* the connection.
*/
abstract protected Command getBendpointsChangedCommand(
BendpointRequest request);
protected Command getCreateBendpointCommand(BendpointRequest request) {
return getBendpointsChangedCommand(request);
}
protected Command getMoveBendpointCommand(BendpointRequest request) {
return getBendpointsChangedCommand(request);
}
protected Command getDeleteBendpointCommand(BendpointRequest request) {
return getBendpointsChangedCommand(request);
}
protected final LineSeg getLineSeg(List bendPoints, int nIndex) {
Point pt1 = new Point(
((Bendpoint) bendPoints.get(nIndex - 1)).getLocation());
Point pt2 = new Point(
((Bendpoint) bendPoints.get(nIndex)).getLocation());
return new LineSeg(pt1, pt2);
}
/**
* @param bendPoints
* @param nIndex
* @param newLine
*/
protected void setLineSeg(List bendPoints, int nIndex, LineSeg newLine) {
Bendpoint bp1 = new AbsoluteBendpoint(newLine.getOrigin());
Bendpoint bp2 = new AbsoluteBendpoint(newLine.getTerminus());
bendPoints.set(nIndex - 1, bp1);
bendPoints.set(nIndex, bp2);
}
/**
* @param request
* @return move line segment command
*/
protected Command getMoveLineSegCommand(BendpointRequest request) {
return getBendpointsChangedCommand(request);
}
/**
* Method lineOutsideSource. Utility method to determine if the constraint
* needs to be adjusted becauase the line is outside of the source bounds.
*
* @param line
* LineSeg defining the new line moved by the user gesture
* @return boolean true if origin of line lies outside the starting source
* element, false otherwise.
*/
protected boolean lineOutsideSource(LineSeg line) {
// check if end points are outside of bounds and if so - add a new point
PrecisionRectangle startRect = new PrecisionRectangle(
FigureUtilities.getAnchorableFigureBounds(getConnection()
.getSourceAnchor().getOwner()));
getConnection().getSourceAnchor().getOwner()
.translateToAbsolute(startRect);
if (getLineSegMode().equals(LineMode.ORTHOGONAL_CONSTRAINED)) {
if (line.isHorizontal()) {
startRect.shrink(0, 2);
} else {
startRect.shrink(2, 0);
}
}
getConnection().translateToRelative(startRect);
/*
* Rectangle needs to be expanded by the "odd" number below because the
* number after translations could be N.999999999...
*/
if (!startRect.expand(0.000001, 0.000001).contains(
new PrecisionPoint(line.getOrigin()))) {
return true;
}
return false;
}
/**
* Method lineOutsideTarget. Utility method to determine if the constraint
* needs to be adjusted because the line is outside of the target bounds.
*
* @param line
* LineSeg defining the new line moved by the user gesture.
* @return boolean true if terminus of line lies outside the target element,
* false otherwise.
*/
protected boolean lineOutsideTarget(LineSeg line) {
// check if end points are outside of bounds and if so - add a new point
PrecisionRectangle endRect = new PrecisionRectangle(
FigureUtilities.getAnchorableFigureBounds(getConnection()
.getTargetAnchor().getOwner()));
getConnection().getTargetAnchor().getOwner()
.translateToAbsolute(endRect);
if (getLineSegMode().equals(LineMode.ORTHOGONAL_CONSTRAINED)) {
if (line.isHorizontal()) {
endRect.shrink(0, 2);
} else {
endRect.shrink(2, 0);
}
}
/*
* Rectangle needs to be expanded by the "odd" number below because the
* number after translations could be N.999999999...
*/
getConnection().translateToRelative(endRect);
if (!endRect.expand(0.00001, 0.00001).contains(
new PrecisionPoint(line.getTerminus()))) {
return true;
}
return false;
}
/**
* Method removeOutsideSourceFeedback. Removes a bendpoint from the
* beginning of the constraint.
*
* @param constraint
* List of bendpoints that the source point will be added too.
*/
protected void removeOutsideSourceFeedback(List constraint) {
constraint.remove(0);
}
/**
* Method removeOutsideTargetFeedback. Removes a bendpoint from the end of
* the constraint.
*
* @param constraint
* List of bendpoints that the target point will be added too.
*/
protected void removeOutsideTargetFeedback(List constraint) {
constraint.remove(constraint.size() - 1);
}
/**
* Draws feedback for moving a bend point of a rectilinear connection
*
* @param request
* Benndpoint request
*/
protected void showMoveOrthogonalBenspointFeedback(BendpointRequest request) {
if (getFeedbackState().originalConstraint == null) {
saveOriginalConstraint();
}
Point ptLoc = new Point(request.getLocation());
List constraint = (List) getConnection().getRoutingConstraint();
getConnection().translateToRelative(ptLoc);
int index = getFeedbackState().isOutsideSource ? request.getIndex() + 1
: request.getIndex();
Point previous = ((Bendpoint) constraint.get(index - 1)).getLocation();
Point moving = ((Bendpoint) constraint.get(index)).getLocation();
Point next = ((Bendpoint) constraint.get(index + 1)).getLocation();
LineSeg originalFirst = new LineSeg(previous.getCopy(),
moving.getCopy());
LineSeg originalSecond = new LineSeg(moving.getCopy(), next.getCopy());
Dimension diff = ptLoc.getDifference(moving);
if (originalFirst.isHorizontal()) {
previous.y += diff.height;
next.x += diff.width;
} else {
previous.x += diff.width;
next.y += diff.height;
}
LineSeg movedFirst = new LineSeg(previous, ptLoc.getCopy());
LineSeg movedSecond = new LineSeg(ptLoc.getCopy(), next);
index = adjustOutsideBoundsLineFeedback(movedFirst, index - 1,
constraint, originalFirst);
constraint.set(index, new AbsoluteBendpoint(movedFirst.getOrigin()));
constraint.set(index + 1,
new AbsoluteBendpoint(movedFirst.getTerminus()));
index = adjustOutsideBoundsLineFeedback(movedSecond, index + 1,
constraint, originalSecond);
constraint.set(index + 1,
new AbsoluteBendpoint(movedSecond.getTerminus()));
getConnection().setRoutingConstraint(constraint);
}
/**
* Shows feedback when a line segment is being moved. Also checks to see if
* the bendpoint should be deleted and then calls
* {@link #showDeleteBendpointFeedback(BendpointRequest)} if needed. The
* original figure is used for feedback and the original constraint is
* saved, so that it can be restored when feedback is erased.
*/
protected void showMoveLineSegFeedback(BendpointRequest request) {
if (getFeedbackState().originalConstraint == null) {
saveOriginalConstraint();
}
Point ptLoc = new Point(request.getLocation());
List constraint = (List) getConnection().getRoutingConstraint();
getConnection().translateToRelative(ptLoc);
// adjust request index to account for source bendpoint if needed
int index = getFeedbackState().isOutsideSource ? request.getIndex() + 1
: request.getIndex();
LineSeg moveLine = getLineSeg(constraint, index + 1);
LineSeg newLine = moveLine.getParallelLineSegThroughPoint(ptLoc);
index = adjustOutsideBoundsLineFeedback(newLine, index, constraint,
moveLine);
setLineSeg(constraint, index + 1, newLine);
getConnection().setRoutingConstraint(constraint);
}
/**
* adjustOutsideBoundsLineFeedback Method to handle feedback where the line
* is dragged outside of the source or target shapes bounding box.
*
* @param newLine
* LineSeg representing the line currently being manipulated.
* @param index
* the index
* @param constraint
* List of Bendpoint objects that is the constraint to the
* gesture.
* @param moveLine
* original segment that is being manipulated
* @return int new index value after the constraint and feedback have been
* adjusted.
*/
private int adjustOutsideBoundsLineFeedback(LineSeg newLine, int index,
List constraint, LineSeg moveLine) {
if (getLineSegMode().equals(LineMode.ORTHOGONAL_CONSTRAINED)) {
// merely enforce the fact that we can't adjust the line outside the
// bounds of the source and target.
if ((index == 0 && lineOutsideSource(newLine))
|| ((index + 1 == constraint.size() - 1) && lineOutsideTarget(newLine))) {
newLine.setOrigin(moveLine.getOrigin());
newLine.setTerminus(moveLine.getTerminus());
}
return index;
}
boolean bRemoveSource = false;
boolean bRemoveTarget = false;
boolean bSetNewSource = false;
boolean bSetNewTarget = false;
// Check source to see if we need to add a bendpoint
if (index == 0 && lineOutsideSource(newLine)) {
if (!getFeedbackState().isOutsideSource) {
getFeedbackState().isOutsideSource = true;
bSetNewSource = true;
}
} else if (index == 1 && getFeedbackState().isOutsideSource
&& !lineOutsideSource(newLine)) {
getFeedbackState().isOutsideSource = false;
bRemoveSource = true;
}
// Check target to see if we need to add a bendpoint
int checkTargetIndex = index + 1
+ (getFeedbackState().isOutsideTarget ? 1 : 0);
if ((checkTargetIndex == constraint.size() - 1)
&& lineOutsideTarget(newLine)) {
if (!getFeedbackState().isOutsideTarget) {
getFeedbackState().isOutsideTarget = true;
bSetNewTarget = true;
}
} else if (checkTargetIndex == constraint.size() - 2
&& getFeedbackState().isOutsideTarget
&& !lineOutsideTarget(newLine)) {
getFeedbackState().isOutsideTarget = false;
bRemoveTarget = true;
}
if (bRemoveSource) {
removeOutsideSourceFeedback(constraint);
index--;
}
if (bRemoveTarget) {
removeOutsideTargetFeedback(constraint);
}
if (bSetNewSource) {
showOutsideSourceFeedback(newLine, moveLine, constraint);
index++;
}
if (bSetNewTarget) {
showOutsideTargetFeedback(newLine, moveLine, constraint);
}
return index;
}
/**
* Method showOutsideSourceFeedback. Adds a bendpoint to the beginning of
* the constraint. Also adjusts the new segment with respect to added
* constraint
*
* @param constraint
* List of bendpoints that the source point will be added too.
*/
protected void showOutsideSourceFeedback(LineSeg newLine, LineSeg moveLine,
List constraint) {
Connection conn = (Connection) getHostFigure();
ConnectionAnchor anchor = conn.getSourceAnchor();
PrecisionPoint startPoint = new PrecisionPoint(anchor.getOwner()
.getBounds().getCenter());
anchor.getOwner().translateToAbsolute(startPoint);
conn.translateToRelative(startPoint);
PrecisionRectangle bounds = new PrecisionRectangle(anchor.getOwner()
.getBounds());
anchor.getOwner().translateToAbsolute(bounds);
conn.translateToRelative(bounds);
Point origin = new Point(newLine.getOrigin());
if (moveLine.isHorizontal()) {
origin.x = startPoint.x;
} else {
origin.y = startPoint.y;
}
newLine.setOrigin(origin);
constraint.add(0, new AbsoluteBendpoint(startPoint));
}
/**
* Method showOutsideTargetFeedback. Adds a bendpoint to the end of the
* constraint. Also adjusts the new segment with respect to added constraint
*
* @param constraint
* List of bendpoints that the target point will be added too.
*/
protected void showOutsideTargetFeedback(LineSeg newLine, LineSeg moveLine,
List constraint) {
Connection conn = (Connection) getHostFigure();
ConnectionAnchor anchor = conn.getTargetAnchor();
PrecisionPoint endPoint = new PrecisionPoint(anchor.getOwner()
.getBounds().getCenter());
anchor.getOwner().translateToAbsolute(endPoint);
conn.translateToRelative(endPoint);
PrecisionRectangle bounds = new PrecisionRectangle(anchor.getOwner()
.getBounds());
anchor.getOwner().translateToAbsolute(bounds);
conn.translateToRelative(bounds);
Point terminus = new Point(newLine.getTerminus());
if (moveLine.isHorizontal()) {
terminus.x = endPoint.x;
} else {
terminus.y = endPoint.y;
}
newLine.setTerminus(terminus);
constraint.add(new AbsoluteBendpoint(endPoint));
}
/**
* Override for AccessibleHandleProvider when deactivated
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=69316
*
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class key) {
if (key == AccessibleHandleProvider.class)
// handles == null when deactivated
if (handles == null) {
return null;
}
return super.getAdapter(key);
}
}