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