/******************************************************************************* * Copyright (c) 2000, 2005 IBM Corporation 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 * SAP AG - Changed for initial API, implementation and documentation of Graphiti: * made fields NULL_CONSTRAINT, originalConstraint, isDeleting, ref1, ref2 protected * made methods protected: * createHandlesForAutomaticBendpoints * createHandlesForUserBendpoints * isAutomaticallyBending * lineContainsPoint * setReferencePoints * added * getConfigurationProvider * getFeatureProvider * setConfigurationProvider * checkMoveAndRemoveBendpointFeature * checkCreateBendpointFeature *******************************************************************************/ // **************************************************************************** // **************************************************************************** // THIS CLASS IS MOSTLY COPIED FROM BendpointEditPolicy // BUT EVERYTHING IS protected, SO THAT IT CAN BE OVERWRITTEN // **************************************************************************** // **************************************************************************** package org.eclipse.graphiti.ui.internal.policy; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.List; import org.eclipse.draw2d.AbsoluteBendpoint; import org.eclipse.draw2d.AutomaticRouter; import org.eclipse.draw2d.Bendpoint; import org.eclipse.draw2d.Connection; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.PointList; import org.eclipse.draw2d.geometry.Rectangle; 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.BendpointHandle; import org.eclipse.gef.handles.BendpointMoveHandle; import org.eclipse.gef.requests.BendpointRequest; import org.eclipse.graphiti.features.IFeatureProvider; import org.eclipse.graphiti.features.context.impl.MoveBendpointContext; import org.eclipse.graphiti.features.context.impl.RemoveBendpointContext; import org.eclipse.graphiti.internal.services.GraphitiInternal; import org.eclipse.graphiti.mm.pictograms.FreeFormConnection; import org.eclipse.graphiti.ui.internal.config.IConfigurationProviderInternal; /** * Used to add bendpoint handles on a {@link ConnectionEditPart}. * <P> * BendpointEditPolicy will automatically observe the * {@link org.eclipse.draw2d.Connection} figure. If the number of bends in the * <code>Connection</code> changes, the handles will be updated. * * @noinstantiate This class is not intended to be instantiated by clients. * @noextend This class is not intended to be subclassed by clients. */ public abstract class BendpointEditPolicyFixed extends SelectionHandlesEditPolicy implements PropertyChangeListener { protected static final List<Bendpoint> NULL_CONSTRAINT = new ArrayList<Bendpoint>(); // CHANGED: make fields protected protected List<?> originalConstraint; protected boolean isDeleting = false; private IConfigurationProviderInternal configurationProvider; protected static final Point ref1 = new Point(); protected static final Point ref2 = new Point(); public BendpointEditPolicyFixed(IConfigurationProviderInternal configurationProvider) { super(); setConfigurationProvider(configurationProvider); } /** * <code>activate()</code> is extended to add a listener to the * <code>Connection</code> figure. * * @see org.eclipse.gef.EditPolicy#activate() */ @Override public void activate() { super.activate(); getConnection().addPropertyChangeListener(Connection.PROPERTY_POINTS, this); } 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++) list.add(new BendpointCreationHandle(connEP, 0, i)); return list; } protected List<BendpointHandle> createHandlesForUserBendpoints() { List<BendpointHandle> list = new ArrayList<BendpointHandle>(); ConnectionEditPart connEP = (ConnectionEditPart) getHost(); PointList points = getConnection().getPoints(); List<?> bendPoints = (List<?>) getConnection().getRoutingConstraint(); int bendPointIndex = 0; Point currBendPoint = null; if (bendPoints == null) { bendPoints = NULL_CONSTRAINT; } else if (!bendPoints.isEmpty()) { currBendPoint = ((Bendpoint) bendPoints.get(0)).getLocation(); } for (int i = 0; i < points.size() - 1; i++) { if (checkCreateBendpointFeature()) { // Put a create handle on the middle of every segment list.add(new BendpointCreationHandle(connEP, bendPointIndex, i)); } // 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))) { if (checkMoveAndRemoveBendpointFeature()) { list.add(new BendpointMoveHandle(connEP, bendPointIndex, i + 1)); } // Go to the next user bendpoint bendPointIndex++; if (bendPointIndex < bendPoints.size()) { currBendPoint = ((Bendpoint) bendPoints.get(bendPointIndex)).getLocation(); } } } return list; } /** * 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. * * @see SelectionHandlesEditPolicy#createSelectionHandles() */ @Override protected List<BendpointHandle> createSelectionHandles() { List<BendpointHandle> list = new ArrayList<BendpointHandle>(); boolean automaticallyBending = isAutomaticallyBending(); if (automaticallyBending) { list = createHandlesForAutomaticBendpoints(); } else { list = createHandlesForUserBendpoints(); } return list; } /** * <code>deactivate()</code> is extended to remove the property change * listener on the <code>Connection</code> figure. * * @see org.eclipse.gef.EditPolicy#deactivate() */ @Override public void deactivate() { getConnection().removePropertyChangeListener(Connection.PROPERTY_POINTS, this); super.deactivate(); } /** * Erases all bendpoint feedback. Since the original <code>Connection</code> * figure is used for feedback, we just restore the original constraint that * was saved before feedback started to show. * * @param request * the BendpointRequest */ protected void eraseConnectionFeedback(BendpointRequest request) { restoreOriginalConstraint(); request.getSource().refresh(); originalConstraint = null; } /** * @see org.eclipse.gef.EditPolicy#eraseSourceFeedback(Request) */ @Override public void eraseSourceFeedback(Request request) { if (REQ_MOVE_BENDPOINT.equals(request.getType()) || REQ_CREATE_BENDPOINT.equals(request.getType())) eraseConnectionFeedback((BendpointRequest) request); } /** * Factors the Request into either a MOVE, a DELETE, or a CREATE of a * bendpoint. * * @see org.eclipse.gef.EditPolicy#getCommand(Request) */ @Override public Command getCommand(Request request) { if (REQ_MOVE_BENDPOINT.equals(request.getType())) { if (isDeleting) { return getDeleteBendpointCommand((BendpointRequest) request); } return getMoveBendpointCommand((BendpointRequest) request); } if (REQ_CREATE_BENDPOINT.equals(request.getType())) { return getCreateBendpointCommand((BendpointRequest) request); } return null; } /** * Convenience method for obtaining the host's <code>Connection</code> * figure. * * @return the Connection figure */ protected Connection getConnection() { return (Connection) ((ConnectionEditPart) getHost()).getFigure(); } /** * Implement this method to return a Command that will create a bendpoint. * * @param request * the BendpointRequest * @return a Command to create a bendpoint */ protected abstract Command getCreateBendpointCommand(BendpointRequest request); /** * Implement this method to return a Command that will delete a bendpoint. * * @param request * the BendpointRequest * @return a Command to delete a bendpoint */ protected abstract Command getDeleteBendpointCommand(BendpointRequest request); /** * Implement this method to return a Command that will move a bendpoint. * * @param request * the BendpointRequest * @return a Command to move a bendpoint */ protected abstract Command getMoveBendpointCommand(BendpointRequest request); protected boolean isAutomaticallyBending() { List<?> constraint = (List<?>) getConnection().getRoutingConstraint(); PointList points = getConnection().getPoints(); return ((points.size() > 2) && (constraint == null || constraint.isEmpty())); } protected boolean lineContainsPoint(Point p1, Point p2, Point p) { int tolerance = 7; Rectangle rect = Rectangle.SINGLETON; rect.setSize(0, 0); rect.setLocation(p1.x, p1.y); rect.union(p2.x, p2.y); rect.expand(tolerance, tolerance); if (!rect.contains(p.x, p.y)) return false; int v1x, v1y, v2x, v2y; int numerator, denominator; double result = 0.0; if (p1.x != p2.x && p1.y != p2.y) { v1x = p2.x - p1.x; v1y = p2.y - p1.y; v2x = p.x - p1.x; v2y = p.y - p1.y; numerator = v2x * v1y - v1x * v2y; denominator = v1x * v1x + v1y * v1y; result = ((numerator << 10) / denominator * numerator) >> 10; } // if it is the same point, and it passes the bounding box test, // the result is always true. return result <= tolerance * tolerance; } /** * If the number of bendpoints changes, handles are updated. * * @see java.beans.PropertyChangeListener#propertyChange(PropertyChangeEvent) */ public void propertyChange(PropertyChangeEvent evt) { // $TODO optimize so that handles aren't added constantly. if (getHost().getSelected() != EditPart.SELECTED_NONE) addSelectionHandles(); } /** * Restores the original constraint that was saved before feedback began to * show. */ protected void restoreOriginalConstraint() { if (originalConstraint != null) { if (originalConstraint == NULL_CONSTRAINT) getConnection().setRoutingConstraint(null); else getConnection().setRoutingConstraint(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() { originalConstraint = (List<?>) getConnection().getRoutingConstraint(); if (originalConstraint == null) originalConstraint = NULL_CONSTRAINT; getConnection().setRoutingConstraint(new ArrayList<Object>(originalConstraint)); } protected void setReferencePoints(BendpointRequest request) { PointList points = getConnection().getPoints(); int bpIndex = -1; List<?> bendPoints = (List<?>) getConnection().getRoutingConstraint(); Point bp = ((Bendpoint) bendPoints.get(request.getIndex())).getLocation(); double smallestDistance = -1; for (int i = 0; i < points.size(); i++) { double dist = points.getPoint(i).getDistance(bp); if (smallestDistance == -1 || dist < smallestDistance) { bpIndex = i; smallestDistance = dist; if (smallestDistance == 0) break; } } points.getPoint(ref1, bpIndex - 1); getConnection().translateToAbsolute(ref1); points.getPoint(ref2, bpIndex + 1); getConnection().translateToAbsolute(ref2); } /** * 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. * * @param request * the BendpointRequest */ protected void showCreateBendpointFeedback(BendpointRequest request) { Point p = new 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(); } constraint.set(request.getIndex(), bp); getConnection().setRoutingConstraint(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. * * @param request * the BendpointRequest */ protected void showDeleteBendpointFeedback(BendpointRequest request) { if (originalConstraint == null) { saveOriginalConstraint(); List<?> constraint = (List<?>) getConnection().getRoutingConstraint(); constraint.remove(request.getIndex()); getConnection().setRoutingConstraint(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. * * @param request * the BendpointRequest */ protected void showMoveBendpointFeedback(BendpointRequest request) { Point p = new Point(request.getLocation()); if (!isDeleting) setReferencePoints(request); if (lineContainsPoint(ref1, ref2, p)) { if (!isDeleting) { isDeleting = true; eraseSourceFeedback(request); showDeleteBendpointFeedback(request); } return; } if (isDeleting) { isDeleting = false; eraseSourceFeedback(request); } if (originalConstraint == null) saveOriginalConstraint(); List<Bendpoint> constraint = getConnectionRoutingConstraint(); getConnection().translateToRelative(p); Bendpoint bp = new AbsoluteBendpoint(p); constraint.set(request.getIndex(), bp); getConnection().setRoutingConstraint(constraint); } /** * Shows feedback when appropriate. Calls a different method depending on * the request type. * * @see #showCreateBendpointFeedback(BendpointRequest) * @see #showMoveBendpointFeedback(BendpointRequest) * @param request * the Request */ @Override public void showSourceFeedback(Request request) { if (REQ_MOVE_BENDPOINT.equals(request.getType())) showMoveBendpointFeedback((BendpointRequest) request); else if (REQ_CREATE_BENDPOINT.equals(request.getType())) showCreateBendpointFeedback((BendpointRequest) request); } protected IConfigurationProviderInternal getConfigurationProvider() { return configurationProvider; } protected IFeatureProvider getFeatureProvider() { if (getConfigurationProvider() != null) { return getConfigurationProvider().getFeatureProvider(); } return null; } private void setConfigurationProvider(IConfigurationProviderInternal configurationProvider) { this.configurationProvider = configurationProvider; } private boolean checkMoveAndRemoveBendpointFeature() { if (!(getConnection() instanceof FreeFormConnection)) { return true; } boolean ret = false; FreeFormConnection ffc = (FreeFormConnection) getConnection(); if (!GraphitiInternal.getEmfService().isObjectAlive(ffc)) { return ret; } ret = (null != getFeatureProvider().getMoveBendpointFeature(new MoveBendpointContext(null))); ret = ret || (null != getFeatureProvider().getRemoveBendpointFeature(new RemoveBendpointContext(ffc, null))); return ret; } private boolean checkCreateBendpointFeature() { if (!(getConnection() instanceof FreeFormConnection)) { return true; } boolean ret = false; FreeFormConnection ffc = (FreeFormConnection) getConnection(); if (!GraphitiInternal.getEmfService().isObjectAlive(ffc)) { return ret; } ret = (null != getFeatureProvider().getMoveBendpointFeature(new MoveBendpointContext(null))); return ret; } protected List<Bendpoint> getConnectionRoutingConstraint() { Object rawRoutingConstraint = getConnection().getRoutingConstraint(); @SuppressWarnings("unchecked") List<Bendpoint> ret = (List<Bendpoint>) rawRoutingConstraint; return ret; } }