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 * DialCap.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 * 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.chart.util.ParamChecks; 058import org.jfree.io.SerialUtilities; 059import org.jfree.util.PaintUtilities; 060import org.jfree.util.PublicCloneable; 061 062/** 063 * A regular dial layer that can be used to draw a cap over the center of 064 * the dial (the base of the dial pointer(s)). 065 * 066 * @since 1.0.7 067 */ 068public class DialCap extends AbstractDialLayer implements DialLayer, Cloneable, 069 PublicCloneable, Serializable { 070 071 /** For serialization. */ 072 static final long serialVersionUID = -2929484264982524463L; 073 074 /** 075 * The radius of the cap, as a percentage of the framing rectangle. 076 */ 077 private double radius; 078 079 /** 080 * The fill paint. This field is transient because it requires special 081 * handling for serialization. 082 */ 083 private transient Paint fillPaint; 084 085 /** 086 * The paint used to draw the cap outline (this should never be 087 * <code>null</code>). This field is transient because it requires 088 * special handling for serialization. 089 */ 090 private transient Paint outlinePaint; 091 092 /** 093 * The stroke used to draw the cap outline (this should never be 094 * <code>null</code>). This field is transient because it requires 095 * special handling for serialization. 096 */ 097 private transient Stroke outlineStroke; 098 099 /** 100 * Creates a new instance of <code>StandardDialBackground</code>. The 101 * default background paint is <code>Color.white</code>. 102 */ 103 public DialCap() { 104 this.radius = 0.05; 105 this.fillPaint = Color.white; 106 this.outlinePaint = Color.black; 107 this.outlineStroke = new BasicStroke(2.0f); 108 } 109 110 /** 111 * Returns the radius of the cap, as a percentage of the dial's framing 112 * rectangle. 113 * 114 * @return The radius. 115 * 116 * @see #setRadius(double) 117 */ 118 public double getRadius() { 119 return this.radius; 120 } 121 122 /** 123 * Sets the radius of the cap, as a percentage of the dial's framing 124 * rectangle, and sends a {@link DialLayerChangeEvent} to all registered 125 * listeners. 126 * 127 * @param radius the radius (must be greater than zero). 128 * 129 * @see #getRadius() 130 */ 131 public void setRadius(double radius) { 132 if (radius <= 0.0) { 133 throw new IllegalArgumentException("Requires radius > 0.0."); 134 } 135 this.radius = radius; 136 notifyListeners(new DialLayerChangeEvent(this)); 137 } 138 139 /** 140 * Returns the paint used to fill the cap. 141 * 142 * @return The paint (never <code>null</code>). 143 * 144 * @see #setFillPaint(Paint) 145 */ 146 public Paint getFillPaint() { 147 return this.fillPaint; 148 } 149 150 /** 151 * Sets the paint for the cap background and sends a 152 * {@link DialLayerChangeEvent} to all registered listeners. 153 * 154 * @param paint the paint (<code>null</code> not permitted). 155 * 156 * @see #getFillPaint() 157 */ 158 public void setFillPaint(Paint paint) { 159 ParamChecks.nullNotPermitted(paint, "paint"); 160 this.fillPaint = paint; 161 notifyListeners(new DialLayerChangeEvent(this)); 162 } 163 164 /** 165 * Returns the paint used to draw the outline of the cap. 166 * 167 * @return The paint (never <code>null</code>). 168 * 169 * @see #setOutlinePaint(Paint) 170 */ 171 public Paint getOutlinePaint() { 172 return this.outlinePaint; 173 } 174 175 /** 176 * Sets the paint used to draw the outline of the cap and sends a 177 * {@link DialLayerChangeEvent} to all registered listeners. 178 * 179 * @param paint the paint (<code>null</code> not permitted). 180 * 181 * @see #getOutlinePaint() 182 */ 183 public void setOutlinePaint(Paint paint) { 184 ParamChecks.nullNotPermitted(paint, "paint"); 185 this.outlinePaint = paint; 186 notifyListeners(new DialLayerChangeEvent(this)); 187 } 188 189 /** 190 * Returns the stroke used to draw the outline of the cap. 191 * 192 * @return The stroke (never <code>null</code>). 193 * 194 * @see #setOutlineStroke(Stroke) 195 */ 196 public Stroke getOutlineStroke() { 197 return this.outlineStroke; 198 } 199 200 /** 201 * Sets the stroke used to draw the outline of the cap and sends a 202 * {@link DialLayerChangeEvent} to all registered listeners. 203 * 204 * @param stroke the stroke (<code>null</code> not permitted). 205 * 206 * @see #getOutlineStroke() 207 */ 208 public void setOutlineStroke(Stroke stroke) { 209 ParamChecks.nullNotPermitted(stroke, "stroke"); 210 this.outlineStroke = stroke; 211 notifyListeners(new DialLayerChangeEvent(this)); 212 } 213 214 /** 215 * Returns <code>true</code> to indicate that this layer should be 216 * clipped within the dial window. 217 * 218 * @return <code>true</code>. 219 */ 220 @Override 221 public boolean isClippedToWindow() { 222 return true; 223 } 224 225 /** 226 * Draws the background to the specified graphics device. If the dial 227 * frame specifies a window, the clipping region will already have been 228 * set to this window before this method is called. 229 * 230 * @param g2 the graphics device (<code>null</code> not permitted). 231 * @param plot the plot (ignored here). 232 * @param frame the dial frame (ignored here). 233 * @param view the view rectangle (<code>null</code> not permitted). 234 */ 235 @Override 236 public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, 237 Rectangle2D view) { 238 239 g2.setPaint(this.fillPaint); 240 241 Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, 242 this.radius); 243 Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), 244 f.getHeight()); 245 g2.fill(e); 246 g2.setPaint(this.outlinePaint); 247 g2.setStroke(this.outlineStroke); 248 g2.draw(e); 249 250 } 251 252 /** 253 * Tests this instance for equality with an arbitrary object. 254 * 255 * @param obj the object (<code>null</code> permitted). 256 * 257 * @return A boolean. 258 */ 259 @Override 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 @Override 289 public int hashCode() { 290 int result = 193; 291 result = 37 * result + HashUtilities.hashCodeForPaint(this.fillPaint); 292 result = 37 * result + HashUtilities.hashCodeForPaint( 293 this.outlinePaint); 294 result = 37 * result + this.outlineStroke.hashCode(); 295 return result; 296 } 297 298 /** 299 * Returns a clone of this instance. 300 * 301 * @return A clone. 302 * 303 * @throws CloneNotSupportedException if some attribute of the cap cannot 304 * be cloned. 305 */ 306 @Override 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