/*
Violet - A program for editing UML diagrams.
Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com)
Alexandre de Pellegrin (http://alexdp.free.fr);
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.horstmann.violet.product.diagram.state.edge;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.JLabel;
import com.horstmann.violet.product.diagram.abstracts.Direction;
import com.horstmann.violet.product.diagram.abstracts.edge.ShapeEdge;
import com.horstmann.violet.product.diagram.abstracts.node.INode;
import com.horstmann.violet.product.diagram.property.ArrowheadChoiceList;
import com.horstmann.violet.product.diagram.property.text.SingleLineText;
import com.horstmann.violet.product.diagram.state.StateDiagramConstant;
/**
* A curved edge for a state transition in a state diagram.
*/
public class StateTransitionEdge extends ShapeEdge
{
public StateTransitionEdge()
{
super();
}
protected StateTransitionEdge(StateTransitionEdge cloned)
{
}
@Override
protected StateTransitionEdge copy() throws CloneNotSupportedException
{
return new StateTransitionEdge(this);
}
@Override
public String getToolTip()
{
return StateDiagramConstant.STATE_DIAGRAM_RESOURCE.getString("tooltip.state_edge");
}
/**
* Sets the label property value.
*
* @param newValue the new value
*/
public void setLabel(SingleLineText newValue)
{
labelText = newValue;
}
/**
* Gets the label property value.
*
* @return the current value
*/
public SingleLineText getLabel()
{
return labelText;
}
public void draw(Graphics2D g2)
{
g2.draw(getShape());
drawLabel(g2);
ArrowheadChoiceList.V.draw(g2, getControlPoint(), getConnectionPoints().getP2());
}
/**
* Draws the label.
*
* @param g2 the graphics context
*/
private void drawLabel(Graphics2D g2)
{
Rectangle2D labelBounds = getLabelBounds();
double x = labelBounds.getX();
double y = labelBounds.getY();
g2.translate(x, y);
label.paint(g2);
g2.translate(-x, -y);
}
/**
* Gets the bounds of the label text
*
* @param g2 the graphics context
* @return the bounds of the label text
*/
private Rectangle2D getLabelBounds()
{
BufferedImage dummy = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
// need a dummy image to get a Graphics to
// measure the size
Graphics2D g2 = (Graphics2D) dummy.getGraphics();
label.setText("<html>" + labelText + "</html>");
label.setFont(g2.getFont());
Dimension d = label.getPreferredSize();
label.setBounds(0, 0, d.width, d.height);
Line2D line = getConnectionPoints();
Point2D control = getControlPoint();
double x = control.getX() / 2 + line.getX1() / 4 + line.getX2() / 4;
double y = control.getY() / 2 + line.getY1() / 4 + line.getY2() / 4;
final int GAP = 3;
if (line.getY1() == line.getY2()) x -= d.getWidth() / 2;
else if (line.getY1() <= line.getY2()) x += GAP;
else x -= d.getWidth() + GAP;
if (line.getX1() == line.getX2()) y += d.getHeight() / 2;
else if (line.getX1() <= line.getX2()) y -= d.getHeight() + GAP;
else y += GAP;
if (Math.abs(line.getX1() - line.getX2()) >= Math.abs(line.getY1() - line.getY2()))
{
x = x - d.getWidth() / 2;
}
if (Math.abs(line.getX1() - line.getX2()) <= Math.abs(line.getY1() - line.getY2()))
{
y = y - d.getHeight() / 2;
}
return new Rectangle2D.Double(x, y, d.width, d.height);
}
/**
* Gets the control point for the quadratic spline.
*
* @return the control point
*/
private Point2D getControlPoint()
{
Line2D line = getConnectionPoints();
double t = Math.tan(Math.toRadians(angle));
double dx = (line.getX2() - line.getX1()) / 2;
double dy = (line.getY2() - line.getY1()) / 2;
return new Point2D.Double((line.getX1() + line.getX2()) / 2 + t * dy, (line.getY1() + line.getY2()) / 2 - t * dx);
}
public Shape getShape()
{
Line2D line = getConnectionPoints();
Point2D control = getControlPoint();
GeneralPath p = new GeneralPath();
p.moveTo((float) line.getX1(), (float) line.getY1());
p.quadTo((float) control.getX(), (float) control.getY(), (float) line.getX2(), (float) line.getY2());
return p;
}
@Override
public Rectangle2D getBounds()
{
Rectangle2D r = super.getBounds();
r.add(getLabelBounds());
return r;
}
@Override
public Direction getDirection(INode node)
{
if (getStartNode() == getEndNode())
{
angle = 60;
if (node.equals(getStartNode())) return Direction.EAST.turn(-30);
if (node.equals(getEndNode())) return Direction.EAST.turn(30);
}
angle = 10;
return super.getDirection(node);
}
private double angle;
private SingleLineText labelText = new SingleLineText();
private static JLabel label = new JLabel();
}