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