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