    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
    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