/***************************************************************************** | |
* Copyright (c) 2014 CEA LIST. | |
* | |
* | |
* 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: | |
* Vincent Lorenzo (CEA LIST) vincent.lorenzo@cea.fr - Initial API and implementation | |
* | |
*****************************************************************************/ | |
package org.eclipse.gmf.tooling.runtime.linklf; | |
import org.eclipse.core.commands.ExecutionException; | |
import org.eclipse.core.runtime.Assert; | |
import org.eclipse.core.runtime.IAdaptable; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.draw2d.AbstractPointListShape; | |
import org.eclipse.draw2d.ConnectionAnchor; | |
import org.eclipse.draw2d.IFigure; | |
import org.eclipse.draw2d.PositionConstants; | |
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.draw2d.geometry.Rectangle; | |
import org.eclipse.emf.transaction.TransactionalEditingDomain; | |
import org.eclipse.gef.commands.Command; | |
import org.eclipse.gef.commands.CompoundCommand; | |
import org.eclipse.gef.editparts.AbstractConnectionEditPart; | |
import org.eclipse.gmf.runtime.common.core.command.CommandResult; | |
import org.eclipse.gmf.runtime.common.core.command.ICommand; | |
import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; | |
import org.eclipse.gmf.runtime.diagram.ui.editparts.INodeEditPart; | |
import org.eclipse.gmf.runtime.draw2d.ui.figures.PolylineConnectionEx; | |
import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; | |
import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; | |
import org.eclipse.gmf.runtime.notation.Edge; | |
import org.eclipse.gmf.runtime.notation.IdentityAnchor; | |
import org.eclipse.gmf.runtime.notation.NotationFactory; | |
import org.eclipse.gmf.runtime.notation.View; | |
/** | |
* | |
* This class provides command to fix the anchors during a resize. | |
* The methods are not static to allow to overriding | |
* | |
*/ | |
public class FixAnchorHelper { | |
/** | |
* the editing domain used for the command | |
*/ | |
protected final TransactionalEditingDomain domain; | |
/** | |
* | |
* Constructor. | |
* | |
* @param domain | |
* the editing domain to use, it must not be <code>null</code> | |
*/ | |
public FixAnchorHelper(final TransactionalEditingDomain domain) { | |
Assert.isNotNull(domain); | |
this.domain = domain; | |
} | |
/** | |
* | |
* @param node | |
* the resized node | |
* @param move | |
* the move direction | |
* @param sizeDelta | |
* the delta of the size | |
* @param moveDirection | |
* the direction for the resize/move | |
* @return | |
* the command to fix the anchor or <code>null</code> if we can't fix it | |
*/ | |
public Command getFixIdentityAnchorCommand(final INodeEditPart node, final Point move, final Dimension sizeDelta, int moveDirection) { | |
final CompoundCommand cc = new CompoundCommand("Fix All Anchors Command"); //$NON-NLS-1$ | |
//1. we calculate the new bounds; | |
final IFigure nodeFigure = node.getFigure(); | |
final PrecisionRectangle oldBounds = new PrecisionRectangle(nodeFigure.getBounds()); | |
//we translate the coordinates to absolute | |
nodeFigure.translateToAbsolute(oldBounds); | |
final PrecisionRectangle newBounds = oldBounds.getPreciseCopy(); | |
newBounds.translate(move.preciseX(), move.preciseY()); | |
newBounds.resize(sizeDelta.preciseWidth(), sizeDelta.preciseHeight()); | |
//2. we iterate on the target anchor | |
for(final Object targetConnection : node.getTargetConnections()) { | |
if(targetConnection instanceof AbstractConnectionEditPart) { | |
final Command tmp = getFixAnchorCommand(node, oldBounds, newBounds, (AbstractConnectionEditPart)targetConnection, move, sizeDelta, false); | |
if(tmp != null) { | |
cc.add(tmp); | |
} | |
} | |
} | |
//. we iterate on the source anchor | |
for(final Object sourceConnection : node.getSourceConnections()) { | |
if(sourceConnection instanceof AbstractConnectionEditPart) { | |
final Command tmp = getFixAnchorCommand(node, oldBounds, newBounds, (AbstractConnectionEditPart)sourceConnection, move, sizeDelta, true); | |
if(tmp != null) { | |
cc.add(tmp); | |
} | |
} | |
} | |
if(cc.isEmpty()) { | |
return null; | |
} | |
return cc; | |
} | |
/** | |
* | |
* @param edgeEP | |
* the edge edit part | |
* @param sourcePoint | |
* if <code>true</code> we return the source point and if false we return the end point | |
* @return | |
* the real point to fix | |
*/ | |
protected Point getRealAnchorPoint(final AbstractConnectionEditPart edgeEP, final boolean sourcePoint) { | |
final IFigure figure = edgeEP.getFigure(); | |
Point point = null; | |
if(figure instanceof AbstractPointListShape) { | |
if(sourcePoint) { | |
point = ((AbstractPointListShape)figure).getStart().getCopy(); | |
} else { | |
point = ((AbstractPointListShape)figure).getEnd().getCopy(); | |
} | |
} | |
figure.translateToAbsolute(point); | |
return point; | |
} | |
/** | |
* | |
* @param edgeEP | |
* the edge edit part | |
* @param sourcePoint | |
* if <code>true</code> we return the source point and if false we return the end point | |
* @param nodeEP | |
* the node edit part | |
* @param p | |
* the real anchor point in absolute coordinate | |
* @return | |
* the anchor representing the point to fix | |
*/ | |
protected IdentityAnchor getIdentityAnchor(final AbstractConnectionEditPart edgeEP, final boolean sourcePoint, final INodeEditPart nodeEP, final Point p) { | |
final View view = (View)edgeEP.getAdapter(View.class); | |
IdentityAnchor anchor = null; | |
if(view instanceof Edge) { | |
final Object tmpAnchor; | |
if(sourcePoint) { | |
tmpAnchor = ((Edge)view).getSourceAnchor(); | |
} else { | |
tmpAnchor = ((Edge)view).getTargetAnchor(); | |
} | |
if(tmpAnchor instanceof IdentityAnchor) { | |
anchor = (IdentityAnchor)tmpAnchor; | |
} | |
if(anchor == null) { | |
ConnectionAnchor connectionAnchor = null; | |
if(nodeEP.getFigure() instanceof NodeFigure) { | |
NodeFigure nodeFigure = (NodeFigure)nodeEP.getFigure(); | |
if(sourcePoint) { | |
connectionAnchor = nodeFigure.getSourceConnectionAnchorAt(p); | |
} else { | |
connectionAnchor = nodeFigure.getTargetConnectionAnchorAt(p); | |
} | |
if(connectionAnchor != null) { | |
final String id = nodeEP.mapConnectionAnchorToTerminal(connectionAnchor); | |
anchor = NotationFactory.eINSTANCE.createIdentityAnchor(); | |
anchor.setId(id); | |
} | |
} | |
} | |
} | |
return anchor; | |
} | |
/** | |
* | |
* @param nodeEditPart | |
* the resized edit part | |
* @param oldNodeBounds | |
* the old bounds for this edit part | |
* @param newNodeBounds | |
* the new bounds for this edit part | |
* @param targetConnectionEP | |
* the edit part of the connection for which we want fix anchor | |
* @param move | |
* the move | |
* @param sizeDelta | |
* the delta of the resize | |
* @param fixSource | |
* if <code>true</code> we are fixing the source anchor if <code>false</code> we are fixing the target anchor | |
* @return | |
*/ | |
public Command getFixAnchorCommand(final INodeEditPart nodeEditPart, final PrecisionRectangle oldNodeBounds, final PrecisionRectangle newNodeBounds, final AbstractConnectionEditPart targetConnectionEP, final Point move, final Dimension sizeDelta, final boolean fixSource) { | |
final Point realAnchorPoint = getRealAnchorPoint(targetConnectionEP, fixSource); | |
final IFigure fig = nodeEditPart.getFigure(); | |
if(fig instanceof NodeFigure) { | |
final NodeFigure nodeFigure = (NodeFigure)fig; | |
final IdentityAnchor editedAnchor = getIdentityAnchor(targetConnectionEP, fixSource, nodeEditPart, realAnchorPoint); | |
if(realAnchorPoint != null && editedAnchor != null) { | |
final View view = (View)targetConnectionEP.getAdapter(View.class); | |
if(view instanceof Edge) { | |
//1. get the real side on which start/end the manipulated anchor | |
final int anchorSide = getSideOfConnectionPoint(nodeFigure, targetConnectionEP, fixSource); | |
//2. determine the new values | |
double newX = -1; | |
double newY = -1; | |
switch(anchorSide) { | |
case PositionConstants.NORTH: | |
newY = 0; | |
break; | |
case PositionConstants.WEST: | |
newX = 0; | |
break; | |
case PositionConstants.EAST: | |
newX = 1; | |
break; | |
case PositionConstants.SOUTH: | |
newY = 1; | |
break; | |
default: | |
// other case not yet manager because they are resize dependant! | |
break; | |
} | |
PrecisionPoint newRealAnchorPoint = new PrecisionPoint(realAnchorPoint); | |
newRealAnchorPoint.setPreciseLocation(newRealAnchorPoint.x() - oldNodeBounds.preciseX(), newRealAnchorPoint.y() - oldNodeBounds.preciseY()); | |
PrecisionPoint newLocation = newRealAnchorPoint.getPreciseCopy(); | |
newLocation.setPreciseX(newLocation.preciseX() - move.preciseX()); | |
newLocation.setPreciseY(newLocation.preciseY() - move.preciseY()); | |
if(newX == -1) { | |
newX = newLocation.preciseX() / newNodeBounds.preciseWidth(); | |
} | |
if(newY == -1) { | |
newY = newLocation.preciseY() / newNodeBounds.preciseHeight(); | |
} | |
if(newX <= 1 && newX >= 0 && newY <= 1 && newY >= 0) { | |
final String newIdValue = IdentityAnchorHelper.createNewAnchorIdValue(newX, newY); | |
final ICommand cmd = new AbstractTransactionalCommand(this.domain, "Fix Anchor Location", null) { //$NON-NLS-1$ | |
@Override | |
protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { | |
editedAnchor.setId(newIdValue); | |
if(editedAnchor.eContainer() == null) { | |
if(fixSource) { | |
((Edge)view).setSourceAnchor(editedAnchor); | |
} else { | |
((Edge)view).setTargetAnchor(editedAnchor); | |
} | |
} | |
return CommandResult.newOKCommandResult(editedAnchor); | |
} | |
}; | |
return new ICommandProxy(cmd); | |
} | |
} | |
} | |
} | |
return null; | |
} | |
/** | |
* | |
* @param nodeFigure | |
* the node figure | |
* @param edgeEP | |
* the connection edit part | |
* @param fixingSource | |
* if <code>true</code> we are fixing the source anchor and if <code>false</code> we are fixing the target anchor | |
* @return | |
* the direction of the manipulated anchor (according to {@link PositionConstants} | |
*/ | |
protected final int getSideOfConnectionPoint(final IFigure nodeFigure, final AbstractConnectionEditPart edgeEP, final boolean fixingSource) { | |
int side = -1; | |
final IFigure figure = edgeEP.getFigure(); | |
if(figure instanceof PolylineConnectionEx) { | |
final PolylineConnectionEx connection = (PolylineConnectionEx)figure; | |
final Rectangle bounds = nodeFigure.getBounds().getCopy(); | |
nodeFigure.translateToAbsolute(bounds); | |
final Point pt; | |
if(fixingSource) { | |
pt = connection.getStart(); | |
} else { | |
pt = connection.getEnd(); | |
} | |
figure.translateToAbsolute(pt); | |
Rectangle includedRect = bounds.getCopy(); | |
while(includedRect.contains(pt)) { | |
includedRect.shrink(1, 1); | |
} | |
side = includedRect.getPosition(pt); | |
//if the anchor side is a corner, we determine its side using another point | |
if(side == PositionConstants.NORTH_WEST || side == PositionConstants.NORTH_EAST || side == PositionConstants.SOUTH_EAST || side == PositionConstants.SOUTH_WEST) { | |
final Point previousPoint; | |
final PointList list = connection.getPoints(); | |
if(list.size() > 1) { | |
if(fixingSource) { | |
previousPoint = list.getPoint(1); | |
} else { | |
previousPoint = list.getPoint(list.size() - 2); | |
} | |
nodeFigure.translateToAbsolute(previousPoint.getCopy()); | |
while(includedRect.contains(previousPoint)) { | |
includedRect.shrink(1, 1); | |
} | |
side = includedRect.getPosition(previousPoint); | |
} | |
} | |
} | |
return side; | |
} | |
} |