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: * DialTextAnnotation.java 29: * ----------------------- 30: * (C) Copyright 2006, 2007, by Object Refinery Limited. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * Changes 36: * ------- 37: * 03-Nov-2006 : Version 1 (DG); 38: * 08-Mar-2007 : Fix in hashCode() (DG); 39: * 17-Oct-2007 : Updated equals() (DG); 40: * 24-Oct-2007 : Added getAnchor() and setAnchor() methods (DG); 41: * 42: */ 43: 44: package org.jfree.chart.plot.dial; 45: 46: import java.awt.Color; 47: import java.awt.Font; 48: import java.awt.Graphics2D; 49: import java.awt.Paint; 50: import java.awt.geom.Arc2D; 51: import java.awt.geom.Point2D; 52: import java.awt.geom.Rectangle2D; 53: import java.io.IOException; 54: import java.io.ObjectInputStream; 55: import java.io.ObjectOutputStream; 56: import java.io.Serializable; 57: 58: import org.jfree.chart.HashUtilities; 59: import org.jfree.io.SerialUtilities; 60: import org.jfree.text.TextUtilities; 61: import org.jfree.ui.TextAnchor; 62: import org.jfree.util.PaintUtilities; 63: import org.jfree.util.PublicCloneable; 64: 65: /** 66: * A text annotation for a {@link DialPlot}. 67: * 68: * @since 1.0.7 69: */ 70: public class DialTextAnnotation extends AbstractDialLayer implements DialLayer, 71: Cloneable, PublicCloneable, Serializable { 72: 73: /** For serialization. */ 74: static final long serialVersionUID = 3065267524054428071L; 75: 76: /** The label text. */ 77: private String label; 78: 79: /** The font. */ 80: private Font font; 81: 82: /** 83: * The paint for the label. This field is transient because it requires 84: * special handling for serialization. 85: */ 86: private transient Paint paint; 87: 88: /** The angle that defines the anchor point for the annotation. */ 89: private double angle; 90: 91: /** The radius that defines the anchor point for the annotation. */ 92: private double radius; 93: 94: /** The text anchor to be aligned to the annotation's anchor point. */ 95: private TextAnchor anchor; 96: 97: /** 98: * Creates a new instance of <code>DialTextAnnotation</code>. 99: * 100: * @param label the label (<code>null</code> not permitted). 101: */ 102: public DialTextAnnotation(String label) { 103: if (label == null) { 104: throw new IllegalArgumentException("Null 'label' argument."); 105: } 106: this.angle = -90.0; 107: this.radius = 0.3; 108: this.font = new Font("Dialog", Font.BOLD, 14); 109: this.paint = Color.black; 110: this.label = label; 111: this.anchor = TextAnchor.TOP_CENTER; 112: } 113: 114: /** 115: * Returns the label text. 116: * 117: * @return The label text (never <code>null</code). 118: * 119: * @see #setLabel(String) 120: */ 121: public String getLabel() { 122: return this.label; 123: } 124: 125: /** 126: * Sets the label and sends a {@link DialLayerChangeEvent} to all 127: * registered listeners. 128: * 129: * @param label the label (<code>null</code> not permitted). 130: * 131: * @see #getLabel() 132: */ 133: public void setLabel(String label) { 134: if (label == null) { 135: throw new IllegalArgumentException("Null 'label' argument."); 136: } 137: this.label = label; 138: notifyListeners(new DialLayerChangeEvent(this)); 139: } 140: 141: /** 142: * Returns the font used to display the label. 143: * 144: * @return The font (never <code>null</code>). 145: * 146: * @see #setFont(Font) 147: */ 148: public Font getFont() { 149: return this.font; 150: } 151: 152: /** 153: * Sets the font used to display the label and sends a 154: * {@link DialLayerChangeEvent} to all registered listeners. 155: * 156: * @param font the font (<code>null</code> not permitted). 157: * 158: * @see #getFont() 159: */ 160: public void setFont(Font font) { 161: if (font == null) { 162: throw new IllegalArgumentException("Null 'font' argument."); 163: } 164: this.font = font; 165: notifyListeners(new DialLayerChangeEvent(this)); 166: } 167: 168: /** 169: * Returns the paint used to display the label. 170: * 171: * @return The paint (never <code>null</code>). 172: * 173: * @see #setPaint(Paint) 174: */ 175: public Paint getPaint() { 176: return this.paint; 177: } 178: 179: /** 180: * Sets the paint used to display the label and sends a 181: * {@link DialLayerChangeEvent} to all registered listeners. 182: * 183: * @param paint the paint (<code>null</code> not permitted). 184: * 185: * @see #getPaint() 186: */ 187: public void setPaint(Paint paint) { 188: if (paint == null) { 189: throw new IllegalArgumentException("Null 'paint' argument."); 190: } 191: this.paint = paint; 192: notifyListeners(new DialLayerChangeEvent(this)); 193: } 194: 195: /** 196: * Returns the angle used to calculate the anchor point. 197: * 198: * @return The angle (in degrees). 199: * 200: * @see #setAngle(double) 201: * @see #getRadius() 202: */ 203: public double getAngle() { 204: return this.angle; 205: } 206: 207: /** 208: * Sets the angle used to calculate the anchor point and sends a 209: * {@link DialLayerChangeEvent} to all registered listeners. 210: * 211: * @param angle the angle (in degrees). 212: * 213: * @see #getAngle() 214: * @see #setRadius(double) 215: */ 216: public void setAngle(double angle) { 217: this.angle = angle; 218: notifyListeners(new DialLayerChangeEvent(this)); 219: } 220: 221: /** 222: * Returns the radius used to calculate the anchor point. This is 223: * specified as a percentage relative to the dial's framing rectangle. 224: * 225: * @return The radius. 226: * 227: * @see #setRadius(double) 228: * @see #getAngle() 229: */ 230: public double getRadius() { 231: return this.radius; 232: } 233: 234: /** 235: * Sets the radius used to calculate the anchor point and sends a 236: * {@link DialLayerChangeEvent} to all registered listeners. 237: * 238: * @param radius the radius (as a percentage of the dial's framing 239: * rectangle). 240: * 241: * @see #getRadius() 242: * @see #setAngle(double) 243: */ 244: public void setRadius(double radius) { 245: if (radius < 0.0) { 246: throw new IllegalArgumentException( 247: "The 'radius' cannot be negative."); 248: } 249: this.radius = radius; 250: notifyListeners(new DialLayerChangeEvent(this)); 251: } 252: 253: /** 254: * Returns the text anchor point that will be aligned to the position 255: * specified by {@link #getAngle()} and {@link #getRadius()}. 256: * 257: * @return The anchor point. 258: * 259: * @see #setAnchor(TextAnchor) 260: */ 261: public TextAnchor getAnchor() { 262: return this.anchor; 263: } 264: 265: /** 266: * Sets the text anchor point and sends a {@link DialLayerChangeEvent} to 267: * all registered listeners. 268: * 269: * @param anchor the anchor point (<code>null</code> not permitted). 270: * 271: * @see #getAnchor() 272: */ 273: public void setAnchor(TextAnchor anchor) { 274: if (anchor == null) { 275: throw new IllegalArgumentException("Null 'anchor' argument."); 276: } 277: this.anchor = anchor; 278: notifyListeners(new DialLayerChangeEvent(this)); 279: } 280: 281: /** 282: * Returns <code>true</code> to indicate that this layer should be 283: * clipped within the dial window. 284: * 285: * @return <code>true</code>. 286: */ 287: public boolean isClippedToWindow() { 288: return true; 289: } 290: 291: /** 292: * Draws the background to the specified graphics device. If the dial 293: * frame specifies a window, the clipping region will already have been 294: * set to this window before this method is called. 295: * 296: * @param g2 the graphics device (<code>null</code> not permitted). 297: * @param plot the plot (ignored here). 298: * @param frame the dial frame (ignored here). 299: * @param view the view rectangle (<code>null</code> not permitted). 300: */ 301: public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 302: Rectangle2D view) { 303: 304: // work out the anchor point 305: Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 306: this.radius); 307: Arc2D arc = new Arc2D.Double(f, this.angle, 0.0, Arc2D.OPEN); 308: Point2D pt = arc.getStartPoint(); 309: g2.setPaint(this.paint); 310: g2.setFont(this.font); 311: TextUtilities.drawAlignedString(this.label, g2, (float) pt.getX(), 312: (float) pt.getY(), this.anchor); 313: 314: } 315: 316: /** 317: * Tests this instance for equality with an arbitrary object. 318: * 319: * @param obj the object (<code>null</code> permitted). 320: * 321: * @return A boolean. 322: */ 323: public boolean equals(Object obj) { 324: if (obj == this) { 325: return true; 326: } 327: if (!(obj instanceof DialTextAnnotation)) { 328: return false; 329: } 330: DialTextAnnotation that = (DialTextAnnotation) obj; 331: if (!this.label.equals(that.label)) { 332: return false; 333: } 334: if (!this.font.equals(that.font)) { 335: return false; 336: } 337: if (!PaintUtilities.equal(this.paint, that.paint)) { 338: return false; 339: } 340: if (this.radius != that.radius) { 341: return false; 342: } 343: if (this.angle != that.angle) { 344: return false; 345: } 346: if (!this.anchor.equals(that.anchor)) { 347: return false; 348: } 349: return super.equals(obj); 350: } 351: 352: /** 353: * Returns a hash code for this instance. 354: * 355: * @return The hash code. 356: */ 357: public int hashCode() { 358: int result = 193; 359: result = 37 * result + HashUtilities.hashCodeForPaint(this.paint); 360: result = 37 * result + this.font.hashCode(); 361: result = 37 * result + this.label.hashCode(); 362: result = 37 * result + this.anchor.hashCode(); 363: long temp = Double.doubleToLongBits(this.angle); 364: result = 37 * result + (int) (temp ^ (temp >>> 32)); 365: temp = Double.doubleToLongBits(this.radius); 366: result = 37 * result + (int) (temp ^ (temp >>> 32)); 367: return result; 368: } 369: 370: /** 371: * Returns a clone of this instance. 372: * 373: * @return The clone. 374: * 375: * @throws CloneNotSupportedException if some attribute of this instance 376: * cannot be cloned. 377: */ 378: public Object clone() throws CloneNotSupportedException { 379: return super.clone(); 380: } 381: 382: /** 383: * Provides serialization support. 384: * 385: * @param stream the output stream. 386: * 387: * @throws IOException if there is an I/O error. 388: */ 389: private void writeObject(ObjectOutputStream stream) throws IOException { 390: stream.defaultWriteObject(); 391: SerialUtilities.writePaint(this.paint, stream); 392: } 393: 394: /** 395: * Provides serialization support. 396: * 397: * @param stream the input stream. 398: * 399: * @throws IOException if there is an I/O error. 400: * @throws ClassNotFoundException if there is a classpath problem. 401: */ 402: private void readObject(ObjectInputStream stream) 403: throws IOException, ClassNotFoundException { 404: stream.defaultReadObject(); 405: this.paint = SerialUtilities.readPaint(stream); 406: } 407: 408: }