001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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-2008, 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 043 package org.jfree.chart.plot.dial; 044 045 import java.awt.BasicStroke; 046 import java.awt.Color; 047 import java.awt.Graphics2D; 048 import java.awt.Paint; 049 import java.awt.Shape; 050 import java.awt.Stroke; 051 import java.awt.geom.Area; 052 import java.awt.geom.Ellipse2D; 053 import java.awt.geom.Rectangle2D; 054 import java.io.IOException; 055 import java.io.ObjectInputStream; 056 import java.io.ObjectOutputStream; 057 import java.io.Serializable; 058 059 import org.jfree.chart.HashUtilities; 060 import org.jfree.io.SerialUtilities; 061 import org.jfree.util.PaintUtilities; 062 import org.jfree.util.PublicCloneable; 063 064 /** 065 * A simple circular frame for the {@link DialPlot} class. 066 * 067 * @since 1.0.7 068 */ 069 public class StandardDialFrame extends AbstractDialLayer implements DialFrame, 070 Cloneable, PublicCloneable, Serializable { 071 072 /** For serialization. */ 073 static final long serialVersionUID = 1016585407507121596L; 074 075 /** The outer radius, relative to the framing rectangle. */ 076 private double radius; 077 078 /** 079 * The color used for the front of the panel. This field is transient 080 * because it requires special handling for serialization. 081 */ 082 private transient Paint backgroundPaint; 083 084 /** 085 * The color used for the border around the window. This field is transient 086 * because it requires special handling for serialization. 087 */ 088 private transient Paint foregroundPaint; 089 090 /** 091 * The stroke for drawing the frame outline. This field is transient 092 * because it requires special handling for serialization. 093 */ 094 private transient Stroke stroke; 095 096 /** 097 * Creates a new instance of <code>StandardDialFrame</code>. 098 */ 099 public StandardDialFrame() { 100 this.backgroundPaint = Color.gray; 101 this.foregroundPaint = Color.black; 102 this.stroke = new BasicStroke(2.0f); 103 this.radius = 0.95; 104 } 105 106 /** 107 * Returns the radius, relative to the framing rectangle. 108 * 109 * @return The radius. 110 * 111 * @see #setRadius(double) 112 */ 113 public double getRadius() { 114 return this.radius; 115 } 116 117 /** 118 * Sets the radius and sends a {@link DialLayerChangeEvent} to all 119 * registered listeners. 120 * 121 * @param radius the radius (must be positive). 122 * 123 * @see #getRadius() 124 */ 125 public void setRadius(double radius) { 126 if (radius <= 0) { 127 throw new IllegalArgumentException( 128 "The 'radius' must be positive."); 129 } 130 this.radius = radius; 131 notifyListeners(new DialLayerChangeEvent(this)); 132 } 133 134 /** 135 * Returns the background paint. 136 * 137 * @return The background paint (never <code>null</code>). 138 * 139 * @see #setBackgroundPaint(Paint) 140 */ 141 public Paint getBackgroundPaint() { 142 return this.backgroundPaint; 143 } 144 145 /** 146 * Sets the background paint and sends a {@link DialLayerChangeEvent} to 147 * all registered listeners. 148 * 149 * @param paint the paint (<code>null</code> not permitted). 150 * 151 * @see #getBackgroundPaint() 152 */ 153 public void setBackgroundPaint(Paint paint) { 154 if (paint == null) { 155 throw new IllegalArgumentException("Null 'paint' argument."); 156 } 157 this.backgroundPaint = paint; 158 notifyListeners(new DialLayerChangeEvent(this)); 159 } 160 161 /** 162 * Returns the foreground paint. 163 * 164 * @return The foreground paint (never <code>null</code>). 165 * 166 * @see #setForegroundPaint(Paint) 167 */ 168 public Paint getForegroundPaint() { 169 return this.foregroundPaint; 170 } 171 172 /** 173 * Sets the foreground paint and sends a {@link DialLayerChangeEvent} to 174 * all registered listeners. 175 * 176 * @param paint the paint (<code>null</code> not permitted). 177 * 178 * @see #getForegroundPaint() 179 */ 180 public void setForegroundPaint(Paint paint) { 181 if (paint == null) { 182 throw new IllegalArgumentException("Null 'paint' argument."); 183 } 184 this.foregroundPaint = paint; 185 notifyListeners(new DialLayerChangeEvent(this)); 186 } 187 188 /** 189 * Returns the stroke for the frame. 190 * 191 * @return The stroke (never <code>null</code>). 192 * 193 * @see #setStroke(Stroke) 194 */ 195 public Stroke getStroke() { 196 return this.stroke; 197 } 198 199 /** 200 * Sets the stroke and sends a {@link DialLayerChangeEvent} to all 201 * registered listeners. 202 * 203 * @param stroke the stroke (<code>null</code> not permitted). 204 * 205 * @see #getStroke() 206 */ 207 public void setStroke(Stroke stroke) { 208 if (stroke == null) { 209 throw new IllegalArgumentException("Null 'stroke' argument."); 210 } 211 this.stroke = stroke; 212 notifyListeners(new DialLayerChangeEvent(this)); 213 } 214 215 /** 216 * Returns the shape for the window for this dial. Some dial layers will 217 * request that their drawing be clipped within this window. 218 * 219 * @param frame the reference frame (<code>null</code> not permitted). 220 * 221 * @return The shape of the dial's window. 222 */ 223 public Shape getWindow(Rectangle2D frame) { 224 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 225 this.radius); 226 return new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 227 f.getHeight()); 228 } 229 230 /** 231 * Returns <code>false</code> to indicate that this dial layer is not 232 * clipped to the dial window. 233 * 234 * @return A boolean. 235 */ 236 public boolean isClippedToWindow() { 237 return false; 238 } 239 240 /** 241 * Draws the frame. This method is called by the {@link DialPlot} class, 242 * you shouldn't need to call it directly. 243 * 244 * @param g2 the graphics target (<code>null</code> not permitted). 245 * @param plot the plot (<code>null</code> not permitted). 246 * @param frame the frame (<code>null</code> not permitted). 247 * @param view the view (<code>null</code> not permitted). 248 */ 249 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 250 Rectangle2D view) { 251 252 Shape window = getWindow(frame); 253 254 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius + 0.02, 255 this.radius + 0.02); 256 Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 257 f.getHeight()); 258 259 Area area = new Area(e); 260 Area area2 = new Area(window); 261 area.subtract(area2); 262 g2.setPaint(this.backgroundPaint); 263 g2.fill(area); 264 265 g2.setStroke(this.stroke); 266 g2.setPaint(this.foregroundPaint); 267 g2.draw(window); 268 g2.draw(e); 269 } 270 271 /** 272 * Tests this instance for equality with an arbitrary object. 273 * 274 * @param obj the object (<code>null</code> permitted). 275 * 276 * @return A boolean. 277 */ 278 public boolean equals(Object obj) { 279 if (obj == this) { 280 return true; 281 } 282 if (!(obj instanceof StandardDialFrame)) { 283 return false; 284 } 285 StandardDialFrame that = (StandardDialFrame) obj; 286 if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) { 287 return false; 288 } 289 if (!PaintUtilities.equal(this.foregroundPaint, that.foregroundPaint)) { 290 return false; 291 } 292 if (this.radius != that.radius) { 293 return false; 294 } 295 if (!this.stroke.equals(that.stroke)) { 296 return false; 297 } 298 return super.equals(obj); 299 } 300 301 /** 302 * Returns a hash code for this instance. 303 * 304 * @return The hash code. 305 */ 306 public int hashCode() { 307 int result = 193; 308 long temp = Double.doubleToLongBits(this.radius); 309 result = 37 * result + (int) (temp ^ (temp >>> 32)); 310 result = 37 * result + HashUtilities.hashCodeForPaint( 311 this.backgroundPaint); 312 result = 37 * result + HashUtilities.hashCodeForPaint( 313 this.foregroundPaint); 314 result = 37 * result + this.stroke.hashCode(); 315 return result; 316 } 317 318 /** 319 * Returns a clone of this instance. 320 * 321 * @return A clone. 322 * 323 * @throws CloneNotSupportedException if any of the frame's attributes 324 * cannot be cloned. 325 */ 326 public Object clone() throws CloneNotSupportedException { 327 return super.clone(); 328 } 329 330 /** 331 * Provides serialization support. 332 * 333 * @param stream the output stream. 334 * 335 * @throws IOException if there is an I/O error. 336 */ 337 private void writeObject(ObjectOutputStream stream) throws IOException { 338 stream.defaultWriteObject(); 339 SerialUtilities.writePaint(this.backgroundPaint, stream); 340 SerialUtilities.writePaint(this.foregroundPaint, stream); 341 SerialUtilities.writeStroke(this.stroke, stream); 342 } 343 344 /** 345 * Provides serialization support. 346 * 347 * @param stream the input stream. 348 * 349 * @throws IOException if there is an I/O error. 350 * @throws ClassNotFoundException if there is a classpath problem. 351 */ 352 private void readObject(ObjectInputStream stream) 353 throws IOException, ClassNotFoundException { 354 stream.defaultReadObject(); 355 this.backgroundPaint = SerialUtilities.readPaint(stream); 356 this.foregroundPaint = SerialUtilities.readPaint(stream); 357 this.stroke = SerialUtilities.readStroke(stream); 358 } 359 360 }