/*****************************************************************************
* Copyright (c) 2010 Atos Origin.
*
*
* 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:
* Atos Origin - Initial API and implementation
*
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.activity.edit.policies;
import java.util.Collections;
import java.util.List;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PolylineShape;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.PointList;
import org.eclipse.draw2d.geometry.PrecisionRectangle;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.handles.MoveHandle;
import org.eclipse.gef.requests.AlignmentRequest;
import org.eclipse.gef.requests.ChangeBoundsRequest;
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.commands.SetBoundsCommand;
import org.eclipse.gmf.runtime.diagram.ui.editparts.AbstractBorderedShapeEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.NonResizableEditPolicyEx;
import org.eclipse.gmf.runtime.diagram.ui.figures.IBorderItemLocator;
import org.eclipse.gmf.runtime.diagram.ui.internal.figures.LabelHelper;
import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages;
import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants;
import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter;
import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure;
import org.eclipse.gmf.runtime.notation.View;
public class BehaviorPropertyNodeEditPolicy extends NonResizableEditPolicyEx {
private PolylineShape linkFeedback = null;
/**
* @see org.eclipse.gef.editpolicies.NonResizableEditPolicy#createSelectionHandles()
*/
protected List createSelectionHandles() {
MoveHandle mh = new MoveHandle((GraphicalEditPart)getHost());
mh.setBorder(null);
return Collections.singletonList(mh);
}
/**
* Calls other methods as appropriate.
*/
public void eraseSourceFeedback(Request request) {
if((REQ_MOVE.equals(request.getType()) && isDragAllowed()) || REQ_CLONE.equals(request.getType()) || REQ_ADD.equals(request.getType()) || RequestConstants.REQ_DROP.equals(request.getType()))
eraseChangeBoundsFeedback((ChangeBoundsRequest)request);
}
/**
* Calls other methods as appropriate.
*/
public void showSourceFeedback(Request request) {
if((REQ_MOVE.equals(request.getType()) && isDragAllowed()) || REQ_ADD.equals(request.getType()) || REQ_CLONE.equals(request.getType()) || RequestConstants.REQ_DROP.equals(request.getType()))
showChangeBoundsFeedback((ChangeBoundsRequest)request);
}
/**
* Returns the command contribution to a change bounds request.
*
* @param request
* the change bounds requesgt
* @return the command contribution to the request
*/
protected Command getMoveCommand(ChangeBoundsRequest request) {
// translate the feedback figure
PrecisionRectangle rect = new PrecisionRectangle(getInitialFeedbackBounds().getCopy());
getHostFigure().translateToAbsolute(rect);
rect.translate(request.getMoveDelta());
rect.resize(request.getSizeDelta());
getHostFigure().translateToRelative(rect);
if(getHost() instanceof IBorderItemEditPart) {
IBorderItemEditPart borderItemEP = (IBorderItemEditPart)getHost();
IBorderItemLocator borderItemLocator = borderItemEP.getBorderItemLocator();
if(borderItemLocator != null) {
Rectangle realLocation = borderItemLocator.getValidLocation(rect.getCopy(), borderItemEP.getFigure());
Point parentOrigin = borderItemEP.getFigure().getParent().getBounds().getTopLeft();
Dimension d = realLocation.getTopLeft().getDifference(parentOrigin);
Point location = new Point(d.width, d.height);
ICommand moveCommand = new SetBoundsCommand(borderItemEP.getEditingDomain(), DiagramUIMessages.Commands_MoveElement, new EObjectAdapter((View)getHost().getModel()), location);
return new ICommandProxy(moveCommand);
}
} else if(getHost() instanceof LabelEditPart) {
LabelEditPart editPart = (LabelEditPart)getHost();
Point refPoint = editPart.getReferencePoint();
Point normalPoint = LabelHelper.offsetFromRelativeCoordinate(getHostFigure(), rect, refPoint);
ICommand moveCommand = new SetBoundsCommand(editPart.getEditingDomain(), DiagramUIMessages.Commands_MoveElement, new EObjectAdapter((View)editPart.getModel()), normalPoint);
return new ICommandProxy(moveCommand);
}
return null;
}
/** Return <tt>null</tt> to avoid handling the request. */
protected Command getAlignCommand(AlignmentRequest request) {
return null;
}
/**
* Erase the feedback link figure
*
* @see org.eclipse.gef.editpolicies.NonResizableEditPolicy#eraseChangeBoundsFeedback(org.eclipse.gef.requests.ChangeBoundsRequest)
*/
protected void eraseChangeBoundsFeedback(ChangeBoundsRequest request) {
super.eraseChangeBoundsFeedback(request);
if(linkFeedback != null)
removeFeedback(linkFeedback);
linkFeedback = null;
}
/**
* Create the feedback link figure
*
* @see org.eclipse.gef.editpolicies.NonResizableEditPolicy#createDragSourceFeedbackFigure()
*/
protected IFigure createDragSourceFeedbackFigure() {
IFigure feedback = super.createDragSourceFeedbackFigure();
linkFeedback = new PolylineShape();
linkFeedback.setLineWidth(1);
linkFeedback.setLineStyle(Graphics.LINE_DASHDOT);
linkFeedback.setForegroundColor(((IGraphicalEditPart)getHost()).getFigure().getForegroundColor());
addFeedback(linkFeedback);
return feedback;
}
/**
* Show link feedback
*
* @see org.eclipse.gmf.runtime.diagram.ui.editpolicies.BorderItemSelectionEditPolicy#showChangeBoundsFeedback(org.eclipse.gef.requests.ChangeBoundsRequest)
*/
protected void showChangeBoundsFeedback(org.eclipse.gef.requests.ChangeBoundsRequest request) {
if(getHost() instanceof IBorderItemEditPart) {
IBorderItemEditPart borderItemEP = (IBorderItemEditPart)getHost();
IBorderItemLocator borderItemLocator = borderItemEP.getBorderItemLocator();
if(borderItemLocator != null) {
IFigure feedback = getDragSourceFeedbackFigure();
PrecisionRectangle rect = new PrecisionRectangle(getInitialFeedbackBounds().getCopy());
getHostFigure().translateToAbsolute(rect);
rect.translate(request.getMoveDelta());
rect.resize(request.getSizeDelta());
getHostFigure().translateToRelative(rect);
Rectangle realLocation = borderItemLocator.getValidLocation(rect.getCopy(), borderItemEP.getFigure());
getHostFigure().translateToAbsolute(realLocation);
feedback.translateToRelative(realLocation);
feedback.setBounds(realLocation);
}
} else {
super.showChangeBoundsFeedback(request);
}
// translate the feedback figure
IFigure p = getDragSourceFeedbackFigure();
PrecisionRectangle rect = new PrecisionRectangle(getInitialFeedbackBounds().getCopy());
getHostFigure().translateToAbsolute(rect);
rect.translate(request.getMoveDelta());
rect.resize(request.getSizeDelta());
p.translateToRelative(rect);
p.setBounds(rect);
Point referencePoint = getReferencePoint(request);
Point end = getLinkEndPoint(request, referencePoint);
linkFeedback.setEnd(end);
Point start = getLinkStartPoint(request, referencePoint, end);
if(start != null) {
linkFeedback.setStart(start);
} else {
linkFeedback.setStart(referencePoint);
}
};
/**
* Get the reference point at the center of the parent figure
*
* @param request
* change bounds request
* @return point
*/
private Point getReferencePoint(ChangeBoundsRequest request) {
Point refPoint = ((LabelEditPart)getHost()).getReferencePoint();
Rectangle centerMain = null;
if(((IGraphicalEditPart)getHost().getParent()).getFigure() instanceof Connection) {
centerMain = new Rectangle(refPoint.x, refPoint.y, 0, 0);
} else {
centerMain = ((IGraphicalEditPart)getHost().getParent()).getFigure().getBounds().getCopy();
centerMain.translate(centerMain.width / 2, centerMain.height / 2);
}
PrecisionRectangle ref = new PrecisionRectangle(centerMain);
getHostFigure().translateToAbsolute(ref);
getDragSourceFeedbackFigure().translateToRelative(ref);
return ref.getLocation();
}
/**
* Get the point for starting the link
*
* @param request
* change bounds request
* @param referencePoint
* the reference point at the center of the start figure
* @param endPoint
* the end point at the border of the end figure
* @return point where to start the link
*/
private Point getLinkStartPoint(ChangeBoundsRequest request, Point referencePoint, Point endPoint) {
Point u1 = referencePoint;
Point u2 = endPoint;
if(getHost().getParent() instanceof AbstractBorderedShapeEditPart) {
// the parent figure is a node : choose a point on its border
AbstractBorderedShapeEditPart parentPart = (AbstractBorderedShapeEditPart)getHost().getParent();
IFigure fig = parentPart.getMainFigure();
if(fig instanceof NodeFigure) {
PointList pointsList = ((NodeFigure)fig).getPolygonPoints().getCopy();
// translate points relatively to drag feedback
fig.translateToAbsolute(pointsList);
getDragSourceFeedbackFigure().translateToRelative(pointsList);
return getIntersectionPoint(pointsList, u1, u2);
}
}
// either computation failed or the parent figure is a link
return referencePoint;
}
/**
* Get the point for ending the link
*
* @param request
* change bounds request
* @param referencePoint
* the reference point at the center of the start figure
* @return point
*/
private Point getLinkEndPoint(ChangeBoundsRequest request, Point referencePoint) {
Rectangle rect = getDragSourceFeedbackFigure().getBounds();
Point endPoint = getAppropriateBorderPoint(referencePoint, rect);
return endPoint;
}
/**
* Get the point on the border intersection the segment
*
* @param polygonalBounds
* the list of points tracing the border
* @param insidePoint
* first segment extremity
* @param outsideExtremity
* second segment extremity
* @return the intersection point or null if none
*/
public static Point getIntersectionPoint(PointList polygonalBounds, Point insidePoint, Point outsideExtremity) {
Point intersection = null;
int i = 0;
while(intersection == null && i < polygonalBounds.size() - 1) {
Point v1 = polygonalBounds.getPoint(i).getCopy();
Point v2 = polygonalBounds.getPoint(i + 1).getCopy();
intersection = getIntersection(insidePoint, outsideExtremity, v1, v2);
i++;
}
return intersection;
}
/**
* Get the point of the border which is the more appropriated to link to the
* reference point
*
* @param referencePoint
* point to refer to, which the link is directed to (not
* necessary an end of the link)
* @param border
* the border on which an anchoring point must be chosen
* @return a point of the border to use as link end
*/
public static Point getAppropriateBorderPoint(Point referencePoint, Rectangle border) {
Point midTop = new Point(border.x + border.width / 2, border.y);
Point midBottom = new Point(border.x + border.width / 2, border.y + border.height);
Point midLeft = new Point(border.x, border.y + border.height / 2);
Point midRight = new Point(border.x + border.width, border.y + border.height / 2);
Dimension diffTop = referencePoint.getDifference(midTop);
Dimension diffBottom = referencePoint.getDifference(midBottom);
Dimension diffRight = referencePoint.getDifference(midRight);
Dimension diffLeft = referencePoint.getDifference(midLeft);
Point startPoint = midBottom;
if(diffBottom.height > 0) {
// check if right or left is more appropriated
if(diffLeft.width < 0 && diffBottom.height < -diffLeft.width) {
startPoint = midLeft;
} else if(diffRight.width > 0 && diffBottom.height < diffRight.width) {
startPoint = midRight;
} else {
startPoint = midBottom;
}
} else if(diffTop.height < 0) {
// check if right or left is more appropriated
if(diffLeft.width < 0 && -diffTop.height < -diffLeft.width) {
startPoint = midLeft;
} else if(diffRight.width > 0 && -diffTop.height < diffRight.width) {
startPoint = midRight;
} else {
startPoint = midTop;
}
} else if(diffLeft.width < 0) {
startPoint = midLeft;
} else if(diffRight.width > 0) {
startPoint = midRight;
}
return startPoint;
}
/**
* Get the intersection between segments joining the points
*
* @param u1
* the first extremity of the u segment
* @param u2
* the second extremity of the u segment
* @param v1
* the first extremity of the v segment
* @param v2
* the second extremity of the v segment
* @return the intersection point or null if none
*/
public static Point getIntersection(Point u1, Point u2, Point v1, Point v2) {
float denom = ((u2.y - u1.y) * (v2.x - v1.x)) - ((u2.x - u1.x) * (v2.y - v1.y));
float nume_a = ((u2.x - u1.x) * (v1.y - u1.y)) - ((u2.y - u1.y) * (v1.x - u1.x));
float nume_b = ((v2.x - v1.x) * (v1.y - u1.y)) - ((v2.y - v1.y) * (v1.x - u1.x));
if(denom == 0.0f) {
return null;
}
float ua = nume_a / denom;
float ub = nume_b / denom;
if(ua >= 0.0f && ua <= 1.0f && ub >= 0.0f && ub <= 1.0f) {
// Get the intersection point.
Float x = v1.x + ua * (v2.x - v1.x);
Float y = v1.y + ua * (v2.y - v1.y);
return new Point(x.intValue(), y.intValue());
}
return null;
}
}