    001/* ===========================================================
    002 * JFreeChart : a free chart library for the Java(tm) platform
    003 * ===========================================================
    004 *
    005 * (C) Copyright 2000-2011, 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-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
    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 *
    065 * @since 1.0.7
    066 */
    067public 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