    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, 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     * LegendGraphic.java
    029     * ------------------
    030     * (C) Copyright 2004, 2005, by Object Refinery Limited.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: LegendGraphic.java,v 1.9.2.3 2005/11/24 09:57:19 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 26-Oct-2004 : Version 1 (DG);
    040     * 21-Jan-2005 : Modified return type of RectangleAnchor.coordinates()
    041     *               method (DG);
    042     * 20-Apr-2005 : Added new draw() method (DG);
    043     * 13-May-2005 : Fixed to respect margin, border and padding settings (DG);
    044     * 01-Sep-2005 : Implemented PublicCloneable (DG);
    045     *
    046     */
    047
    048    package org.jfree.chart.title;
    049
    050    import java.awt.Graphics2D;
    051    import java.awt.Paint;
    052    import java.awt.Shape;
    053    import java.awt.Stroke;
    054    import java.awt.geom.Point2D;
    055    import java.awt.geom.Rectangle2D;
    056    import java.io.IOException;
    057    import java.io.ObjectInputStream;
    058    import java.io.ObjectOutputStream;
    059
    060    import org.jfree.chart.block.AbstractBlock;
    061    import org.jfree.chart.block.Block;
    062    import org.jfree.chart.block.LengthConstraintType;
    063    import org.jfree.chart.block.RectangleConstraint;
    064    import org.jfree.io.SerialUtilities;
    065    import org.jfree.ui.RectangleAnchor;
    066    import org.jfree.ui.Size2D;
    067    import org.jfree.util.ObjectUtilities;
    068    import org.jfree.util.PaintUtilities;
    069    import org.jfree.util.PublicCloneable;
    070    import org.jfree.util.ShapeUtilities;
    071
    072    /**
    073     * The graphical item within a legend item.
    074     */
    075    public class LegendGraphic extends AbstractBlock
    076                               implements Block, PublicCloneable {
    077
    078        /**
    079         * A flag that controls whether or not the shape is visible - see also
    080         * lineVisible.
    081         */
    082        private boolean shapeVisible;
    083
    084        /**
    085         * The shape to display.  To allow for accurate positioning, the center
    086         * of the shape should be at (0, 0).
    087         */
    088        private transient Shape shape;
    089
    090        /**
    091         * Defines the location within the block to which the shape will be aligned.
    092         */
    093        private RectangleAnchor shapeLocation;
    094
    095        /**
    096         * Defines the point on the shape's bounding rectangle that will be
    097         * aligned to the drawing location when the shape is rendered.
    098         */
    099        private RectangleAnchor shapeAnchor;
    100
    101        /** A flag that controls whether or not the shape is filled. */
    102        private boolean shapeFilled;
    103
    104        /** The fill paint for the shape. */
    105        private transient Paint fillPaint;
    106
    107        /** A flag that controls whether or not the shape outline is visible. */
    108        private boolean shapeOutlineVisible;
    109
    110        /** The outline paint for the shape. */
    111        private transient Paint outlinePaint;
    112
    113        /** The outline stroke for the shape. */
    114        private transient Stroke outlineStroke;
    115
    116        /**
    117         * A flag that controls whether or not the line is visible - see also
    118         * shapeVisible.
    119         */
    120        private boolean lineVisible;
    121
    122        /** The line. */
    123        private transient Shape line;
    124
    125        /** The line stroke. */
    126        private transient Stroke lineStroke;
    127
    128        /** The line paint. */
    129        private transient Paint linePaint;
    130
    131        /**
    132         * Creates a new legend graphic.
    133         *
    134         * @param shape  the shape (<code>null</code> not permitted).
    135         * @param fillPaint  the fill paint (<code>null</code> not permitted).
    136         */
    137        public LegendGraphic(Shape shape, Paint fillPaint) {
    138            if (shape == null) {
    139                throw new IllegalArgumentException("Null 'shape' argument.");
    140            }
    141            if (fillPaint == null) {
    142                throw new IllegalArgumentException("Null 'fillPaint' argument.");
    143            }
    144            this.shapeVisible = true;
    145            this.shape = shape;
    146            this.shapeAnchor = RectangleAnchor.CENTER;
    147            this.shapeLocation = RectangleAnchor.CENTER;
    148            this.shapeFilled = true;
    149            this.fillPaint = fillPaint;
    150            setPadding(2.0, 2.0, 2.0, 2.0);
    151        }
    152
    153        /**
    154         * Returns a flag that controls whether or not the shape
    155         * is visible.
    156         *
    157         * @return A boolean.
    158         */
    159        public boolean isShapeVisible() {
    160            return this.shapeVisible;
    161        }
    162
    163        /**
    164         * Sets a flag that controls whether or not the shape is
    165         * visible.
    166         *
    167         * @param visible  the flag.
    168         */
    169        public void setShapeVisible(boolean visible) {
    170            this.shapeVisible = visible;
    171        }
    172
    173        /**
    174         * Returns the shape.
    175         *
    176         * @return The shape.
    177         */
    178        public Shape getShape() {
    179            return this.shape;
    180        }
    181
    182        /**
    183         * Sets the shape.
    184         *
    185         * @param shape  the shape.
    186         */
    187        public void setShape(Shape shape) {
    188            this.shape = shape;
    189        }
    190
    191        /**
    192         * Returns a flag that controls whether or not the shapes
    193         * are filled.
    194         *
    195         * @return A boolean.
    196         */
    197        public boolean isShapeFilled() {
    198            return this.shapeFilled;
    199        }
    200
    201        /**
    202         * Sets a flag that controls whether or not the shape is
    203         * filled.
    204         *
    205         * @param filled  the flag.
    206         */
    207        public void setShapeFilled(boolean filled) {
    208            this.shapeFilled = filled;
    209        }
    210
    211        /**
    212         * Returns the paint used to fill the shape.
    213         *
    214         * @return The fill paint.
    215         */
    216        public Paint getFillPaint() {
    217            return this.fillPaint;
    218        }
    219
    220        /**
    221         * Sets the paint used to fill the shape.
    222         *
    223         * @param paint  the paint.
    224         */
    225        public void setFillPaint(Paint paint) {
    226            this.fillPaint = paint;
    227        }
    228
    229        /**
    230         * Returns a flag that controls whether the shape outline is visible.
    231         *
    232         * @return A boolean.
    233         */
    234        public boolean isShapeOutlineVisible() {
    235            return this.shapeOutlineVisible;
    236        }
    237
    238        /**
    239         * Sets a flag that controls whether or not the shape outline
    240         * is visible.
    241         *
    242         * @param visible  the flag.
    243         */
    244        public void setShapeOutlineVisible(boolean visible) {
    245            this.shapeOutlineVisible = visible;
    246        }
    247
    248        /**
    249         * Returns the outline paint.
    250         *
    251         * @return The paint.
    252         */
    253        public Paint getOutlinePaint() {
    254            return this.outlinePaint;
    255        }
    256
    257        /**
    258         * Sets the outline paint.
    259         *
    260         * @param paint  the paint.
    261         */
    262        public void setOutlinePaint(Paint paint) {
    263            this.outlinePaint = paint;
    264        }
    265
    266        /**
    267         * Returns the outline stroke.
    268         *
    269         * @return The stroke.
    270         */
    271        public Stroke getOutlineStroke() {
    272            return this.outlineStroke;
    273        }
    274
    275        /**
    276         * Sets the outline stroke.
    277         *
    278         * @param stroke  the stroke.
    279         */
    280        public void setOutlineStroke(Stroke stroke) {
    281            this.outlineStroke = stroke;
    282        }
    283
    284        /**
    285         * Returns the shape anchor.
    286         *
    287         * @return The shape anchor.
    288         */
    289        public RectangleAnchor getShapeAnchor() {
    290            return this.shapeAnchor;
    291        }
    292
    293        /**
    294         * Sets the shape anchor.  This defines a point on the shapes bounding
    295         * rectangle that will be used to align the shape to a location.
    296         *
    297         * @param anchor  the anchor (<code>null</code> not permitted).
    298         */
    299        public void setShapeAnchor(RectangleAnchor anchor) {
    300            if (anchor == null) {
    301                throw new IllegalArgumentException("Null 'anchor' argument.");
    302            }
    303            this.shapeAnchor = anchor;
    304        }
    305
    306        /**
    307         * Returns the shape location.
    308         *
    309         * @return The shape location.
    310         */
    311        public RectangleAnchor getShapeLocation() {
    312            return this.shapeLocation;
    313        }
    314
    315        /**
    316         * Sets the shape location.  This defines a point within the drawing
    317         * area that will be used to align the shape to.
    318         *
    319         * @param location  the location (<code>null</code> not permitted).
    320         */
    321        public void setShapeLocation(RectangleAnchor location) {
    322            if (location == null) {
    323                throw new IllegalArgumentException("Null 'location' argument.");
    324            }
    325            this.shapeLocation = location;
    326        }
    327
    328        /**
    329         * Returns the flag that controls whether or not the line is visible.
    330         *
    331         * @return A boolean.
    332         */
    333        public boolean isLineVisible() {
    334            return this.lineVisible;
    335        }
    336
    337        /**
    338         * Sets the flag that controls whether or not the line is visible.
    339         *
    340         * @param visible  the flag.
    341         */
    342        public void setLineVisible(boolean visible) {
    343            this.lineVisible = visible;
    344        }
    345
    346        /**
    347         * Returns the line centered about (0, 0).
    348         *
    349         * @return The line.
    350         */
    351        public Shape getLine() {
    352            return this.line;
    353        }
    354
    355        /**
    356         * Sets the line.  A Shape is used here, because then you can use Line2D,
    357         * GeneralPath or any other Shape to represent the line.
    358         *
    359         * @param line  the line.
    360         */
    361        public void setLine(Shape line) {
    362            this.line = line;
    363        }
    364
    365        /**
    366         * Returns the line paint.
    367         *
    368         * @return The paint.
    369         */
    370        public Paint getLinePaint() {
    371            return this.linePaint;
    372        }
    373
    374        /**
    375         * Sets the line paint.
    376         *
    377         * @param paint  the paint.
    378         */
    379        public void setLinePaint(Paint paint) {
    380            this.linePaint = paint;
    381        }
    382
    383        /**
    384         * Returns the line stroke.
    385         *
    386         * @return The stroke.
    387         */
    388        public Stroke getLineStroke() {
    389            return this.lineStroke;
    390        }
    391
    392        /**
    393         * Sets the line stroke.
    394         *
    395         * @param stroke  the stroke.
    396         */
    397        public void setLineStroke(Stroke stroke) {
    398            this.lineStroke = stroke;
    399        }
    400
    401        /**
    402         * Arranges the contents of the block, within the given constraints, and
    403         * returns the block size.
    404         *
    405         * @param g2  the graphics device.
    406         * @param constraint  the constraint (<code>null</code> not permitted).
    407         *
    408         * @return The block size (in Java2D units, never <code>null</code>).
    409         */
    410        public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
    411            RectangleConstraint contentConstraint = toContentConstraint(constraint);
    412            LengthConstraintType w = contentConstraint.getWidthConstraintType();
    413            LengthConstraintType h = contentConstraint.getHeightConstraintType();
    414            Size2D contentSize = null;
    415            if (w == LengthConstraintType.NONE) {
    416                if (h == LengthConstraintType.NONE) {
    417                    contentSize = arrangeNN(g2);
    418                }
    419                else if (h == LengthConstraintType.RANGE) {
    420                    throw new RuntimeException("Not yet implemented.");
    421                }
    422                else if (h == LengthConstraintType.FIXED) {
    423                    throw new RuntimeException("Not yet implemented.");
    424                }
    425            }
    426            else if (w == LengthConstraintType.RANGE) {
    427                if (h == LengthConstraintType.NONE) {
    428                    throw new RuntimeException("Not yet implemented.");
    429                }
    430                else if (h == LengthConstraintType.RANGE) {
    431                    throw new RuntimeException("Not yet implemented.");
    432                }
    433                else if (h == LengthConstraintType.FIXED) {
    434                    throw new RuntimeException("Not yet implemented.");
    435                }
    436            }
    437            else if (w == LengthConstraintType.FIXED) {
    438                if (h == LengthConstraintType.NONE) {
    439                    throw new RuntimeException("Not yet implemented.");
    440                }
    441                else if (h == LengthConstraintType.RANGE) {
    442                    throw new RuntimeException("Not yet implemented.");
    443                }
    444                else if (h == LengthConstraintType.FIXED) {
    445                    contentSize = new Size2D(
    446                        contentConstraint.getWidth(),
    447                        contentConstraint.getHeight()
    448                    );
    449                }
    450            }
    451            return new Size2D(
    452                calculateTotalWidth(contentSize.getWidth()),
    453                calculateTotalHeight(contentSize.getHeight())
    454            );
    455        }
    456
    457        /**
    458         * Performs the layout with no constraint, so the content size is
    459         * determined by the bounds of the shape and/or line drawn to represent
    460         * the series.
    461         *
    462         * @param g2  the graphics device.
    463         *
    464         * @return  The content size.
    465         */
    466        protected Size2D arrangeNN(Graphics2D g2) {
    467            Rectangle2D contentSize = new Rectangle2D.Double();
    468            if (this.line != null) {
    469                contentSize.setRect(this.line.getBounds2D());
    470            }
    471            if (this.shape != null) {
    472                contentSize = contentSize.createUnion(this.shape.getBounds2D());
    473            }
    474            return new Size2D(contentSize.getWidth(), contentSize.getHeight());
    475        }
    476
    477        /**
    478         * Draws the graphic item within the specified area.
    479         *
    480         * @param g2  the graphics device.
    481         * @param area  the area.
    482         */
    483        public void draw(Graphics2D g2, Rectangle2D area) {
    484
    485            area = trimMargin(area);
    486            drawBorder(g2, area);
    487            area = trimBorder(area);
    488            area = trimPadding(area);
    489
    490            if (this.lineVisible) {
    491                Point2D location = RectangleAnchor.coordinates(
    492                    area, this.shapeLocation
    493                );
    494                Shape aLine = ShapeUtilities.createTranslatedShape(
    495                    getLine(), this.shapeAnchor, location.getX(), location.getY()
    496                );
    497                g2.setPaint(this.linePaint);
    498                g2.setStroke(this.lineStroke);
    499                g2.draw(aLine);
    500            }
    501
    502            if (this.shapeVisible) {
    503                Point2D location = RectangleAnchor.coordinates(
    504                    area, this.shapeLocation
    505                );
    506
    507                Shape s = ShapeUtilities.createTranslatedShape(
    508                    this.shape, this.shapeAnchor, location.getX(), location.getY()
    509                );
    510                if (this.shapeFilled) {
    511                    g2.setPaint(this.fillPaint);
    512                    g2.fill(s);
    513                }
    514                if (this.shapeOutlineVisible) {
    515                    g2.setPaint(this.outlinePaint);
    516                    g2.setStroke(this.outlineStroke);
    517                    g2.draw(s);
    518                }
    519            }
    520
    521        }
    522
    523        /**
    524         * Draws the block within the specified area.
    525         *
    526         * @param g2  the graphics device.
    527         * @param area  the area.
    528         * @param params  ignored (<code>null</code> permitted).
    529         *
    530         * @return Always <code>null</code>.
    531         */
    532        public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
    533            draw(g2, area);
    534            return null;
    535        }
    536
    537        /**
    538         * Tests this <code>LegendGraphic</code> instance for equality with an
    539         * arbitrary object.
    540         *
    541         * @param obj  the object (<code>null</code> permitted).
    542         *
    543         * @return A boolean.
    544         */
    545        public boolean equals(Object obj) {
    546            if (!(obj instanceof LegendGraphic)) {
    547                return false;
    548            }
    549            LegendGraphic that = (LegendGraphic) obj;
    550            if (this.shapeVisible != that.shapeVisible) {
    551                return false;
    552            }
    553            if (!ShapeUtilities.equal(this.shape, that.shape)) {
    554                return false;
    555            }
    556            if (this.shapeFilled != that.shapeFilled) {
    557                return false;
    558            }
    559            if (!PaintUtilities.equal(this.fillPaint, that.fillPaint)) {
    560                return false;
    561            }
    562            if (this.shapeOutlineVisible != that.shapeOutlineVisible) {
    563                return false;
    564            }
    565            if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) {
    566                return false;
    567            }
    568            if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) {
    569                return false;
    570            }
    571            if (this.shapeAnchor != that.shapeAnchor) {
    572                return false;
    573            }
    574            if (this.shapeLocation != that.shapeLocation) {
    575                return false;
    576            }
    577            if (this.lineVisible != that.lineVisible) {
    578                return false;
    579            }
    580            if (!ShapeUtilities.equal(this.line, that.line)) {
    581                return false;
    582            }
    583            if (!PaintUtilities.equal(this.linePaint, that.linePaint)) {
    584                return false;
    585            }
    586            if (!ObjectUtilities.equal(this.lineStroke, that.lineStroke)) {
    587                return false;
    588            }
    589            if (!super.equals(obj)) {
    590                return false;
    591            }
    592            return true;
    593        }
    594
    595        /**
    596         * Returns a clone of this <code>LegendGraphic</code> instance.
    597         *
    598         * @return A clone of this <code>LegendGraphic</code> instance.
    599         *
    600         * @throws CloneNotSupportedException if there is a problem cloning.
    601         */
    602        public Object clone() throws CloneNotSupportedException {
    603            return super.clone();
    604        }
    605
    606        /**
    607         * Provides serialization support.
    608         *
    609         * @param stream  the output stream.
    610         *
    611         * @throws IOException  if there is an I/O error.
    612         */
    613        private void writeObject(ObjectOutputStream stream) throws IOException {
    614            stream.defaultWriteObject();
    615            SerialUtilities.writeShape(this.shape, stream);
    616            SerialUtilities.writePaint(this.fillPaint, stream);
    617            SerialUtilities.writePaint(this.outlinePaint, stream);
    618            SerialUtilities.writeStroke(this.outlineStroke, stream);
    619            SerialUtilities.writeShape(this.line, stream);
    620            SerialUtilities.writePaint(this.linePaint, stream);
    621            SerialUtilities.writeStroke(this.lineStroke, stream);
    622        }
    623
    624        /**
    625         * Provides serialization support.
    626         *
    627         * @param stream  the input stream.
    628         *
    629         * @throws IOException  if there is an I/O error.
    630         * @throws ClassNotFoundException  if there is a classpath problem.
    631         */
    632        private void readObject(ObjectInputStream stream)
    633            throws IOException, ClassNotFoundException
    634        {
    635            stream.defaultReadObject();
    636            this.shape = SerialUtilities.readShape(stream);
    637            this.fillPaint = SerialUtilities.readPaint(stream);
    638            this.outlinePaint = SerialUtilities.readPaint(stream);
    639            this.outlineStroke = SerialUtilities.readStroke(stream);
    640            this.line = SerialUtilities.readShape(stream);
    641            this.linePaint = SerialUtilities.readPaint(stream);
    642            this.lineStroke = SerialUtilities.readStroke(stream);
    643        }
    644
    645    }