/*****************************************************************************
* 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.xylayout;
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.
* <p/>
*
* This class is originally authored by Papyrus project.
*
* @since 3.3
*/
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 = 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;
}
/**
*
* @param percentageOnX
* the percentage on x
* @param percentageOnY
* the percentage on y
* @return the string representing the new id for an anchor
*/
public static final String createNewAnchorIdValue(
final double percentageOnX, final double percentageOnY) {
final StringBuffer buffer = new StringBuffer();
buffer.append('(');
buffer.append(Double.toString(percentageOnX));
buffer.append(',');
buffer.append(Double.toString(percentageOnY));
buffer.append(')');
return buffer.toString();
}
}