/*
Violet - A program for editing UML diagrams.
Copyright (C) 2002 Cay S. Horstmann (http://horstmann.com)
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.framework;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.JLabel;
import com.horstmann.violet.ArrowHead;
import com.horstmann.violet.LineStyle;
/**
An edge that is composed of multiple line segments
*/
public abstract class SegmentedLineEdge extends ShapeEdge
{
/**
Costructs an edge with no adornments.
*/
public SegmentedLineEdge()
{
lineStyle = LineStyle.SOLID;
startArrowHead = ArrowHead.NONE;
endArrowHead = ArrowHead.NONE;
startLabel = "";
middleLabel = "";
endLabel = "";
}
/**
Sets the line style property.
@param newValue the new value
*/
public void setLineStyle(LineStyle newValue) { lineStyle = newValue; }
/**
Gets the line style property.
@return the line style
*/
public LineStyle getLineStyle() { return lineStyle; }
/**
Sets the start arrow head property
@param newValue the new value
*/
public void setStartArrowHead(ArrowHead newValue) { startArrowHead = newValue; }
/**
Gets the start arrow head property
@return the start arrow head style
*/
public ArrowHead getStartArrowHead() { return startArrowHead; }
/**
Sets the end arrow head property
@param newValue the new value
*/
public void setEndArrowHead(ArrowHead newValue) { endArrowHead = newValue; }
/**
Gets the end arrow head property
@return the end arrow head style
*/
public ArrowHead getEndArrowHead() { return endArrowHead; }
/**
Sets the start label property
@param newValue the new value
*/
public void setStartLabel(String newValue) { startLabel = newValue; }
/**
Gets the start label property
@return the label at the start of the edge
*/
public String getStartLabel() { return startLabel; }
/**
Sets the middle label property
@param newValue the new value
*/
public void setMiddleLabel(String newValue) { middleLabel = newValue; }
/**
Gets the middle label property
@return the label at the middle of the edge
*/
public String getMiddleLabel() { return middleLabel; }
/**
Sets the end label property
@param newValue the new value
*/
public void setEndLabel(String newValue) { endLabel = newValue; }
/**
Gets the end label property
@return the label at the end of the edge
*/
public String getEndLabel() { return endLabel; }
/**
Draws the edge.
@param g2 the graphics context
*/
public void draw(Graphics2D g2)
{
ArrayList points = getPoints();
Stroke oldStroke = g2.getStroke();
g2.setStroke(lineStyle.getStroke());
g2.draw(getSegmentPath());
g2.setStroke(oldStroke);
startArrowHead.draw(g2, (Point2D)points.get(1),
(Point2D)points.get(0));
endArrowHead.draw(g2, (Point2D)points.get(points.size() - 2),
(Point2D)points.get(points.size() - 1));
drawString(g2, (Point2D)points.get(1), (Point2D)points.get(0),
startArrowHead, startLabel, false);
drawString(g2, (Point2D)points.get(points.size() / 2 - 1),
(Point2D)points.get(points.size() / 2),
null, middleLabel, true);
drawString(g2, (Point2D)points.get(points.size() - 2),
(Point2D)points.get(points.size() - 1),
endArrowHead, endLabel, false);
}
/**
Draws a string.
@param g2 the graphics context
@param p an endpoint of the segment along which to
draw the string
@param q the other endpoint of the segment along which to
draw the string
@param s the string to draw
@param center true if the string should be centered
along the segment
*/
private static void drawString(Graphics2D g2,
Point2D p, Point2D q, ArrowHead arrow, String s, boolean center)
{
if (s == null || s.length() == 0) return;
label.setText("<html>" + s + "</html>");
label.setFont(g2.getFont());
Dimension d = label.getPreferredSize();
label.setBounds(0, 0, d.width, d.height);
Rectangle2D b = getStringBounds(g2, p, q, arrow, s, center);
Color oldColor = g2.getColor();
g2.setColor(g2.getBackground());
g2.fill(b);
g2.setColor(oldColor);
g2.translate(b.getX(), b.getY());
label.paint(g2);
g2.translate(-b.getX(), -b.getY());
}
/**
Computes the attachment point for drawing a string.
@param g2 the graphics context
@param p an endpoint of the segment along which to
draw the string
@param q the other endpoint of the segment along which to
draw the string
@param b the bounds of the string to draw
@param center true if the string should be centered
along the segment
@return the point at which to draw the string
*/
private static Point2D getAttachmentPoint(Graphics2D g2,
Point2D p, Point2D q, ArrowHead arrow, Dimension d, boolean center)
{
final int GAP = 3;
double xoff = GAP;
double yoff = -GAP - d.getHeight();
Point2D attach = q;
if (center)
{
if (p.getX() > q.getX())
{
return getAttachmentPoint(g2, q, p, arrow, d, center);
}
attach = new Point2D.Double((p.getX() + q.getX()) / 2,
(p.getY() + q.getY()) / 2);
if (p.getY() < q.getY())
yoff = - GAP - d.getHeight();
else if (p.getY() == q.getY())
xoff = -d.getWidth() / 2;
else
yoff = GAP;
}
else
{
if (p.getX() < q.getX())
{
xoff = -GAP - d.getWidth();
}
if (p.getY() > q.getY())
{
yoff = GAP;
}
if (arrow != null)
{
Rectangle2D arrowBounds = arrow.getPath(p, q).getBounds2D();
if (p.getX() < q.getX())
{
xoff -= arrowBounds.getWidth();
}
else
{
xoff += arrowBounds.getWidth();
}
}
}
return new Point2D.Double(attach.getX() + xoff, attach.getY() + yoff);
}
/**
Computes the extent of a string that is drawn along a line segment.
@param g2 the graphics context
@param p an endpoint of the segment along which to
draw the string
@param q the other endpoint of the segment along which to
draw the string
@param s the string to draw
@param center true if the string should be centered
along the segment
@return the rectangle enclosing the string
*/
private static Rectangle2D getStringBounds(Graphics2D g2,
Point2D p, Point2D q, ArrowHead arrow, String s, boolean center)
{
if (g2 == null) return new Rectangle2D.Double();
if (s == null || s.equals("")) return new Rectangle2D.Double(q.getX(), q.getY(), 0, 0);
label.setText("<html>" + s + "</html>");
label.setFont(g2.getFont());
Dimension d = label.getPreferredSize();
Point2D a = getAttachmentPoint(g2, p, q, arrow, d, center);
return new Rectangle2D.Double(a.getX(), a.getY(), d.getWidth(), d.getHeight());
}
public Rectangle2D getBounds(Graphics2D g2)
{
ArrayList points = getPoints();
Rectangle2D r = super.getBounds(g2);
r.add(getStringBounds(g2,
(Point2D) points.get(1), (Point2D) points.get(0),
startArrowHead, startLabel, false));
r.add(getStringBounds(g2,
(Point2D) points.get(points.size() / 2 - 1),
(Point2D) points.get(points.size() / 2),
null, middleLabel, true));
r.add(getStringBounds(g2,
(Point2D) points.get(points.size() - 2),
(Point2D) points.get(points.size() - 1),
endArrowHead, endLabel, false));
return r;
}
public Shape getShape()
{
GeneralPath path = getSegmentPath();
ArrayList points = getPoints();
path.append(startArrowHead.getPath((Point2D)points.get(1),
(Point2D)points.get(0)), false);
path.append(endArrowHead.getPath((Point2D)points.get(points.size() - 2),
(Point2D)points.get(points.size() - 1)), false);
return path;
}
private GeneralPath getSegmentPath()
{
ArrayList points = getPoints();
GeneralPath path = new GeneralPath();
Point2D p = (Point2D) points.get(points.size() - 1);
path.moveTo((float) p.getX(), (float) p.getY());
for (int i = points.size() - 2; i >= 0; i--)
{
p = (Point2D) points.get(i);
path.lineTo((float) p.getX(), (float) p.getY());
}
return path;
}
public Line2D getConnectionPoints()
{
ArrayList points = getPoints();
return new Line2D.Double((Point2D) points.get(0),
(Point2D) points.get(points.size() - 1));
}
/**
Gets the corner points of this segmented line edge
@return an array list of Point2D objects, containing
the corner points
*/
public abstract ArrayList getPoints();
private LineStyle lineStyle;
private ArrowHead startArrowHead;
private ArrowHead endArrowHead;
private String startLabel;
private String middleLabel;
private String endLabel;
private static JLabel label = new JLabel();
}