package org.eclipse.gmf.tooling.runtime.linklf.policies; import java.util.List; import org.eclipse.draw2d.AbsoluteBendpoint; import org.eclipse.draw2d.Connection; import org.eclipse.draw2d.ConnectionAnchor; import org.eclipse.draw2d.ConnectionRouter; import org.eclipse.draw2d.PositionConstants; 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.emf.transaction.TransactionalEditingDomain; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.SnapToHelper; import org.eclipse.gef.commands.Command; import org.eclipse.gef.requests.BendpointRequest; import org.eclipse.gef.requests.LocationRequest; import org.eclipse.gef.requests.ReconnectRequest; import org.eclipse.gmf.runtime.common.core.command.ICommand; import org.eclipse.gmf.runtime.diagram.core.commands.SetConnectionAnchorsCommand; import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; import org.eclipse.gmf.runtime.diagram.ui.editparts.INodeEditPart; import org.eclipse.gmf.runtime.diagram.ui.requests.SetAllBendpointRequest; import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg; import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; import org.eclipse.gmf.runtime.gef.ui.internal.editpolicies.LineMode; import org.eclipse.gmf.runtime.gef.ui.internal.tools.ConnectionBendpointTrackerEx; import org.eclipse.gmf.runtime.gef.ui.internal.tools.SelectConnectionEditPartTracker; import org.eclipse.gmf.runtime.notation.Edge; import org.eclipse.gmf.tooling.runtime.linklf.router.HintedOrthogonalRouter; import org.eclipse.gmf.tooling.runtime.linklf.router.HintedOrthogonalRouter.EndRoutingHint; /** * @since 3.3 */ public class LinksLFConnectionBendpointEditPolicy extends ConnectionBendpointEditPolicy3 { /** * @see #getBendpointsChangedCommand(BendpointRequest) */ private static final String PARAM_CACHED_COMMAND_BASED_ON_FEEDBACK = LinksLFConnectionBendpointEditPolicy.class .getName() + ":CachedCommand"; /** * @see #getBendpointsChangedCommand(BendpointRequest) */ private boolean myIsShowingFeedback; public LinksLFConnectionBendpointEditPolicy(LineMode lineMode) { super(lineMode); } public LinksLFConnectionBendpointEditPolicy() { this(LineMode.OBLIQUE); } /** * This method is overridden in order to workaround the bug #445681 for * special rerouting of rectilinear links. There the routing is changed * correctly when user moves (in some specific way) the single bendpoint, * but does not work when user operates on the corresponding link segment. * <p/> * The difference is that the {@link ConnectionBendpointTrackerEx} (case of * single bendpoint) and {@link SelectConnectionEditPartTracker} (case of * segment) behave differently when the user wants to actually fire the * change. * <p/> * The former computes the command when the connection feedback is shown, * and then on buttonUp just executes the cached command. The latter (@see * {@link SelectConnectionEditPartTracker#handleButtonUp(int)}) first erases * the feedback and then asks the editpart to prepare the command. * <p/> * It breaks the idea of computing the command based on the feedback * connection routing which is central for approach of this class and its * super-classes. It ultilmately leads to the bug #445681, because at the * time when the final command is computed, the feedback connection is gone * and routing is wrong. * <p/> * To workaround this, we will cache the previously computed command and * will use it as a one time replacement for the first call immediately * after the erasing the feedback. */ @Override protected Command getBendpointsChangedCommand(BendpointRequest request) { // note: it is **removed** from cache below: Command cachedPreviousCommand = (Command) request.getExtendedData() .remove(PARAM_CACHED_COMMAND_BASED_ON_FEEDBACK); if (cachedPreviousCommand != null && !myIsShowingFeedback) { // one time, immediately after erasing the feedback we are going to // use the old command // which is based on the routing of the just erased feedback // to avoid breaking other things, we will remove it from cache and // won't use it beyond this one time return cachedPreviousCommand; } Command result = super.getBendpointsChangedCommand(request); if (myIsShowingFeedback) { // still showing feedback, cache the new result for later use request.getExtendedData().put( PARAM_CACHED_COMMAND_BASED_ON_FEEDBACK, result); } return result; } /** * Method getBendpointsChangedCommand Different signature method that allows * a command to constructed for changing the bendpoints without requiring * the original Request. * * @param connection * Connection to generate the bendpoints changed command from * @param edge * notation element that the command will operate on. * @return Command SetBendpointsCommand that contains the point changes for * the connection. */ protected Command getBendpointsChangedCommand(Connection connection, Edge edge) { TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost()) .getEditingDomain(); Point ptRef1 = connection.getSourceAnchor().getReferencePoint(); getConnection().translateToRelative(ptRef1); SetConnectionAnchorsCommand srcAnchorUpdate = null; if (getHost().getSource() instanceof INodeEditPart && connection.getPoints().size() > 1) { ptRef1 = connection.getPoints().getFirstPoint(); INodeEditPart sourceEP = (INodeEditPart) getHost().getSource(); ReconnectRequest reconnectSource = new ReconnectRequest( RequestConstants.REQ_RECONNECT_SOURCE); Point ptAbs1 = ptRef1.getCopy(); getConnection().translateToAbsolute(ptAbs1); reconnectSource.setLocation(ptAbs1); reconnectSource.setConnectionEditPart(getHost()); ConnectionAnchor newAnchor = sourceEP .getSourceConnectionAnchor(reconnectSource); String newTerminal = sourceEP .mapConnectionAnchorToTerminal(newAnchor); srcAnchorUpdate = new SetConnectionAnchorsCommand(editingDomain, "Updating source anchor"); srcAnchorUpdate.setEdgeAdaptor(new EObjectAdapter(edge)); srcAnchorUpdate.setNewSourceTerminal(newTerminal); } Point ptRef2 = connection.getTargetAnchor().getReferencePoint(); getConnection().translateToRelative(ptRef2); SetConnectionAnchorsCommand trgAnchorUpdate = null; if (getHost().getTarget() instanceof INodeEditPart && connection.getPoints().size() > 1) { ptRef2 = connection.getPoints().getLastPoint(); INodeEditPart targetEP = (INodeEditPart) getHost().getTarget(); ReconnectRequest reconnectTarget = new ReconnectRequest( RequestConstants.REQ_RECONNECT_TARGET); Point ptAbs2 = ptRef2.getCopy(); getConnection().translateToAbsolute(ptAbs2); reconnectTarget.setLocation(ptAbs2); reconnectTarget.setConnectionEditPart(getHost()); ConnectionAnchor newTargetAnchor = targetEP .getTargetConnectionAnchor(reconnectTarget); String newTerminal = targetEP .mapConnectionAnchorToTerminal(newTargetAnchor); trgAnchorUpdate = new SetConnectionAnchorsCommand(editingDomain, "Updating target anchor"); trgAnchorUpdate.setEdgeAdaptor(new EObjectAdapter(edge)); trgAnchorUpdate.setNewTargetTerminal(newTerminal); } SetAbsoluteBendpointsCommand sbbCommand = new SetAbsoluteBendpointsCommand( editingDomain); sbbCommand.setEdgeAdapter(new EObjectAdapter(edge)); sbbCommand.setNewPointList(connection.getPoints()); ICommand result = sbbCommand; if (srcAnchorUpdate != null) { result = result.compose(srcAnchorUpdate); } if (trgAnchorUpdate != null) { result = result.compose(trgAnchorUpdate); } return new ICommandProxy(result.reduce()); } /** * Method getSetBendpointCommand. This method returns a command that * executes the REQ_SET_ALL_BENDPOINT request * * @param request * SetAllBendpointRequest that stores the points to be set by the * command. * @return Command to be executed. */ protected Command getSetBendpointCommand(SetAllBendpointRequest request) { Connection connection = getConnection(); PointList newPoints = request.getPoints(); TransactionalEditingDomain editingDomain = ((IGraphicalEditPart) getHost()) .getEditingDomain(); SetAbsoluteBendpointsCommand sbbCommand = new SetAbsoluteBendpointsCommand( editingDomain); sbbCommand.setEdgeAdapter(new EObjectAdapter((Edge) getHost() .getModel())); // with SetAbsoluteBendpointsCommand we can use // setNewPointList(PointList) here // but I left warnings here to revisit what are // request.getSource/TargetReference() is // and how it is expected to affect the result here if (request.getSourceReference() != null && request.getTargetReference() != null) { sbbCommand.setNewPointList( // newPoints, request.getSourceReference(), request.getTargetReference()); } else { sbbCommand.setNewPointList( // newPoints, connection.getSourceAnchor(), connection.getTargetAnchor()); } return new ICommandProxy(sbbCommand); } @Override public ConnectionEditPart getHost() { return (ConnectionEditPart) super.getHost(); } @Override protected Connection getConnection() { return super.getConnection(); } /** * Overrides default behavior by additional snapping of the added point with * grid when grid is active */ @Override protected void showOutsideSourceFeedback(LineSeg newLine, LineSeg moveLine, List constraint) { Connection conn = (Connection) getHostFigure(); ConnectionAnchor anchor = conn.getSourceAnchor(); PrecisionRectangle bounds = new PrecisionRectangle(anchor.getOwner() .getBounds()); anchor.getOwner().translateToAbsolute(bounds); PrecisionPoint startPoint = new PrecisionPoint(anchor.getOwner() .getBounds().getCenter()); anchor.getOwner().translateToAbsolute(startPoint); snapToGrid(startPoint, moveLine.isHorizontal(), bounds); conn.translateToRelative(startPoint); 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)); } /** * Overrides default behavior by additional snapping of the added point with * grid when grid is active */ @Override protected void showOutsideTargetFeedback(LineSeg newLine, LineSeg moveLine, List constraint) { Connection conn = (Connection) getHostFigure(); ConnectionAnchor anchor = conn.getTargetAnchor(); PrecisionRectangle bounds = new PrecisionRectangle(anchor.getOwner() .getBounds()); anchor.getOwner().translateToAbsolute(bounds); PrecisionPoint endPoint = new PrecisionPoint(anchor.getOwner() .getBounds().getCenter()); anchor.getOwner().translateToAbsolute(endPoint); snapToGrid(endPoint, moveLine.isHorizontal(), bounds); conn.translateToRelative(endPoint); 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)); } protected void snapToGrid(PrecisionPoint point, boolean horizontally, PrecisionRectangle limitingBounds) { SnapToHelper snapper = (SnapToHelper) getHost().getAdapter( SnapToHelper.class); if (snapper == null) { return; } PrecisionPoint snapped = point.getPreciseCopy(); snapper.snapPoint(new LocationRequest(REQ_MOVE_BENDPOINT), horizontally ? PositionConstants.HORIZONTAL : PositionConstants.VERTICAL, point, snapped); point.setLocation(snapped); } @Override public void showSourceFeedback(Request request) { ConnectionRouter router = getConnection().getConnectionRouter(); if (router instanceof HintedOrthogonalRouter) { HintedOrthogonalRouter routerImpl = (HintedOrthogonalRouter) router; EndRoutingHint hint = request instanceof BendpointRequest ? EndRoutingHint.FixBendpointMoveAnchor : EndRoutingHint.FixAnchorMoveBendpoint; routerImpl.setEndRoutingHint(getConnection(), hint); } super.showSourceFeedback(request); } @Override public void eraseSourceFeedback(Request request) { ConnectionRouter router = getConnection().getConnectionRouter(); if (router instanceof HintedOrthogonalRouter) { ((HintedOrthogonalRouter) router).setEndRoutingHint( getConnection(), null); } super.eraseSourceFeedback(request); } /** * @see #getBendpointsChangedCommand(BendpointRequest) */ @Override protected void eraseConnectionFeedback(BendpointRequest request, boolean removeFeedbackFigure) { myIsShowingFeedback = false; super.eraseConnectionFeedback(request, removeFeedbackFigure); } /** * @see org.eclipse.papyrus.infra.gmfdiag.common.linklf.editpolicies.notformars.ConnectionBendpointEditPolicy2#showMoveBendpointFeedback(org.eclipse.gef.requests.BendpointRequest) * * @param request */ @Override protected void showMoveBendpointFeedback(BendpointRequest request) { super.showMoveBendpointFeedback(request); myIsShowingFeedback = true; } @Override protected void showCreateBendpointFeedback(BendpointRequest request) { super.showCreateBendpointFeedback(request); myIsShowingFeedback = true; } @Override protected void showMoveLineSegFeedback(BendpointRequest request) { super.showMoveLineSegFeedback(request); myIsShowingFeedback = true; } @Override protected void showMoveOrthogonalBenspointFeedback(BendpointRequest request) { super.showMoveOrthogonalBenspointFeedback(request); myIsShowingFeedback = true; } }