/*******************************************************************************
* 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
* Exadel, Inc.
* Red Hat, Inc.
*******************************************************************************/
package org.jboss.tools.common.gef.figures.xpl;
import org.eclipse.draw2d.Connection;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Locator;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.draw2d.geometry.Transposer;
/**
* Used to place IFigures along the endpoint or starting point of a {@link Connection}.
* <code>uDistance</code> represents the distance from the Connection's owner to the
* IFigure. <code>vDistance</code> represents the distance from the IFigure to the
* Connection itself.
*/
public class CustomLocator implements Locator
{
private boolean end;
private Connection conn;
private int uDistance;
private int vDistance;
private static Rectangle figureBounds;
/**
* Transposes the location if the connection point is along the top or bottom of its owner
* figure.
*/
protected Transposer transposer = new Transposer();
/**
* Constructs a ConnectionEndpointLocator using the given {@link Connection}. If
* <i>isEnd</i> is <code>true</code>, the location is relative to the Connection's end (or
* target) point. If <i>isEnd</i> is <code>false</code>, the location is relative to the
* Connection's start (or source) point.
*
* @param c The Connection
* @param isEnd <code>true</code> is location is relative to end point
* @since 2.0
*/
public CustomLocator(Connection c, boolean isEnd) {
end = isEnd;
conn = c;
uDistance = 14;
vDistance = 4;
figureBounds = new Rectangle();
}
/*
* Returns an integer representing the side of the passed Rectangle that a point lies on.
* 1 == Top
* 2 == Right
* 3 == Bottom
* 4 == Left
*
* @param loc The point that is to be located
*/
private int calculateConnectionLocation(Point loc, Point topLeft, Point center) {
double m1, m2 = 0;
m1 = (double)(topLeft.y - center.y) / (double)(topLeft.x - center.x);
if (loc.x - center.x != 0)
m2 = (double)(loc.y - center.y) / (double)(loc.x - center.x);
if (loc.x == center.x) { // Case where m2 is vertical
if (loc.y < center.y)
return 3;
else
return 1;
} else if (Math.abs(m2) <= Math.abs(m1)) { // Connection start point along left or right side
if (loc.x < center.x)
return 4;
else
return 2;
} else { // Connection start point along top or bottom
if (loc.y < center.y)
return 3;
else
return 1;
}
}
/*
* This method is used to calculate the "quadrant" value of a connection that does not
* have an owner on its starting point.
*
* 1 == Top
* 2 == Right
* 3 == Bottom
* 4 == Left
*
* @param startPoint The starting point of the connection.
* @param endPoint The end point of the connection.
*/
private int calculateConnectionLocation(Point startPoint, Point endPoint) {
if (Math.abs(endPoint.x - startPoint.x) > Math.abs(endPoint.x - startPoint.x)) {
if (endPoint.x > startPoint.x)
return 2;
else
return 4;
} else {
if (endPoint.y > startPoint.y)
return 1;
else
return 3;
}
}
/*
* Calculates 'tan' which is used as a factor for y adjustment when placing the connection
* label. 'tan' is capped at 1.0 in the positive direction and -1.0 in the negative
* direction.
*
* @param startPoint The starting point of the connection.
* @param endPoint The end point of the connection.
* @since 2.0
*/
private double calculateTan(Point startPoint, Point endPoint) {
double tan = 0;
if (endPoint.x == startPoint.x)
tan = 1.0;
else
tan = (double)(endPoint.y - startPoint.y) / (double)(endPoint.x - startPoint.x);
if (tan > 1)
tan = 1.0;
else if (tan < -1)
tan = -1.0;
return tan;
}
private int calculateYShift(int figureWidth, int figureHeight) {
int yShift = 0;
if (vDistance < 0)
yShift = -figureHeight;
else if (vDistance == 0)
yShift = -figureHeight / 2;
return 0;//yShift;
}
private Connection getConnection() {
return conn;
}
private IFigure getConnectionOwner() {
IFigure connOwner;
if (isEnd()){
if(conn.getTargetAnchor() == null) return null;
connOwner = conn.getTargetAnchor().getOwner();
}else{
if(conn.getSourceAnchor() == null) return null;
connOwner = conn.getSourceAnchor().getOwner();
}
return connOwner;
}
private int getUDistance() {
return uDistance;
}
private int getVDistance() {
return vDistance;
}
private boolean isEnd() {
return end;
}
/**
* Relocates the given IFigure at either the source or target end of the Connection,
* based on the <code>boolean</code> given in the constructor
* {@link #ConnectionEndpointLocator(Connection, boolean)}.
*
* @param figure The figure to relocate
*/
public void relocate(IFigure figure) {
Connection conn = getConnection();
Point startPoint = Point.SINGLETON;
Point endPoint = new Point();
int startPointPosition = 0;
int endPointPosition = 1;
if (isEnd()) {
startPointPosition = conn.getPoints().size() - 1;
endPointPosition = startPointPosition - 1;
}
conn.getPoints().getPoint(startPoint, startPointPosition);
conn.getPoints().getPoint(endPoint, endPointPosition);
IFigure connOwner = getConnectionOwner();
int quadrant;
if (connOwner != null) {
Rectangle connOwnerBounds = connOwner.getBounds();
Point connOwnerCenter = connOwnerBounds.getCenter();
Point connOwnerTL = connOwnerBounds.getTopLeft();
quadrant = calculateConnectionLocation(startPoint, connOwnerTL, connOwnerCenter);
} else
quadrant = calculateConnectionLocation(startPoint, endPoint);
int cos = 1;
transposer.setEnabled(false);
/*
* Label placement calculations are done as if the connection point is along the left
* or right side of the figure. If the connection point is along the top or bottom,
* values are transposed.
*/
if (quadrant == 1 || quadrant == 3)
transposer.setEnabled(true);
if (quadrant == 3 || quadrant == 4)
cos = -1;
Dimension figureSize = transposer.t(figure.getPreferredSize());
startPoint = transposer.t(startPoint);
endPoint = transposer.t(endPoint);
double tan = calculateTan(startPoint, endPoint);
int figureWidth = figureSize.width;
int figureHeight = figureSize.height;
int yShift = calculateYShift(figureWidth, figureHeight);
Point figurePoint =
new Point((int)(startPoint.x + (uDistance * cos) + figureWidth * ((cos - 1) / 2)),
(int)(startPoint.y + cos * uDistance * tan + vDistance + yShift));
figureBounds.setSize(transposer.t(figureSize));
figureBounds.setLocation(transposer.t(figurePoint));
figure.setBounds(figureBounds);
}
/**
* Sets the distance in pixels from the Connection's owner.
*
* @param distance Number of pixels to place the ConnectionEndpointLocator from its owner.
* @since 2.0
*/
public void setUDistance(int distance) {
uDistance = distance;
}
/**
* Sets the distance in pixels from the Connection.
*
* @param distance Number of pixels to place the ConnectionEndpointLocator from its
* Connection.
* @since 2.0
*/
public void setVDistance(int distance) {
vDistance = distance;
}
}