    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     * XYTitleAnnotation.java
    029     * ----------------------
    030     * (C) Copyright 2007, 2008, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Andrew Mickish;
    034     *
    035     * Changes:
    036     * --------
    037     * 02-Feb-2007 : Version 1 (DG);
    038     * 30-Apr-2007 : Fixed equals() method (DG);
    039     * 26-Feb-2008 : Fixed NullPointerException when drawing chart with a null
    040     *               ChartRenderingInfo - see patch 1901599 by Andrew Mickish (DG);
    041     * 03-Sep-2008 : Moved from experimental to main (DG);
    042     *
    043     */
    044
    045    package org.jfree.chart.annotations;
    046
    047    import java.awt.Graphics2D;
    048    import java.awt.geom.Point2D;
    049    import java.awt.geom.Rectangle2D;
    050    import java.io.Serializable;
    051
    052    import org.jfree.chart.HashUtilities;
    053    import org.jfree.chart.axis.AxisLocation;
    054    import org.jfree.chart.axis.ValueAxis;
    055    import org.jfree.chart.block.BlockParams;
    056    import org.jfree.chart.block.EntityBlockResult;
    057    import org.jfree.chart.block.RectangleConstraint;
    058    import org.jfree.chart.plot.Plot;
    059    import org.jfree.chart.plot.PlotOrientation;
    060    import org.jfree.chart.plot.PlotRenderingInfo;
    061    import org.jfree.chart.plot.XYPlot;
    062    import org.jfree.chart.title.Title;
    063    import org.jfree.chart.util.XYCoordinateType;
    064    import org.jfree.data.Range;
    065    import org.jfree.ui.RectangleAnchor;
    066    import org.jfree.ui.RectangleEdge;
    067    import org.jfree.ui.Size2D;
    068    import org.jfree.util.ObjectUtilities;
    069    import org.jfree.util.PublicCloneable;
    070
    071    /**
    072     * An annotation that allows any {@link Title} to be placed at a location on
    073     * an {@link XYPlot}.
    074     *
    075     * @since 1.0.11
    076     */
    077    public class XYTitleAnnotation extends AbstractXYAnnotation
    078            implements Cloneable, PublicCloneable, Serializable {
    079
    080        /** For serialization. */
    081        private static final long serialVersionUID = -4364694501921559958L;
    082
    083        /** The coordinate type. */
    084        private XYCoordinateType coordinateType;
    085
    086        /** The x-coordinate (in data space). */
    087        private double x;
    088
    089        /** The y-coordinate (in data space). */
    090        private double y;
    091
    092        /** The maximum width. */
    093        private double maxWidth;
    094
    095        /** The maximum height. */
    096        private double maxHeight;
    097
    098        /** The title. */
    099        private Title title;
    100
    101        /**
    102         * The title anchor point.
    103         */
    104        private RectangleAnchor anchor;
    105
    106        /**
    107         * Creates a new annotation to be displayed at the specified (x, y)
    108         * location.
    109         *
    110         * @param x  the x-coordinate (in data space).
    111         * @param y  the y-coordinate (in data space).
    112         * @param title  the title (<code>null</code> not permitted).
    113         */
    114        public XYTitleAnnotation(double x, double y, Title title) {
    115            this(x, y, title, RectangleAnchor.CENTER);
    116        }
    117
    118        /**
    119         * Creates a new annotation to be displayed at the specified (x, y)
    120         * location.
    121         *
    122         * @param x  the x-coordinate (in data space).
    123         * @param y  the y-coordinate (in data space).
    124         * @param title  the title (<code>null</code> not permitted).
    125         * @param anchor  the title anchor (<code>null</code> not permitted).
    126         */
    127        public XYTitleAnnotation(double x, double y, Title title,
    128                RectangleAnchor anchor) {
    129            if (title == null) {
    130                throw new IllegalArgumentException("Null 'title' argument.");
    131            }
    132            if (anchor == null) {
    133                throw new IllegalArgumentException("Null 'anchor' argument.");
    134            }
    135            this.coordinateType = XYCoordinateType.RELATIVE;
    136            this.x = x;
    137            this.y = y;
    138            this.maxWidth = 0.0;
    139            this.maxHeight = 0.0;
    140            this.title = title;
    141            this.anchor = anchor;
    142        }
    143
    144        /**
    145         * Returns the coordinate type (set in the constructor).
    146         *
    147         * @return The coordinate type (never <code>null</code>).
    148         */
    149        public XYCoordinateType getCoordinateType() {
    150            return this.coordinateType;
    151        }
    152
    153        /**
    154         * Returns the x-coordinate for the annotation.
    155         *
    156         * @return The x-coordinate.
    157         */
    158        public double getX() {
    159            return this.x;
    160        }
    161
    162        /**
    163         * Returns the y-coordinate for the annotation.
    164         *
    165         * @return The y-coordinate.
    166         */
    167        public double getY() {
    168            return this.y;
    169        }
    170
    171        /**
    172         * Returns the title for the annotation.
    173         *
    174         * @return The title.
    175         */
    176        public Title getTitle() {
    177            return this.title;
    178        }
    179
    180        /**
    181         * Returns the title anchor for the annotation.
    182         *
    183         * @return The title anchor.
    184         */
    185        public RectangleAnchor getTitleAnchor() {
    186            return this.anchor;
    187        }
    188
    189        /**
    190         * Returns the maximum width.
    191         *
    192         * @return The maximum width.
    193         */
    194        public double getMaxWidth() {
    195            return this.maxWidth;
    196        }
    197
    198        /**
    199         * Sets the maximum width.
    200         *
    201         * @param max  the maximum width (0.0 or less means no maximum).
    202         */
    203        public void setMaxWidth(double max) {
    204            this.maxWidth = max;
    205        }
    206
    207        /**
    208         * Returns the maximum height.
    209         *
    210         * @return The maximum height.
    211         */
    212        public double getMaxHeight() {
    213            return this.maxHeight;
    214        }
    215
    216        /**
    217         * Sets the maximum height.
    218         *
    219         * @param max  the maximum height.
    220         */
    221        public void setMaxHeight(double max) {
    222            this.maxHeight = max;
    223        }
    224
    225        /**
    226         * Draws the annotation.  This method is called by the drawing code in the
    227         * {@link XYPlot} class, you don't normally need to call this method
    228         * directly.
    229         *
    230         * @param g2  the graphics device.
    231         * @param plot  the plot.
    232         * @param dataArea  the data area.
    233         * @param domainAxis  the domain axis.
    234         * @param rangeAxis  the range axis.
    235         * @param rendererIndex  the renderer index.
    236         * @param info  if supplied, this info object will be populated with
    237         *              entity information.
    238         */
    239        public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea,
    240                         ValueAxis domainAxis, ValueAxis rangeAxis,
    241                         int rendererIndex,
    242                         PlotRenderingInfo info) {
    243
    244            PlotOrientation orientation = plot.getOrientation();
    245            AxisLocation domainAxisLocation = plot.getDomainAxisLocation();
    246            AxisLocation rangeAxisLocation = plot.getRangeAxisLocation();
    247            RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
    248                    domainAxisLocation, orientation);
    249            RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
    250                    rangeAxisLocation, orientation);
    251            Range xRange = domainAxis.getRange();
    252            Range yRange = rangeAxis.getRange();
    253            double anchorX = 0.0;
    254            double anchorY = 0.0;
    255            if (this.coordinateType == XYCoordinateType.RELATIVE) {
    256                anchorX = xRange.getLowerBound() + (this.x * xRange.getLength());
    257                anchorY = yRange.getLowerBound() + (this.y * yRange.getLength());
    258            }
    259            else {
    260                anchorX = domainAxis.valueToJava2D(this.x, dataArea, domainEdge);
    261                anchorY = rangeAxis.valueToJava2D(this.y, dataArea, rangeEdge);
    262            }
    263
    264            float j2DX = (float) domainAxis.valueToJava2D(anchorX, dataArea,
    265                    domainEdge);
    266            float j2DY = (float) rangeAxis.valueToJava2D(anchorY, dataArea,
    267                    rangeEdge);
    268            float xx = 0.0f;
    269            float yy = 0.0f;
    270            if (orientation == PlotOrientation.HORIZONTAL) {
    271                xx = j2DY;
    272                yy = j2DX;
    273            }
    274            else if (orientation == PlotOrientation.VERTICAL) {
    275                xx = j2DX;
    276                yy = j2DY;
    277            }
    278
    279            double maxW = dataArea.getWidth();
    280            double maxH = dataArea.getHeight();
    281            if (this.coordinateType == XYCoordinateType.RELATIVE) {
    282                if (this.maxWidth > 0.0) {
    283                    maxW = maxW * this.maxWidth;
    284                }
    285                if (this.maxHeight > 0.0) {
    286                    maxH = maxH * this.maxHeight;
    287                }
    288            }
    289            if (this.coordinateType == XYCoordinateType.DATA) {
    290                maxW = this.maxWidth;
    291                maxH = this.maxHeight;
    292            }
    293            RectangleConstraint rc = new RectangleConstraint(
    294                    new Range(0, maxW), new Range(0, maxH));
    295
    296            Size2D size = this.title.arrange(g2, rc);
    297            Rectangle2D titleRect = new Rectangle2D.Double(0, 0, size.width,
    298                    size.height);
    299            Point2D anchorPoint = RectangleAnchor.coordinates(titleRect,
    300                    this.anchor);
    301            xx = xx - (float) anchorPoint.getX();
    302            yy = yy - (float) anchorPoint.getY();
    303            titleRect.setRect(xx, yy, titleRect.getWidth(), titleRect.getHeight());
    304            BlockParams p = new BlockParams();
    305            if (info != null) {
    306                if (info.getOwner().getEntityCollection() != null) {
    307                    p.setGenerateEntities(true);
    308                }
    309            }
    310            Object result = this.title.draw(g2, titleRect, p);
    311            if (info != null) {
    312                if (result instanceof EntityBlockResult) {
    313                    EntityBlockResult ebr = (EntityBlockResult) result;
    314                    info.getOwner().getEntityCollection().addAll(
    315                            ebr.getEntityCollection());
    316                }
    317                String toolTip = getToolTipText();
    318                String url = getURL();
    319                if (toolTip != null || url != null) {
    320                    addEntity(info, new Rectangle2D.Float(xx, yy,
    321                            (float) size.width, (float) size.height),
    322                            rendererIndex, toolTip, url);
    323                }
    324            }
    325        }
    326
    327        /**
    328         * Tests this object for equality with an arbitrary object.
    329         *
    330         * @param obj  the object (<code>null</code> permitted).
    331         *
    332         * @return A boolean.
    333         */
    334        public boolean equals(Object obj) {
    335            if (obj == this) {
    336                return true;
    337            }
    338            if (!(obj instanceof XYTitleAnnotation)) {
    339                return false;
    340            }
    341            XYTitleAnnotation that = (XYTitleAnnotation) obj;
    342            if (this.coordinateType != that.coordinateType) {
    343                return false;
    344            }
    345            if (this.x != that.x) {
    346                return false;
    347            }
    348            if (this.y != that.y) {
    349                return false;
    350            }
    351            if (this.maxWidth != that.maxWidth) {
    352                return false;
    353            }
    354            if (this.maxHeight != that.maxHeight) {
    355                return false;
    356            }
    357            if (!ObjectUtilities.equal(this.title, that.title)) {
    358                return false;
    359            }
    360            if (!this.anchor.equals(that.anchor)) {
    361                return false;
    362            }
    363            return super.equals(obj);
    364        }
    365
    366        /**
    367         * Returns a hash code for this object.
    368         *
    369         * @return A hash code.
    370         */
    371        public int hashCode() {
    372            int result = 193;
    373            result = HashUtilities.hashCode(result, this.anchor);
    374            result = HashUtilities.hashCode(result, this.coordinateType);
    375            result = HashUtilities.hashCode(result, this.x);
    376            result = HashUtilities.hashCode(result, this.y);
    377            result = HashUtilities.hashCode(result, this.maxWidth);
    378            result = HashUtilities.hashCode(result, this.maxHeight);
    379            result = HashUtilities.hashCode(result, this.title);
    380            return result;
    381        }
    382
    383        /**
    384         * Returns a clone of the annotation.
    385         *
    386         * @return A clone.
    387         *
    388         * @throws CloneNotSupportedException  if the annotation can't be cloned.
    389         */
    390        public Object clone() throws CloneNotSupportedException {
    391            return super.clone();
    392        }
    393
    394    }