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