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: }