Frames | No Frames |
1: /* =========================================================== 2: * JFreeChart : a free chart library for the Java(tm) platform 3: * =========================================================== 4: * 5: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jfreechart/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * --------------------- 28: * XYTextAnnotation.java 29: * --------------------- 30: * (C) Copyright 2002-2007, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * Changes: 36: * -------- 37: * 28-Aug-2002 : Version 1 (DG); 38: * 07-Nov-2002 : Fixed errors reported by Checkstyle (DG); 39: * 13-Jan-2003 : Reviewed Javadocs (DG); 40: * 26-Mar-2003 : Implemented Serializable (DG); 41: * 02-Jul-2003 : Added new text alignment and rotation options (DG); 42: * 19-Aug-2003 : Implemented Cloneable (DG); 43: * 17-Jan-2003 : Added fix for bug 878706, where the annotation is placed 44: * incorrectly for a plot with horizontal orientation (thanks to 45: * Ed Yu for the fix) (DG); 46: * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 47: * ------------- JFREECHART 1.0.x --------------------------------------------- 48: * 26-Jan-2006 : Fixed equals() method (bug 1415480) (DG); 49: * 06-Mar-2007 : Added argument checks, re-implemented hashCode() method (DG); 50: * 51: */ 52: 53: package org.jfree.chart.annotations; 54: 55: import java.awt.Color; 56: import java.awt.Font; 57: import java.awt.Graphics2D; 58: import java.awt.Paint; 59: import java.awt.Shape; 60: import java.awt.geom.Rectangle2D; 61: import java.io.IOException; 62: import java.io.ObjectInputStream; 63: import java.io.ObjectOutputStream; 64: import java.io.Serializable; 65: 66: import org.jfree.chart.HashUtilities; 67: import org.jfree.chart.axis.ValueAxis; 68: import org.jfree.chart.plot.Plot; 69: import org.jfree.chart.plot.PlotOrientation; 70: import org.jfree.chart.plot.PlotRenderingInfo; 71: import org.jfree.chart.plot.XYPlot; 72: import org.jfree.io.SerialUtilities; 73: import org.jfree.text.TextUtilities; 74: import org.jfree.ui.RectangleEdge; 75: import org.jfree.ui.TextAnchor; 76: import org.jfree.util.PaintUtilities; 77: import org.jfree.util.PublicCloneable; 78: 79: /** 80: * A text annotation that can be placed at a particular (x, y) location on an 81: * {@link XYPlot}. 82: */ 83: public class XYTextAnnotation extends AbstractXYAnnotation 84: implements Cloneable, PublicCloneable, 85: Serializable { 86: 87: /** For serialization. */ 88: private static final long serialVersionUID = -2946063342782506328L; 89: 90: /** The default font. */ 91: public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 92: 10); 93: 94: /** The default paint. */ 95: public static final Paint DEFAULT_PAINT = Color.black; 96: 97: /** The default text anchor. */ 98: public static final TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.CENTER; 99: 100: /** The default rotation anchor. */ 101: public static final TextAnchor DEFAULT_ROTATION_ANCHOR = TextAnchor.CENTER; 102: 103: /** The default rotation angle. */ 104: public static final double DEFAULT_ROTATION_ANGLE = 0.0; 105: 106: /** The text. */ 107: private String text; 108: 109: /** The font. */ 110: private Font font; 111: 112: /** The paint. */ 113: private transient Paint paint; 114: 115: /** The x-coordinate. */ 116: private double x; 117: 118: /** The y-coordinate. */ 119: private double y; 120: 121: /** The text anchor (to be aligned with (x, y)). */ 122: private TextAnchor textAnchor; 123: 124: /** The rotation anchor. */ 125: private TextAnchor rotationAnchor; 126: 127: /** The rotation angle. */ 128: private double rotationAngle; 129: 130: /** 131: * Creates a new annotation to be displayed at the given coordinates. The 132: * coordinates are specified in data space (they will be converted to 133: * Java2D space for display). 134: * 135: * @param text the text (<code>null</code> not permitted). 136: * @param x the x-coordinate (in data space). 137: * @param y the y-coordinate (in data space). 138: */ 139: public XYTextAnnotation(String text, double x, double y) { 140: if (text == null) { 141: throw new IllegalArgumentException("Null 'text' argument."); 142: } 143: this.text = text; 144: this.font = DEFAULT_FONT; 145: this.paint = DEFAULT_PAINT; 146: this.x = x; 147: this.y = y; 148: this.textAnchor = DEFAULT_TEXT_ANCHOR; 149: this.rotationAnchor = DEFAULT_ROTATION_ANCHOR; 150: this.rotationAngle = DEFAULT_ROTATION_ANGLE; 151: } 152: 153: /** 154: * Returns the text for the annotation. 155: * 156: * @return The text (never <code>null</code>). 157: * 158: * @see #setText(String) 159: */ 160: public String getText() { 161: return this.text; 162: } 163: 164: /** 165: * Sets the text for the annotation. 166: * 167: * @param text the text (<code>null</code> not permitted). 168: * 169: * @see #getText() 170: */ 171: public void setText(String text) { 172: if (text == null) { 173: throw new IllegalArgumentException("Null 'text' argument."); 174: } 175: this.text = text; 176: } 177: 178: /** 179: * Returns the font for the annotation. 180: * 181: * @return The font (never <code>null</code>). 182: * 183: * @see #setFont(Font) 184: */ 185: public Font getFont() { 186: return this.font; 187: } 188: 189: /** 190: * Sets the font for the annotation. 191: * 192: * @param font the font (<code>null</code> not permitted). 193: * 194: * @see #getFont() 195: */ 196: public void setFont(Font font) { 197: if (font == null) { 198: throw new IllegalArgumentException("Null 'font' argument."); 199: } 200: this.font = font; 201: } 202: 203: /** 204: * Returns the paint for the annotation. 205: * 206: * @return The paint (never <code>null</code>). 207: * 208: * @see #setPaint(Paint) 209: */ 210: public Paint getPaint() { 211: return this.paint; 212: } 213: 214: /** 215: * Sets the paint for the annotation. 216: * 217: * @param paint the paint (<code>null</code> not permitted). 218: * 219: * @see #getPaint() 220: */ 221: public void setPaint(Paint paint) { 222: if (paint == null) { 223: throw new IllegalArgumentException("Null 'paint' argument."); 224: } 225: this.paint = paint; 226: } 227: 228: /** 229: * Returns the text anchor. 230: * 231: * @return The text anchor (never <code>null</code>). 232: * 233: * @see #setTextAnchor(TextAnchor) 234: */ 235: public TextAnchor getTextAnchor() { 236: return this.textAnchor; 237: } 238: 239: /** 240: * Sets the text anchor (the point on the text bounding rectangle that is 241: * aligned to the (x, y) coordinate of the annotation). 242: * 243: * @param anchor the anchor point (<code>null</code> not permitted). 244: * 245: * @see #getTextAnchor() 246: */ 247: public void setTextAnchor(TextAnchor anchor) { 248: if (anchor == null) { 249: throw new IllegalArgumentException("Null 'anchor' argument."); 250: } 251: this.textAnchor = anchor; 252: } 253: 254: /** 255: * Returns the rotation anchor. 256: * 257: * @return The rotation anchor point (never <code>null</code>). 258: * 259: * @see #setRotationAnchor(TextAnchor) 260: */ 261: public TextAnchor getRotationAnchor() { 262: return this.rotationAnchor; 263: } 264: 265: /** 266: * Sets the rotation anchor point. 267: * 268: * @param anchor the anchor (<code>null</code> not permitted). 269: * 270: * @see #getRotationAnchor() 271: */ 272: public void setRotationAnchor(TextAnchor anchor) { 273: if (anchor == null) { 274: throw new IllegalArgumentException("Null 'anchor' argument."); 275: } 276: this.rotationAnchor = anchor; 277: } 278: 279: /** 280: * Returns the rotation angle. 281: * 282: * @return The rotation angle. 283: * 284: * @see #setRotationAngle(double) 285: */ 286: public double getRotationAngle() { 287: return this.rotationAngle; 288: } 289: 290: /** 291: * Sets the rotation angle. The angle is measured clockwise in radians. 292: * 293: * @param angle the angle (in radians). 294: * 295: * @see #getRotationAngle() 296: */ 297: public void setRotationAngle(double angle) { 298: this.rotationAngle = angle; 299: } 300: 301: /** 302: * Returns the x coordinate for the text anchor point (measured against the 303: * domain axis). 304: * 305: * @return The x coordinate (in data space). 306: * 307: * @see #setX(double) 308: */ 309: public double getX() { 310: return this.x; 311: } 312: 313: /** 314: * Sets the x coordinate for the text anchor point (measured against the 315: * domain axis). 316: * 317: * @param x the x coordinate (in data space). 318: * 319: * @see #getX() 320: */ 321: public void setX(double x) { 322: this.x = x; 323: } 324: 325: /** 326: * Returns the y coordinate for the text anchor point (measured against the 327: * range axis). 328: * 329: * @return The y coordinate (in data space). 330: * 331: * @see #setY(double) 332: */ 333: public double getY() { 334: return this.y; 335: } 336: 337: /** 338: * Sets the y coordinate for the text anchor point (measured against the 339: * range axis). 340: * 341: * @param y the y coordinate. 342: * 343: * @see #getY() 344: */ 345: public void setY(double y) { 346: this.y = y; 347: } 348: 349: /** 350: * Draws the annotation. 351: * 352: * @param g2 the graphics device. 353: * @param plot the plot. 354: * @param dataArea the data area. 355: * @param domainAxis the domain axis. 356: * @param rangeAxis the range axis. 357: * @param rendererIndex the renderer index. 358: * @param info an optional info object that will be populated with 359: * entity information. 360: */ 361: public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, 362: ValueAxis domainAxis, ValueAxis rangeAxis, 363: int rendererIndex, 364: PlotRenderingInfo info) { 365: 366: PlotOrientation orientation = plot.getOrientation(); 367: RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( 368: plot.getDomainAxisLocation(), orientation); 369: RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( 370: plot.getRangeAxisLocation(), orientation); 371: 372: float anchorX = (float) domainAxis.valueToJava2D( 373: this.x, dataArea, domainEdge); 374: float anchorY = (float) rangeAxis.valueToJava2D( 375: this.y, dataArea, rangeEdge); 376: 377: if (orientation == PlotOrientation.HORIZONTAL) { 378: float tempAnchor = anchorX; 379: anchorX = anchorY; 380: anchorY = tempAnchor; 381: } 382: 383: g2.setFont(getFont()); 384: g2.setPaint(getPaint()); 385: TextUtilities.drawRotatedString(getText(), g2, anchorX, anchorY, 386: getTextAnchor(), getRotationAngle(), getRotationAnchor()); 387: Shape hotspot = TextUtilities.calculateRotatedStringBounds( 388: getText(), g2, anchorX, anchorY, getTextAnchor(), 389: getRotationAngle(), getRotationAnchor()); 390: 391: String toolTip = getToolTipText(); 392: String url = getURL(); 393: if (toolTip != null || url != null) { 394: addEntity(info, hotspot, rendererIndex, toolTip, url); 395: } 396: 397: } 398: 399: /** 400: * Tests this annotation for equality with an arbitrary object. 401: * 402: * @param obj the object (<code>null</code> permitted). 403: * 404: * @return A boolean. 405: */ 406: public boolean equals(Object obj) { 407: if (obj == this) { 408: return true; 409: } 410: if (!(obj instanceof XYTextAnnotation)) { 411: return false; 412: } 413: if (!super.equals(obj)) { 414: return false; 415: } 416: XYTextAnnotation that = (XYTextAnnotation) obj; 417: if (!this.text.equals(that.text)) { 418: return false; 419: } 420: if (this.x != that.x) { 421: return false; 422: } 423: if (this.y != that.y) { 424: return false; 425: } 426: if (!this.font.equals(that.font)) { 427: return false; 428: } 429: if (!PaintUtilities.equal(this.paint, that.paint)) { 430: return false; 431: } 432: if (!this.rotationAnchor.equals(that.rotationAnchor)) { 433: return false; 434: } 435: if (this.rotationAngle != that.rotationAngle) { 436: return false; 437: } 438: if (!this.textAnchor.equals(that.textAnchor)) { 439: return false; 440: } 441: return true; 442: } 443: 444: /** 445: * Returns a hash code for the object. 446: * 447: * @return A hash code. 448: */ 449: public int hashCode() { 450: int result = 193; 451: result = 37 * this.text.hashCode(); 452: result = 37 * this.font.hashCode(); 453: result = 37 * result + HashUtilities.hashCodeForPaint(this.paint); 454: long temp = Double.doubleToLongBits(this.x); 455: result = 37 * result + (int) (temp ^ (temp >>> 32)); 456: temp = Double.doubleToLongBits(this.y); 457: result = 37 * result + (int) (temp ^ (temp >>> 32)); 458: result = 37 * result + this.textAnchor.hashCode(); 459: result = 37 * result + this.rotationAnchor.hashCode(); 460: temp = Double.doubleToLongBits(this.rotationAngle); 461: result = 37 * result + (int) (temp ^ (temp >>> 32)); 462: return result; 463: } 464: 465: /** 466: * Returns a clone of the annotation. 467: * 468: * @return A clone. 469: * 470: * @throws CloneNotSupportedException if the annotation can't be cloned. 471: */ 472: public Object clone() throws CloneNotSupportedException { 473: return super.clone(); 474: } 475: 476: /** 477: * Provides serialization support. 478: * 479: * @param stream the output stream. 480: * 481: * @throws IOException if there is an I/O error. 482: */ 483: private void writeObject(ObjectOutputStream stream) throws IOException { 484: stream.defaultWriteObject(); 485: SerialUtilities.writePaint(this.paint, stream); 486: } 487: 488: /** 489: * Provides serialization support. 490: * 491: * @param stream the input stream. 492: * 493: * @throws IOException if there is an I/O error. 494: * @throws ClassNotFoundException if there is a classpath problem. 495: */ 496: private void readObject(ObjectInputStream stream) 497: throws IOException, ClassNotFoundException { 498: stream.defaultReadObject(); 499: this.paint = SerialUtilities.readPaint(stream); 500: } 501: 502: }