/*****************************************************************************
* Copyright (c) 2012 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:
* CEA LIST - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.common.layout;
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.gef.ConnectionEditPart;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.requests.ReconnectRequest;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.GraphicalNodeEditPolicy;
import org.eclipse.gmf.runtime.diagram.ui.requests.SetAllBendpointRequest;
import org.eclipse.gmf.runtime.draw2d.ui.figures.PolylineConnectionEx;
/**
*
* This class is used to represent a link, to allow an alignment action. Its
* provides fields to store the sources and the target and methods to do the
* needed action to align the link extremities.
*
* This alignment align the two anchors of a link. To do this, only one {@link EditPart} can moves.
*
* TODO should be merge with LinkRepresentationForLayoutAction
*/
public class LinkRepresentation {
/** the alignment */
private final int align;
/** the represented link */
private final EditPart link;
/** the representation of the source for the {@link #link} */
private final EditPartRepresentation target;
/** the representation of the target for the {@link #link} */
private final EditPartRepresentation source;
/** the {@link EditPartRepresentation} for the fixed {@link EditPart} */
private EditPartRepresentation fixedEditPart = null;
/** the {@link EditPartRepresentation} for the mowing {@link EditPart} */
private EditPartRepresentation movedEditPart = null;
/** the reference point for this action (this point will not move) */
private Point referencePoint;
/** the point which moves to do the action */
private Point movedPoint;
/**
*
* Constructor.
*
* @param link
* the represented link
* @param source
* the representation of the source of the link
* @param target
* the representation of the target of the link
* @param alignment
* the alignment
*/
public LinkRepresentation(EditPart link, EditPartRepresentation source, EditPartRepresentation target, int alignment) {
this.link = link;
this.target = target;
this.source = source;
this.align = alignment;
}
/**
* Getter for {@link #link}
*
* @return {@link #link}
*/
public EditPart getLink() {
return this.link;
}
/**
* Getter for {@link #source}
*
* @return {@link #source}
*/
public EditPartRepresentation getSource() {
return this.source;
}
/**
* Getter for {@link #target}
*
* @return {@link #target}
*/
public EditPartRepresentation getTarget() {
return this.target;
}
/**
* Returns the command to align the node with a common link
*
* @return the command to align the node with a common link
*/
public Command getCommand() {
initializeReferences();
buildRequest();
CompoundCommand command = new CompoundCommand("Align with a link"); //$NON-NLS-1$
Command cmd;
// the command to align the node
cmd = movedEditPart.getCommand();
if(cmd != null && cmd.canExecute()) {
command.add(cmd);
}
// the command to reset the anchor location
cmd = preserveAnchorLocationCommand();
if(cmd.canExecute()) {
command.add(cmd);
}
// we want zero bendpoint, and keep the current source and target
Request onlyTwoBendpoints = new SetAllBendpointRequest(org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants.REQ_SET_ALL_BENDPOINT, new PointList(), null, null);
command.add(link.getCommand(onlyTwoBendpoints));
return command;
}
/**
* Builds the requests for this {@link LinkRepresentation}
*/
protected void buildRequest() {
int shift = 0;
PrecisionRectangle absolutePosition = movedEditPart.getPosition();
PrecisionPoint oldLocation = new PrecisionPoint(absolutePosition.getTopLeft());
PrecisionPoint newLocation = new PrecisionPoint(oldLocation);
/* we calculate the shift for the editpart */
switch(this.align) {
case PositionConstants.LEFT:// it's the left figure we move
shift = referencePoint.y - movedPoint.y;
movedPoint.y += shift;
newLocation.setLocation(newLocation.x, newLocation.y + shift);
break;
case PositionConstants.RIGHT:// it's the right figure we move
shift = referencePoint.y - movedPoint.y;
movedPoint.y += shift;
newLocation.setLocation(newLocation.x, newLocation.y + shift);
break;
case PositionConstants.TOP:// it's the top figure we move
shift = referencePoint.x - movedPoint.x;
movedPoint.x += shift;
newLocation.setLocation(newLocation.x + shift, newLocation.y);
break;
case PositionConstants.BOTTOM:// it's the bottom figure we move
shift = referencePoint.x - movedPoint.x;
movedPoint.x += shift;
newLocation.setLocation(newLocation.x + shift, newLocation.y);
break;
}
// we correct the location to avoid the scrollbar!
// the parent bounds
PrecisionRectangle parentBounds = LayoutUtils.getAbsolutePosition(movedEditPart.getRepresentedEditPart().getParent());
if(this.align == PositionConstants.LEFT || this.align == PositionConstants.RIGHT) {
if((newLocation.y < (parentBounds.y + LayoutUtils.scrollBarSize)) && (shift < 0)) {
newLocation.setLocation(newLocation.x, parentBounds.y + (int)LayoutUtils.scrollBarSize);
} else if(((newLocation.y + absolutePosition.height) > parentBounds.getBottomLeft().y) && (shift > 0)) {
newLocation.setLocation(newLocation.x, parentBounds.getBottomLeft().y - ((int)LayoutUtils.scrollBarSize + absolutePosition.height));
}
} else if(this.align == PositionConstants.TOP || this.align == PositionConstants.BOTTOM) {
if((newLocation.x < (parentBounds.x + LayoutUtils.scrollBarSize)) && (shift < 0)) {
newLocation.setLocation(parentBounds.x + (int)LayoutUtils.scrollBarSize, newLocation.y);
} else if(((newLocation.x + absolutePosition.width) > parentBounds.getBottomRight().x) && (shift > 0)) {
newLocation.setLocation(parentBounds.getBottomRight().x - ((int)LayoutUtils.scrollBarSize + absolutePosition.width), newLocation.y);
}
}
// request creation
ChangeBoundsRequest req = new ChangeBoundsRequest(RequestConstants.REQ_MOVE);
req.setEditParts(movedEditPart.getRepresentedEditPart());
Dimension delta = newLocation.getDifference(oldLocation);
req.setMoveDelta(new Point(delta.width, delta.height));
req.setSizeDelta(new Dimension(0, 0));
movedEditPart.setRequest(req);
movedEditPart.setMoveDelta(delta);
}
/**
* This method initializes the following fields :
* <ul>
* <li> {@link #fixedEditPart}</li>
* <li> {@link #movedEditPart}</li>
* <li> {@link #referencePoint}</li>
* <li> {@link #movedPoint}</li>
* </ul>
*
* It determines the reference point and the reference editpart for the
* alignment
*/
protected void initializeReferences() {
PrecisionRectangle absolutePositionSource = LayoutUtils.getAbsolutePosition(source.getRepresentedEditPart());
PrecisionRectangle absolutePositionTarget = LayoutUtils.getAbsolutePosition(target.getRepresentedEditPart());
switch(this.align) {
case PositionConstants.LEFT:
if(absolutePositionSource.preciseX < absolutePositionTarget.preciseX) {// the
// left
// node
// moves
fixedEditPart = target;
} else {
fixedEditPart = source;
}
break;
case PositionConstants.RIGHT:
if(absolutePositionSource.preciseX > absolutePositionTarget.preciseX) {// the
// right
// node
// moves
fixedEditPart = target;
} else {
fixedEditPart = source;
}
break;
case PositionConstants.TOP:
if(absolutePositionSource.preciseY < absolutePositionTarget.preciseY) {// the
// top
// node
// moves
fixedEditPart = target;
} else {
fixedEditPart = source;
}
break;
case PositionConstants.BOTTOM:
if(absolutePositionSource.preciseY > absolutePositionTarget.preciseY) {// the
// bottom
// node
// moves
fixedEditPart = target;
} else {
fixedEditPart = source;
}
break;
}
assert (fixedEditPart != null);
movedEditPart = (source == fixedEditPart) ? target : source;
PolylineConnectionEx figure = (PolylineConnectionEx)((AbstractConnectionEditPart)link).getFigure();
if(fixedEditPart == source) {
referencePoint = figure.getStart();
movedPoint = figure.getEnd();
} else {
referencePoint = figure.getEnd();
movedPoint = figure.getStart();
}
// we move the reference point with its delta. This delta comes from
// another shift, were the editpart was the moved editpart and not the
// reference
Dimension delta = fixedEditPart.getMoveDelta();
referencePoint.translate(delta);
figure.translateToAbsolute(referencePoint);
figure.translateToAbsolute(movedPoint);
}
/**
* Returns the side on which the anchor is on the {@link #source}
*
* @return the side on which the anchor is on the {@link #source}
*/
public int getLinkSideOnSource() {
PolylineConnectionEx figure = (PolylineConnectionEx)((AbstractConnectionEditPart)link).getFigure();
Point sourcePoint = figure.getStart();
figure.translateToAbsolute(sourcePoint);
return LayoutUtils.getAnchorPosition(source.getRepresentedEditPart(), sourcePoint);
}
/**
* Returns the side on which the anchor is on the {@link #target}
*
* @return the side on which the anchor is on the {@link #target}
*/
public int getLinkSideOnTarget() {
PolylineConnectionEx figure = (PolylineConnectionEx)((AbstractConnectionEditPart)link).getFigure();
Point targetPoint = figure.getEnd();
figure.translateToAbsolute(targetPoint);
return LayoutUtils.getAnchorPosition(target.getRepresentedEditPart(), targetPoint);
}
/**
* Creates and returns the command to reset the connection anchors after
* alignment to their initial location
*
*
*
* @return the command to reset the connection anchors to their initial
* location after the node shifting
*
*/
protected Command preserveAnchorLocationCommand() {
CompoundCommand command = new CompoundCommand("Preserve Anchor"); //$NON-NLS-1$
Command cmd;
if(movedEditPart == source) {
// the moved editpart is the source
ReconnectRequest reconnectRequest = new ReconnectRequest();
reconnectRequest.setType(GraphicalNodeEditPolicy.REQ_RECONNECT_SOURCE);
reconnectRequest.setConnectionEditPart((ConnectionEditPart)this.link);
reconnectRequest.setTargetEditPart(source.getRepresentedEditPart());
reconnectRequest.setLocation(movedPoint.getTranslated(-movedEditPart.getMoveDelta().width, -movedEditPart.getMoveDelta().height));
cmd = source.getRepresentedEditPart().getCommand(reconnectRequest);
command.add(cmd);
// the fixed editpart is the target
ReconnectRequest reconnectRequestTarget = new ReconnectRequest();
reconnectRequestTarget.setType(GraphicalNodeEditPolicy.REQ_RECONNECT_TARGET);
reconnectRequestTarget.setConnectionEditPart((ConnectionEditPart)this.link);
reconnectRequestTarget.setTargetEditPart(target.getRepresentedEditPart());
reconnectRequestTarget.setLocation(referencePoint.getTranslated(-fixedEditPart.getMoveDelta().width, -fixedEditPart.getMoveDelta().height));
cmd = target.getRepresentedEditPart().getCommand(reconnectRequestTarget);
command.add(cmd);
} else {
// the moved editpart is the target
ReconnectRequest reconnectRequest = new ReconnectRequest();
reconnectRequest.setType(GraphicalNodeEditPolicy.REQ_RECONNECT_TARGET);
reconnectRequest.setConnectionEditPart((ConnectionEditPart)this.link);
reconnectRequest.setTargetEditPart(target.getRepresentedEditPart());
reconnectRequest.setLocation(movedPoint.getTranslated(-movedEditPart.getMoveDelta().width, -movedEditPart.getMoveDelta().height));
cmd = target.getRepresentedEditPart().getCommand(reconnectRequest);
command.add(cmd);
// the fixed editpart is the source
ReconnectRequest reconnectRequestTarget = new ReconnectRequest();
reconnectRequestTarget.setType(GraphicalNodeEditPolicy.REQ_RECONNECT_SOURCE);
reconnectRequestTarget.setConnectionEditPart((ConnectionEditPart)this.link);
reconnectRequestTarget.setTargetEditPart(source.getRepresentedEditPart());
reconnectRequestTarget.setLocation(referencePoint.getTranslated(-fixedEditPart.getMoveDelta().width, -fixedEditPart.getMoveDelta().height));
cmd = source.getRepresentedEditPart().getCommand(reconnectRequestTarget);
command.add(cmd);
}
return command;
}
}