001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ---------------------- 028 * StandardDialFrame.java 029 * ---------------------- 030 * (C) Copyright 2006-2013, 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 * 03-Jul-2013 : Use ParamChecks (DG); 041 * 042 */ 043 044package org.jfree.chart.plot.dial; 045 046import java.awt.BasicStroke; 047import java.awt.Color; 048import java.awt.Graphics2D; 049import java.awt.Paint; 050import java.awt.Shape; 051import java.awt.Stroke; 052import java.awt.geom.Area; 053import java.awt.geom.Ellipse2D; 054import java.awt.geom.Rectangle2D; 055import java.io.IOException; 056import java.io.ObjectInputStream; 057import java.io.ObjectOutputStream; 058import java.io.Serializable; 059 060import org.jfree.chart.HashUtilities; 061import org.jfree.chart.util.ParamChecks; 062import org.jfree.io.SerialUtilities; 063import org.jfree.util.PaintUtilities; 064import org.jfree.util.PublicCloneable; 065 066/** 067 * A simple circular frame for the {@link DialPlot} class. 068 * 069 * @since 1.0.7 070 */ 071public class StandardDialFrame extends AbstractDialLayer implements DialFrame, 072 Cloneable, PublicCloneable, Serializable { 073 074 /** For serialization. */ 075 static final long serialVersionUID = 1016585407507121596L; 076 077 /** The outer radius, relative to the framing rectangle. */ 078 private double radius; 079 080 /** 081 * The color used for the front of the panel. This field is transient 082 * because it requires special handling for serialization. 083 */ 084 private transient Paint backgroundPaint; 085 086 /** 087 * The color used for the border around the window. This field is transient 088 * because it requires special handling for serialization. 089 */ 090 private transient Paint foregroundPaint; 091 092 /** 093 * The stroke for drawing the frame outline. This field is transient 094 * because it requires special handling for serialization. 095 */ 096 private transient Stroke stroke; 097 098 /** 099 * Creates a new instance of <code>StandardDialFrame</code>. 100 */ 101 public StandardDialFrame() { 102 this.backgroundPaint = Color.gray; 103 this.foregroundPaint = Color.black; 104 this.stroke = new BasicStroke(2.0f); 105 this.radius = 0.95; 106 } 107 108 /** 109 * Returns the radius, relative to the framing rectangle. 110 * 111 * @return The radius. 112 * 113 * @see #setRadius(double) 114 */ 115 public double getRadius() { 116 return this.radius; 117 } 118 119 /** 120 * Sets the radius and sends a {@link DialLayerChangeEvent} to all 121 * registered listeners. 122 * 123 * @param radius the radius (must be positive). 124 * 125 * @see #getRadius() 126 */ 127 public void setRadius(double radius) { 128 if (radius <= 0) { 129 throw new IllegalArgumentException( 130 "The 'radius' must be positive."); 131 } 132 this.radius = radius; 133 notifyListeners(new DialLayerChangeEvent(this)); 134 } 135 136 /** 137 * Returns the background paint. 138 * 139 * @return The background paint (never <code>null</code>). 140 * 141 * @see #setBackgroundPaint(Paint) 142 */ 143 public Paint getBackgroundPaint() { 144 return this.backgroundPaint; 145 } 146 147 /** 148 * Sets the background paint and sends a {@link DialLayerChangeEvent} to 149 * all registered listeners. 150 * 151 * @param paint the paint (<code>null</code> not permitted). 152 * 153 * @see #getBackgroundPaint() 154 */ 155 public void setBackgroundPaint(Paint paint) { 156 ParamChecks.nullNotPermitted(paint, "paint"); 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 ParamChecks.nullNotPermitted(paint, "paint"); 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 ParamChecks.nullNotPermitted(stroke, "stroke"); 207 this.stroke = stroke; 208 notifyListeners(new DialLayerChangeEvent(this)); 209 } 210 211 /** 212 * Returns the shape for the window for this dial. Some dial layers will 213 * request that their drawing be clipped within this window. 214 * 215 * @param frame the reference frame (<code>null</code> not permitted). 216 * 217 * @return The shape of the dial's window. 218 */ 219 @Override 220 public Shape getWindow(Rectangle2D frame) { 221 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 222 this.radius); 223 return new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 224 f.getHeight()); 225 } 226 227 /** 228 * Returns <code>false</code> to indicate that this dial layer is not 229 * clipped to the dial window. 230 * 231 * @return A boolean. 232 */ 233 @Override 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 @Override 248 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 249 Rectangle2D view) { 250 251 Shape window = getWindow(frame); 252 253 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius + 0.02, 254 this.radius + 0.02); 255 Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 256 f.getHeight()); 257 258 Area area = new Area(e); 259 Area area2 = new Area(window); 260 area.subtract(area2); 261 g2.setPaint(this.backgroundPaint); 262 g2.fill(area); 263 264 g2.setStroke(this.stroke); 265 g2.setPaint(this.foregroundPaint); 266 g2.draw(window); 267 g2.draw(e); 268 } 269 270 /** 271 * Tests this instance for equality with an arbitrary object. 272 * 273 * @param obj the object (<code>null</code> permitted). 274 * 275 * @return A boolean. 276 */ 277 @Override 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 @Override 307 public int hashCode() { 308 int result = 193; 309 long temp = Double.doubleToLongBits(this.radius); 310 result = 37 * result + (int) (temp ^ (temp >>> 32)); 311 result = 37 * result + HashUtilities.hashCodeForPaint( 312 this.backgroundPaint); 313 result = 37 * result + HashUtilities.hashCodeForPaint( 314 this.foregroundPaint); 315 result = 37 * result + this.stroke.hashCode(); 316 return result; 317 } 318 319 /** 320 * Returns a clone of this instance. 321 * 322 * @return A clone. 323 * 324 * @throws CloneNotSupportedException if any of the frame's attributes 325 * cannot be cloned. 326 */ 327 @Override 328 public Object clone() throws CloneNotSupportedException { 329 return super.clone(); 330 } 331 332 /** 333 * Provides serialization support. 334 * 335 * @param stream the output stream. 336 * 337 * @throws IOException if there is an I/O error. 338 */ 339 private void writeObject(ObjectOutputStream stream) throws IOException { 340 stream.defaultWriteObject(); 341 SerialUtilities.writePaint(this.backgroundPaint, stream); 342 SerialUtilities.writePaint(this.foregroundPaint, stream); 343 SerialUtilities.writeStroke(this.stroke, stream); 344 } 345 346 /** 347 * Provides serialization support. 348 * 349 * @param stream the input stream. 350 * 351 * @throws IOException if there is an I/O error. 352 * @throws ClassNotFoundException if there is a classpath problem. 353 */ 354 private void readObject(ObjectInputStream stream) 355 throws IOException, ClassNotFoundException { 356 stream.defaultReadObject(); 357 this.backgroundPaint = SerialUtilities.readPaint(stream); 358 this.foregroundPaint = SerialUtilities.readPaint(stream); 359 this.stroke = SerialUtilities.readStroke(stream); 360 } 361 362}