1: import java.awt.Color;
2: import java.awt.Dimension;
3: import java.awt.Graphics2D;
4: import java.awt.Shape;
5: import java.awt.Stroke;
6: import java.awt.geom.GeneralPath;
7: import java.awt.geom.Line2D;
8: import java.awt.geom.Point2D;
9: import java.awt.geom.Rectangle2D;
10: import java.util.ArrayList;
12: import javax.swing.JLabel;
14: /**
15: An edge that is composed of multiple line segments
16: */
17: public abstract class SegmentedLineEdge extends ShapeEdge
18: {
19: /**
20: Costructs an edge with no adornments.
21: */
22: public SegmentedLineEdge()
23: {
24: lineStyle = LineStyle.SOLID;
25: startArrowHead = ArrowHead.NONE;
26: endArrowHead = ArrowHead.NONE;
27: startLabel = "";
28: middleLabel = "";
29: endLabel = "";
30: }
32: /**
33: Sets the line style property.
34: @param newValue the new value
35: */
36: public void setLineStyle(LineStyle newValue) { lineStyle = newValue; }
38: /**
39: Gets the line style property.
40: @return the line style
41: */
42: public LineStyle getLineStyle() { return lineStyle; }
44: /**
45: Sets the start arrow head property
46: @param newValue the new value
47: */
48: public void setStartArrowHead(ArrowHead newValue) { startArrowHead = newValue; }
50: /**
51: Gets the start arrow head property
52: @return the start arrow head style
53: */
54: public ArrowHead getStartArrowHead() { return startArrowHead; }
56: /**
57: Sets the end arrow head property
58: @param newValue the new value
59: */
60: public void setEndArrowHead(ArrowHead newValue) { endArrowHead = newValue; }
62: /**
63: Gets the end arrow head property
64: @return the end arrow head style
65: */
66: public ArrowHead getEndArrowHead() { return endArrowHead; }
68: /**
69: Sets the start label property
70: @param newValue the new value
71: */
72: public void setStartLabel(String newValue) { startLabel = newValue; }
74: /**
75: Gets the start label property
76: @return the label at the start of the edge
77: */
78: public String getStartLabel() { return startLabel; }
80: /**
81: Sets the middle label property
82: @param newValue the new value
83: */
84: public void setMiddleLabel(String newValue) { middleLabel = newValue; }
86: /**
87: Gets the middle label property
88: @return the label at the middle of the edge
89: */
90: public String getMiddleLabel() { return middleLabel; }
92: /**
93: Sets the end label property
94: @param newValue the new value
95: */
96: public void setEndLabel(String newValue) { endLabel = newValue; }
98: /**
99: Gets the end label property
100: @return the label at the end of the edge
101: */
102: public String getEndLabel() { return endLabel; }
104: /**
105: Draws the edge.
106: @param g2 the graphics context
107: */
108: public void draw(Graphics2D g2)
109: {
110: ArrayList points = getPoints();
111:
112: Stroke oldStroke = g2.getStroke();
113: g2.setStroke(lineStyle.getStroke());
114: g2.draw(getSegmentPath());
115: g2.setStroke(oldStroke);
116: startArrowHead.draw(g2, (Point2D)points.get(1),
117: (Point2D)points.get(0));
118: endArrowHead.draw(g2, (Point2D)points.get(points.size() - 2),
119: (Point2D)points.get(points.size() - 1));
121: drawString(g2, (Point2D)points.get(1), (Point2D)points.get(0),
122: startArrowHead, startLabel, false);
123: drawString(g2, (Point2D)points.get(points.size() / 2 - 1),
124: (Point2D)points.get(points.size() / 2),
125: null, middleLabel, true);
126: drawString(g2, (Point2D)points.get(points.size() - 2),
127: (Point2D)points.get(points.size() - 1),
128: endArrowHead, endLabel, false);
129: }
131: /**
132: Draws a string.
133: @param g2 the graphics context
134: @param p an endpoint of the segment along which to
135: draw the string
136: @param q the other endpoint of the segment along which to
137: draw the string
138: @param s the string to draw
139: @param center true if the string should be centered
140: along the segment
141: */
142: private static void drawString(Graphics2D g2,
143: Point2D p, Point2D q, ArrowHead arrow, String s, boolean center)
144: {
145: if (s == null || s.length() == 0) return;
146: label.setText("<html>" + s + "</html>");
147: label.setFont(g2.getFont());
148: Dimension d = label.getPreferredSize();
149: label.setBounds(0, 0, d.width, d.height);
151: Rectangle2D b = getStringBounds(g2, p, q, arrow, s, center);
152:
153: Color oldColor = g2.getColor();
154: g2.setColor(g2.getBackground());
155: g2.fill(b);
156: g2.setColor(oldColor);
157:
158: g2.translate(b.getX(), b.getY());
159: label.paint(g2);
160: g2.translate(-b.getX(), -b.getY());
161: }
163: /**
164: Computes the attachment point for drawing a string.
165: @param g2 the graphics context
166: @param p an endpoint of the segment along which to
167: draw the string
168: @param q the other endpoint of the segment along which to
169: draw the string
170: @param b the bounds of the string to draw
171: @param center true if the string should be centered
172: along the segment
173: @return the point at which to draw the string
174: */
175: private static Point2D getAttachmentPoint(Graphics2D g2,
176: Point2D p, Point2D q, ArrowHead arrow, Dimension d, boolean center)
177: {
178: final int GAP = 3;
179: double xoff = GAP;
180: double yoff = -GAP - d.getHeight();
181: Point2D attach = q;
182: if (center)
183: {
184: if (p.getX() > q.getX())
185: {
186: return getAttachmentPoint(g2, q, p, arrow, d, center);
187: }
188: attach = new Point2D.Double((p.getX() + q.getX()) / 2,
189: (p.getY() + q.getY()) / 2);
190: if (p.getY() < q.getY())
191: yoff = - GAP - d.getHeight();
192: else if (p.getY() == q.getY())
193: xoff = -d.getWidth() / 2;
194: else
195: yoff = GAP;
196: }
197: else
198: {
199: if (p.getX() < q.getX())
200: {
201: xoff = -GAP - d.getWidth();
202: }
203: if (p.getY() > q.getY())
204: {
205: yoff = GAP;
206: }
207: if (arrow != null)
208: {
209: Rectangle2D arrowBounds = arrow.getPath(p, q).getBounds2D();
210: if (p.getX() < q.getX())
211: {
212: xoff -= arrowBounds.getWidth();
213: }
214: else
215: {
216: xoff += arrowBounds.getWidth();
217: }
218: }
219: }
220: return new Point2D.Double(attach.getX() + xoff, attach.getY() + yoff);
221: }
223: /**
224: Computes the extent of a string that is drawn along a line segment.
225: @param g2 the graphics context
226: @param p an endpoint of the segment along which to
227: draw the string
228: @param q the other endpoint of the segment along which to
229: draw the string
230: @param s the string to draw
231: @param center true if the string should be centered
232: along the segment
233: @return the rectangle enclosing the string
234: */
235: private static Rectangle2D getStringBounds(Graphics2D g2,
236: Point2D p, Point2D q, ArrowHead arrow, String s, boolean center)
237: {
238: if (g2 == null) return new Rectangle2D.Double();
239: if (s == null || s.equals("")) return new Rectangle2D.Double(q.getX(), q.getY(), 0, 0);
240: label.setText("<html>" + s + "</html>");
241: label.setFont(g2.getFont());
242: Dimension d = label.getPreferredSize();
243: Point2D a = getAttachmentPoint(g2, p, q, arrow, d, center);
244: return new Rectangle2D.Double(a.getX(), a.getY(), d.getWidth(), d.getHeight());
245: }
247: public Rectangle2D getBounds(Graphics2D g2)
248: {
249: ArrayList points = getPoints();
250: Rectangle2D r = super.getBounds(g2);
251: r.add(getStringBounds(g2,
252: (Point2D)points.get(1), (Point2D)points.get(0),
253: startArrowHead, startLabel, false));
254: r.add(getStringBounds(g2,
255: (Point2D)points.get(points.size() / 2 - 1),
256: (Point2D)points.get(points.size() / 2),
257: null, middleLabel, true));
258: r.add(getStringBounds(g2,
259: (Point2D)points.get(points.size() - 2),
260: (Point2D)points.get(points.size() - 1),
261: endArrowHead, endLabel, false));
262: return r;
263: }
265: public Shape getShape()
266: {
267: GeneralPath path = getSegmentPath();
268: ArrayList points = getPoints();
269: path.append(startArrowHead.getPath((Point2D)points.get(1),
270: (Point2D)points.get(0)), false);
271: path.append(endArrowHead.getPath((Point2D)points.get(points.size() - 2),
272: (Point2D)points.get(points.size() - 1)), false);
273: return path;
274: }
276: private GeneralPath getSegmentPath()
277: {
278: ArrayList points = getPoints();
279:
280: GeneralPath path = new GeneralPath();
281: Point2D p = (Point2D) points.get(points.size() - 1);
282: path.moveTo((float) p.getX(), (float) p.getY());
283: for (int i = points.size() - 2; i >= 0; i--)
284: {
285: p = (Point2D) points.get(i);
286: path.lineTo((float) p.getX(), (float) p.getY());
287: }
288: return path;
289: }
290:
291: public Line2D getConnectionPoints()
292: {
293: ArrayList points = getPoints();
294: return new Line2D.Double((Point2D) points.get(0),
295: (Point2D) points.get(points.size() - 1));
296: }
298: /**
299: Gets the corner points of this segmented line edge
300: @return an array list of Point2D objects, containing
301: the corner points
302: */
303: public abstract ArrayList getPoints();
305: private LineStyle lineStyle;
306: private ArrowHead startArrowHead;
307: private ArrowHead endArrowHead;
308: private String startLabel;
309: private String middleLabel;
310: private String endLabel;
311:
312: private static JLabel label = new JLabel();
313: }