001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ---------------------- 028 * StandardDialFrame.java 029 * ---------------------- 030 * (C) Copyright 2006, 2007, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 03-Nov-2006 : Version 1 (DG); 038 * 08-Mar-2007 : Fix in hashCode() (DG); 039 * 29-Oct-2007 : Renamed StandardDialFrame (DG); 040 * 041 */ 042 043package org.jfree.chart.plot.dial; 044 045import java.awt.BasicStroke; 046import java.awt.Color; 047import java.awt.Graphics2D; 048import java.awt.Paint; 049import java.awt.Shape; 050import java.awt.Stroke; 051import java.awt.geom.Area; 052import java.awt.geom.Ellipse2D; 053import java.awt.geom.Rectangle2D; 054import java.io.IOException; 055import java.io.ObjectInputStream; 056import java.io.ObjectOutputStream; 057import java.io.Serializable; 058 059import org.jfree.chart.HashUtilities; 060import org.jfree.io.SerialUtilities; 061import org.jfree.util.PaintUtilities; 062import org.jfree.util.PublicCloneable; 063 064/** 065 * A simple circular frame for the {@link DialPlot} class. 066 */ 067public class StandardDialFrame extends AbstractDialLayer implements DialFrame, 068 Cloneable, PublicCloneable, Serializable { 069 070 /** For serialization. */ 071 static final long serialVersionUID = 1016585407507121596L; 072 073 /** The outer radius, relative to the framing rectangle. */ 074 private double radius; 075 076 /** 077 * The color used for the front of the panel. This field is transient 078 * because it requires special handling for serialization. 079 */ 080 private transient Paint backgroundPaint; 081 082 /** 083 * The color used for the border around the window. This field is transient 084 * because it requires special handling for serialization. 085 */ 086 private transient Paint foregroundPaint; 087 088 /** 089 * The stroke for drawing the frame outline. This field is transient 090 * because it requires special handling for serialization. 091 */ 092 private transient Stroke stroke; 093 094 /** 095 * Creates a new instance of <code>StandardDialFrame</code>. 096 */ 097 public StandardDialFrame() { 098 this.backgroundPaint = Color.gray; 099 this.foregroundPaint = Color.black; 100 this.stroke = new BasicStroke(2.0f); 101 this.radius = 0.95; 102 } 103 104 /** 105 * Returns the radius, relative to the framing rectangle. 106 * 107 * @return The radius. 108 * 109 * @see #setRadius(double) 110 */ 111 public double getRadius() { 112 return this.radius; 113 } 114 115 /** 116 * Sets the radius and sends a {@link DialLayerChangeEvent} to all 117 * registered listeners. 118 * 119 * @param radius the radius (must be positive). 120 * 121 * @see #getRadius() 122 */ 123 public void setRadius(double radius) { 124 if (radius <= 0) { 125 throw new IllegalArgumentException( 126 "The 'radius' must be positive."); 127 } 128 this.radius = radius; 129 notifyListeners(new DialLayerChangeEvent(this)); 130 } 131 132 /** 133 * Returns the background paint. 134 * 135 * @return The background paint (never <code>null</code>). 136 * 137 * @see #setBackgroundPaint(Paint) 138 */ 139 public Paint getBackgroundPaint() { 140 return this.backgroundPaint; 141 } 142 143 /** 144 * Sets the background paint and sends a {@link DialLayerChangeEvent} to 145 * all registered listeners. 146 * 147 * @param paint the paint (<code>null</code> not permitted). 148 * 149 * @see #getBackgroundPaint() 150 */ 151 public void setBackgroundPaint(Paint paint) { 152 if (paint == null) { 153 throw new IllegalArgumentException("Null 'paint' argument."); 154 } 155 this.backgroundPaint = paint; 156 notifyListeners(new DialLayerChangeEvent(this)); 157 } 158 159 /** 160 * Returns the foreground paint. 161 * 162 * @return The foreground paint (never <code>null</code>). 163 * 164 * @see #setForegroundPaint(Paint) 165 */ 166 public Paint getForegroundPaint() { 167 return this.foregroundPaint; 168 } 169 170 /** 171 * Sets the foreground paint and sends a {@link DialLayerChangeEvent} to 172 * all registered listeners. 173 * 174 * @param paint the paint (<code>null</code> not permitted). 175 * 176 * @see #getForegroundPaint() 177 */ 178 public void setForegroundPaint(Paint paint) { 179 if (paint == null) { 180 throw new IllegalArgumentException("Null 'paint' argument."); 181 } 182 this.foregroundPaint = paint; 183 notifyListeners(new DialLayerChangeEvent(this)); 184 } 185 186 /** 187 * Returns the stroke for the frame. 188 * 189 * @return The stroke (never <code>null</code>). 190 * 191 * @see #setStroke(Stroke) 192 */ 193 public Stroke getStroke() { 194 return this.stroke; 195 } 196 197 /** 198 * Sets the stroke and sends a {@link DialLayerChangeEvent} to all 199 * registered listeners. 200 * 201 * @param stroke the stroke (<code>null</code> not permitted). 202 * 203 * @see #getStroke() 204 */ 205 public void setStroke(Stroke stroke) { 206 if (stroke == null) { 207 throw new IllegalArgumentException("Null 'stroke' argument."); 208 } 209 this.stroke = stroke; 210 notifyListeners(new DialLayerChangeEvent(this)); 211 } 212 213 /** 214 * Returns the shape for the window for this dial. Some dial layers will 215 * request that their drawing be clipped within this window. 216 * 217 * @param frame the reference frame (<code>null</code> not permitted). 218 * 219 * @return The shape of the dial's window. 220 */ 221 public Shape getWindow(Rectangle2D frame) { 222 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 223 this.radius); 224 return new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 225 f.getHeight()); 226 } 227 228 /** 229 * Returns <code>false</code> to indicate that this dial layer is not 230 * clipped to the dial window. 231 * 232 * @return A boolean. 233 */ 234 public boolean isClippedToWindow() { 235 return false; 236 } 237 238 /** 239 * Draws the frame. This method is called by the {@link DialPlot} class, 240 * you shouldn't need to call it directly. 241 * 242 * @param g2 the graphics target (<code>null</code> not permitted). 243 * @param plot the plot (<code>null</code> not permitted). 244 * @param frame the frame (<code>null</code> not permitted). 245 * @param view the view (<code>null</code> not permitted). 246 */ 247 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 248 Rectangle2D view) { 249 250 Shape window = getWindow(frame); 251 252 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius + 0.02, 253 this.radius + 0.02); 254 Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 255 f.getHeight()); 256 257 Area area = new Area(e); 258 Area area2 = new Area(window); 259 area.subtract(area2); 260 g2.setPaint(this.backgroundPaint); 261 g2.fill(area); 262 263 g2.setStroke(this.stroke); 264 g2.setPaint(this.foregroundPaint); 265 g2.draw(window); 266 g2.draw(e); 267 } 268 269 /** 270 * Tests this instance for equality with an arbitrary object. 271 * 272 * @param obj the object (<code>null</code> permitted). 273 * 274 * @return A boolean. 275 */ 276 public boolean equals(Object obj) { 277 if (obj == this) { 278 return true; 279 } 280 if (!(obj instanceof StandardDialFrame)) { 281 return false; 282 } 283 StandardDialFrame that = (StandardDialFrame) obj; 284 if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) { 285 return false; 286 } 287 if (!PaintUtilities.equal(this.foregroundPaint, that.foregroundPaint)) { 288 return false; 289 } 290 if (this.radius != that.radius) { 291 return false; 292 } 293 if (!this.stroke.equals(that.stroke)) { 294 return false; 295 } 296 return super.equals(obj); 297 } 298 299 /** 300 * Returns a hash code for this instance. 301 * 302 * @return The hash code. 303 */ 304 public int hashCode() { 305 int result = 193; 306 long temp = Double.doubleToLongBits(this.radius); 307 result = 37 * result + (int) (temp ^ (temp >>> 32)); 308 result = 37 * result + HashUtilities.hashCodeForPaint( 309 this.backgroundPaint); 310 result = 37 * result + HashUtilities.hashCodeForPaint( 311 this.foregroundPaint); 312 result = 37 * result + this.stroke.hashCode(); 313 return result; 314 } 315 316 /** 317 * Returns a clone of this instance. 318 * 319 * @return A clone. 320 * 321 * @throws CloneNotSupportedException if any of the frame's attributes 322 * cannot be cloned. 323 */ 324 public Object clone() throws CloneNotSupportedException { 325 return super.clone(); 326 } 327 328 /** 329 * Provides serialization support. 330 * 331 * @param stream the output stream. 332 * 333 * @throws IOException if there is an I/O error. 334 */ 335 private void writeObject(ObjectOutputStream stream) throws IOException { 336 stream.defaultWriteObject(); 337 SerialUtilities.writePaint(this.backgroundPaint, stream); 338 SerialUtilities.writePaint(this.foregroundPaint, stream); 339 SerialUtilities.writeStroke(this.stroke, stream); 340 } 341 342 /** 343 * Provides serialization support. 344 * 345 * @param stream the input stream. 346 * 347 * @throws IOException if there is an I/O error. 348 * @throws ClassNotFoundException if there is a classpath problem. 349 */ 350 private void readObject(ObjectInputStream stream) 351 throws IOException, ClassNotFoundException { 352 stream.defaultReadObject(); 353 this.backgroundPaint = SerialUtilities.readPaint(stream); 354 this.foregroundPaint = SerialUtilities.readPaint(stream); 355 this.stroke = SerialUtilities.readStroke(stream); 356 } 357 358}