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 * DialCap.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 * 17-Oct-2007 : Updated equals() method (DG); 039 * 040 */ 041 042 package org.jfree.chart.plot.dial; 043 044 import java.awt.BasicStroke; 045 import java.awt.Color; 046 import java.awt.Graphics2D; 047 import java.awt.Paint; 048 import java.awt.Stroke; 049 import java.awt.geom.Ellipse2D; 050 import java.awt.geom.Rectangle2D; 051 import java.io.IOException; 052 import java.io.ObjectInputStream; 053 import java.io.ObjectOutputStream; 054 import java.io.Serializable; 055 056 import org.jfree.chart.HashUtilities; 057 import org.jfree.io.SerialUtilities; 058 import org.jfree.util.PaintUtilities; 059 import org.jfree.util.PublicCloneable; 060 061 /** 062 * A regular dial layer that can be used to draw a cap over the center of 063 * the dial (the base of the dial pointer(s)). 064 * 065 * @since 1.0.7 066 */ 067 public class DialCap extends AbstractDialLayer implements DialLayer, Cloneable, 068 PublicCloneable, Serializable { 069 070 /** For serialization. */ 071 static final long serialVersionUID = -2929484264982524463L; 072 073 /** 074 * The radius of the cap, as a percentage of the framing rectangle. 075 */ 076 private double radius; 077 078 /** 079 * The fill paint. This field is transient because it requires special 080 * handling for serialization. 081 */ 082 private transient Paint fillPaint; 083 084 /** 085 * The paint used to draw the cap outline (this should never be 086 * <code>null</code>). This field is transient because it requires 087 * special handling for serialization. 088 */ 089 private transient Paint outlinePaint; 090 091 /** 092 * The stroke used to draw the cap outline (this should never be 093 * <code>null</code>). This field is transient because it requires 094 * special handling for serialization. 095 */ 096 private transient Stroke outlineStroke; 097 098 /** 099 * Creates a new instance of <code>StandardDialBackground</code>. The 100 * default background paint is <code>Color.white</code>. 101 */ 102 public DialCap() { 103 this.radius = 0.05; 104 this.fillPaint = Color.white; 105 this.outlinePaint = Color.black; 106 this.outlineStroke = new BasicStroke(2.0f); 107 } 108 109 /** 110 * Returns the radius of the cap, as a percentage of the dial's framing 111 * rectangle. 112 * 113 * @return The radius. 114 * 115 * @see #setRadius(double) 116 */ 117 public double getRadius() { 118 return this.radius; 119 } 120 121 /** 122 * Sets the radius of the cap, as a percentage of the dial's framing 123 * rectangle, and sends a {@link DialLayerChangeEvent} to all registered 124 * listeners. 125 * 126 * @param radius the radius (must be greater than zero). 127 * 128 * @see #getRadius() 129 */ 130 public void setRadius(double radius) { 131 if (radius <= 0.0) { 132 throw new IllegalArgumentException("Requires radius > 0.0."); 133 } 134 this.radius = radius; 135 notifyListeners(new DialLayerChangeEvent(this)); 136 } 137 138 /** 139 * Returns the paint used to fill the cap. 140 * 141 * @return The paint (never <code>null</code>). 142 * 143 * @see #setFillPaint(Paint) 144 */ 145 public Paint getFillPaint() { 146 return this.fillPaint; 147 } 148 149 /** 150 * Sets the paint for the cap background and sends a 151 * {@link DialLayerChangeEvent} to all registered listeners. 152 * 153 * @param paint the paint (<code>null</code> not permitted). 154 * 155 * @see #getFillPaint() 156 */ 157 public void setFillPaint(Paint paint) { 158 if (paint == null) { 159 throw new IllegalArgumentException("Null 'paint' argument."); 160 } 161 this.fillPaint = paint; 162 notifyListeners(new DialLayerChangeEvent(this)); 163 } 164 165 /** 166 * Returns the paint used to draw the outline of the cap. 167 * 168 * @return The paint (never <code>null</code>). 169 * 170 * @see #setOutlinePaint(Paint) 171 */ 172 public Paint getOutlinePaint() { 173 return this.outlinePaint; 174 } 175 176 /** 177 * Sets the paint used to draw the outline of the cap and sends a 178 * {@link DialLayerChangeEvent} to all registered listeners. 179 * 180 * @param paint the paint (<code>null</code> not permitted). 181 * 182 * @see #getOutlinePaint() 183 */ 184 public void setOutlinePaint(Paint paint) { 185 if (paint == null) { 186 throw new IllegalArgumentException("Null 'paint' argument."); 187 } 188 this.outlinePaint = paint; 189 notifyListeners(new DialLayerChangeEvent(this)); 190 } 191 192 /** 193 * Returns the stroke used to draw the outline of the cap. 194 * 195 * @return The stroke (never <code>null</code>). 196 * 197 * @see #setOutlineStroke(Stroke) 198 */ 199 public Stroke getOutlineStroke() { 200 return this.outlineStroke; 201 } 202 203 /** 204 * Sets the stroke used to draw the outline of the cap and sends a 205 * {@link DialLayerChangeEvent} to all registered listeners. 206 * 207 * @param stroke the stroke (<code>null</code> not permitted). 208 * 209 * @see #getOutlineStroke() 210 */ 211 public void setOutlineStroke(Stroke stroke) { 212 if (stroke == null) { 213 throw new IllegalArgumentException("Null 'stroke' argument."); 214 } 215 this.outlineStroke = stroke; 216 notifyListeners(new DialLayerChangeEvent(this)); 217 } 218 219 /** 220 * Returns <code>true</code> to indicate that this layer should be 221 * clipped within the dial window. 222 * 223 * @return <code>true</code>. 224 */ 225 public boolean isClippedToWindow() { 226 return true; 227 } 228 229 /** 230 * Draws the background to the specified graphics device. If the dial 231 * frame specifies a window, the clipping region will already have been 232 * set to this window before this method is called. 233 * 234 * @param g2 the graphics device (<code>null</code> not permitted). 235 * @param plot the plot (ignored here). 236 * @param frame the dial frame (ignored here). 237 * @param view the view rectangle (<code>null</code> not permitted). 238 */ 239 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 240 Rectangle2D view) { 241 242 g2.setPaint(this.fillPaint); 243 244 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 245 this.radius); 246 Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 247 f.getHeight()); 248 g2.fill(e); 249 g2.setPaint(this.outlinePaint); 250 g2.setStroke(this.outlineStroke); 251 g2.draw(e); 252 253 } 254 255 /** 256 * Tests this instance for equality with an arbitrary object. 257 * 258 * @param obj the object (<code>null</code> permitted). 259 * 260 * @return A boolean. 261 */ 262 public boolean equals(Object obj) { 263 if (obj == this) { 264 return true; 265 } 266 if (!(obj instanceof DialCap)) { 267 return false; 268 } 269 DialCap that = (DialCap) obj; 270 if (this.radius != that.radius) { 271 return false; 272 } 273 if (!PaintUtilities.equal(this.fillPaint, that.fillPaint)) { 274 return false; 275 } 276 if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) { 277 return false; 278 } 279 if (!this.outlineStroke.equals(that.outlineStroke)) { 280 return false; 281 } 282 return super.equals(obj); 283 } 284 285 /** 286 * Returns a hash code for this instance. 287 * 288 * @return The hash code. 289 */ 290 public int hashCode() { 291 int result = 193; 292 result = 37 * result + HashUtilities.hashCodeForPaint(this.fillPaint); 293 result = 37 * result + HashUtilities.hashCodeForPaint( 294 this.outlinePaint); 295 result = 37 * result + this.outlineStroke.hashCode(); 296 return result; 297 } 298 299 /** 300 * Returns a clone of this instance. 301 * 302 * @return A clone. 303 * 304 * @throws CloneNotSupportedException if some attribute of the cap cannot 305 * be cloned. 306 */ 307 public Object clone() throws CloneNotSupportedException { 308 return super.clone(); 309 } 310 311 /** 312 * Provides serialization support. 313 * 314 * @param stream the output stream. 315 * 316 * @throws IOException if there is an I/O error. 317 */ 318 private void writeObject(ObjectOutputStream stream) throws IOException { 319 stream.defaultWriteObject(); 320 SerialUtilities.writePaint(this.fillPaint, stream); 321 SerialUtilities.writePaint(this.outlinePaint, stream); 322 SerialUtilities.writeStroke(this.outlineStroke, stream); 323 } 324 325 /** 326 * Provides serialization support. 327 * 328 * @param stream the input stream. 329 * 330 * @throws IOException if there is an I/O error. 331 * @throws ClassNotFoundException if there is a classpath problem. 332 */ 333 private void readObject(ObjectInputStream stream) 334 throws IOException, ClassNotFoundException { 335 stream.defaultReadObject(); 336 this.fillPaint = SerialUtilities.readPaint(stream); 337 this.outlinePaint = SerialUtilities.readPaint(stream); 338 this.outlineStroke = SerialUtilities.readStroke(stream); 339 } 340 341 } 342