    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 * 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
    043package org.jfree.chart.plot.dial;
    044
    045import java.awt.BasicStroke;
    046import java.awt.Color;
    047import java.awt.Graphics2D;
    048import java.awt.Paint;
    049import java.awt.Shape;
    050import java.awt.Stroke;
    051import java.awt.geom.Area;
    052import java.awt.geom.Ellipse2D;
    053import java.awt.geom.Rectangle2D;
    054import java.io.IOException;
    055import java.io.ObjectInputStream;
    056import java.io.ObjectOutputStream;
    057import java.io.Serializable;
    058
    059import org.jfree.chart.HashUtilities;
    060import org.jfree.io.SerialUtilities;
    061import org.jfree.util.PaintUtilities;
    062import org.jfree.util.PublicCloneable;
    063
    064/**
    065 * A simple circular frame for the {@link DialPlot} class.
    066 *
    067 * @since 1.0.7
    068 */
    069public 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}