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