    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     * LineAndShapeRenderer.java
    029     * -------------------------
    030     * (C) Copyright 2001-2007, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   Mark Watson (www.markwatson.com);
    034     *                   Jeremy Bowman;
    035     *                   Richard Atkinson;
    036     *                   Christian W. Zuckschwerdt;
    037     *
    038     * Changes
    039     * -------
    040     * 23-Oct-2001 : Version 1 (DG);
    041     * 15-Nov-2001 : Modified to allow for null data values (DG);
    042     * 16-Jan-2002 : Renamed HorizontalCategoryItemRenderer.java
    043     *               --> CategoryItemRenderer.java (DG);
    044     * 05-Feb-2002 : Changed return type of the drawCategoryItem method from void
    045     *               to Shape, as part of the tooltips implementation (DG);
    046     * 11-May-2002 : Support for value label drawing (JB);
    047     * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
    048     * 25-Jun-2002 : Removed redundant import (DG);
    049     * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
    050     *               for HTML image maps (RA);
    051     * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
    052     * 11-Oct-2002 : Added new constructor to incorporate tool tip and URL
    053     *               generators (DG);
    054     * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
    055     *               CategoryToolTipGenerator interface (DG);
    056     * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
    057     * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
    058     *               for category spacing (DG);
    059     * 17-Jan-2003 : Moved plot classes to a separate package (DG);
    060     * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in drawItem()
    061     *               method (DG);
    062     * 12-May-2003 : Modified to take into account the plot orientation (DG);
    063     * 29-Jul-2003 : Amended code that doesn't compile with JDK 1.2.2 (DG);
    064     * 30-Jul-2003 : Modified entity constructor (CZ);
    065     * 22-Sep-2003 : Fixed cloning (DG);
    066     * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
    067     *               override easier (DG);
    068     * 16-Jun-2004 : Fixed bug (id=972454) with label positioning on horizontal
    069     *               charts (DG);
    070     * 15-Oct-2004 : Updated equals() method (DG);
    071     * 05-Nov-2004 : Modified drawItem() signature (DG);
    072     * 11-Nov-2004 : Now uses ShapeUtilities class to translate shapes (DG);
    073     * 27-Jan-2005 : Changed attribute names, modified constructor and removed
    074     *               constants (DG);
    075     * 01-Feb-2005 : Removed unnecessary constants (DG);
    076     * 15-Mar-2005 : Fixed bug 1163897, concerning outlines for shapes (DG);
    077     * 13-Apr-2005 : Check flags that control series visibility (DG);
    078     * 20-Apr-2005 : Use generators for legend labels, tooltips and URLs (DG);
    079     * 09-Jun-2005 : Use addItemEntity() method (DG);
    080     * ------------- JFREECHART 1.0.x ---------------------------------------------
    081     * 25-May-2006 : Added check to drawItem() to detect when both the line and
    082     *               the shape are not visible (DG);
    083     * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
    084     * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
    085     * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
    086     * 24-Sep-2007 : Deprecated redundant fields/methods (DG);
    087     * 27-Sep-2007 : Added option to offset series x-position within category (DG);
    088     *
    089     */
    090
    091    package org.jfree.chart.renderer.category;
    092
    093    import java.awt.Graphics2D;
    094    import java.awt.Paint;
    095    import java.awt.Shape;
    096    import java.awt.Stroke;
    097    import java.awt.geom.Line2D;
    098    import java.awt.geom.Rectangle2D;
    099    import java.io.Serializable;
    100
    101    import org.jfree.chart.LegendItem;
    102    import org.jfree.chart.axis.CategoryAxis;
    103    import org.jfree.chart.axis.ValueAxis;
    104    import org.jfree.chart.entity.EntityCollection;
    105    import org.jfree.chart.event.RendererChangeEvent;
    106    import org.jfree.chart.plot.CategoryPlot;
    107    import org.jfree.chart.plot.PlotOrientation;
    108    import org.jfree.data.category.CategoryDataset;
    109    import org.jfree.util.BooleanList;
    110    import org.jfree.util.BooleanUtilities;
    111    import org.jfree.util.ObjectUtilities;
    112    import org.jfree.util.PublicCloneable;
    113    import org.jfree.util.ShapeUtilities;
    114
    115    /**
    116     * A renderer that draws shapes for each data item, and lines between data
    117     * items (for use with the {@link CategoryPlot} class).
    118     */
    119    public class LineAndShapeRenderer extends AbstractCategoryItemRenderer
    120            implements Cloneable, PublicCloneable, Serializable {
    121
    122        /** For serialization. */
    123        private static final long serialVersionUID = -197749519869226398L;
    124
    125        /**
    126         * A flag that controls whether or not lines are visible for ALL series.
    127         *
    128         * @deprecated As of 1.0.7 (this override flag is unnecessary).
    129         */
    130        private Boolean linesVisible;
    131
    132        /**
    133         * A table of flags that control (per series) whether or not lines are
    134         * visible.
    135         */
    136        private BooleanList seriesLinesVisible;
    137
    138        /**
    139         * A flag indicating whether or not lines are drawn between non-null
    140         * points.
    141         */
    142        private boolean baseLinesVisible;
    143
    144        /**
    145         * A flag that controls whether or not shapes are visible for ALL series.
    146         *
    147         * @deprecated As of 1.0.7 (this override flag is unnecessary).
    148         */
    149        private Boolean shapesVisible;
    150
    151        /**
    152         * A table of flags that control (per series) whether or not shapes are
    153         * visible.
    154         */
    155        private BooleanList seriesShapesVisible;
    156
    157        /** The default value returned by the getShapeVisible() method. */
    158        private boolean baseShapesVisible;
    159
    160        /**
    161         * A flag that controls whether or not shapes are filled for ALL series.
    162         *
    163         * @deprecated As of 1.0.7 (this override flag is unnecessary).
    164         */
    165        private Boolean shapesFilled;
    166
    167        /**
    168         * A table of flags that control (per series) whether or not shapes are
    169         * filled.
    170         */
    171        private BooleanList seriesShapesFilled;
    172
    173        /** The default value returned by the getShapeFilled() method. */
    174        private boolean baseShapesFilled;
    175
    176        /**
    177         * A flag that controls whether the fill paint is used for filling
    178         * shapes.
    179         */
    180        private boolean useFillPaint;
    181
    182        /** A flag that controls whether outlines are drawn for shapes. */
    183        private boolean drawOutlines;
    184
    185        /**
    186         * A flag that controls whether the outline paint is used for drawing shape
    187         * outlines - if not, the regular series paint is used.
    188         */
    189        private boolean useOutlinePaint;
    190
    191        /**
    192         * A flag that controls whether or not the x-position for each item is
    193         * offset within the category according to the series.
    194         *
    195         * @since 1.0.7
    196         */
    197        private boolean useSeriesOffset;
    198
    199        /**
    200         * The item margin used for series offsetting - this allows the positioning
    201         * to match the bar positions of the {@link BarRenderer} class.
    202         *
    203         * @since 1.0.7
    204         */
    205        private double itemMargin;
    206
    207        /**
    208         * Creates a renderer with both lines and shapes visible by default.
    209         */
    210        public LineAndShapeRenderer() {
    211            this(true, true);
    212        }
    213
    214        /**
    215         * Creates a new renderer with lines and/or shapes visible.
    216         *
    217         * @param lines  draw lines?
    218         * @param shapes  draw shapes?
    219         */
    220        public LineAndShapeRenderer(boolean lines, boolean shapes) {
    221            super();
    222            this.linesVisible = null;
    223            this.seriesLinesVisible = new BooleanList();
    224            this.baseLinesVisible = lines;
    225            this.shapesVisible = null;
    226            this.seriesShapesVisible = new BooleanList();
    227            this.baseShapesVisible = shapes;
    228            this.shapesFilled = null;
    229            this.seriesShapesFilled = new BooleanList();
    230            this.baseShapesFilled = true;
    231            this.useFillPaint = false;
    232            this.drawOutlines = true;
    233            this.useOutlinePaint = false;
    234            this.useSeriesOffset = false;  // preserves old behaviour
    235            this.itemMargin = 0.0;
    236        }
    237
    238        // LINES VISIBLE
    239
    240        /**
    241         * Returns the flag used to control whether or not the line for an item is
    242         * visible.
    243         *
    244         * @param series  the series index (zero-based).
    245         * @param item  the item index (zero-based).
    246         *
    247         * @return A boolean.
    248         */
    249        public boolean getItemLineVisible(int series, int item) {
    250            Boolean flag = this.linesVisible;
    251            if (flag == null) {
    252                flag = getSeriesLinesVisible(series);
    253            }
    254            if (flag != null) {
    255                return flag.booleanValue();
    256            }
    257            else {
    258                return this.baseLinesVisible;
    259            }
    260        }
    261
    262        /**
    263         * Returns a flag that controls whether or not lines are drawn for ALL
    264         * series.  If this flag is <code>null</code>, then the "per series"
    265         * settings will apply.
    266         *
    267         * @return A flag (possibly <code>null</code>).
    268         *
    269         * @see #setLinesVisible(Boolean)
    270         *
    271         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    272         *     use the per-series and base (default) settings).
    273         */
    274        public Boolean getLinesVisible() {
    275            return this.linesVisible;
    276        }
    277
    278        /**
    279         * Sets a flag that controls whether or not lines are drawn between the
    280         * items in ALL series, and sends a {@link RendererChangeEvent} to all
    281         * registered listeners.  You need to set this to <code>null</code> if you
    282         * want the "per series" settings to apply.
    283         *
    284         * @param visible  the flag (<code>null</code> permitted).
    285         *
    286         * @see #getLinesVisible()
    287         *
    288         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    289         *     use the per-series and base (default) settings).
    290         */
    291        public void setLinesVisible(Boolean visible) {
    292            this.linesVisible = visible;
    293            fireChangeEvent();
    294        }
    295
    296        /**
    297         * Sets a flag that controls whether or not lines are drawn between the
    298         * items in ALL series, and sends a {@link RendererChangeEvent} to all
    299         * registered listeners.
    300         *
    301         * @param visible  the flag.
    302         *
    303         * @see #getLinesVisible()
    304         *
    305         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    306         *     use the per-series and base (default) settings).
    307         */
    308        public void setLinesVisible(boolean visible) {
    309            setLinesVisible(BooleanUtilities.valueOf(visible));
    310        }
    311
    312        /**
    313         * Returns the flag used to control whether or not the lines for a series
    314         * are visible.
    315         *
    316         * @param series  the series index (zero-based).
    317         *
    318         * @return The flag (possibly <code>null</code>).
    319         *
    320         * @see #setSeriesLinesVisible(int, Boolean)
    321         */
    322        public Boolean getSeriesLinesVisible(int series) {
    323            return this.seriesLinesVisible.getBoolean(series);
    324        }
    325
    326        /**
    327         * Sets the 'lines visible' flag for a series and sends a
    328         * {@link RendererChangeEvent} to all registered listeners.
    329         *
    330         * @param series  the series index (zero-based).
    331         * @param flag  the flag (<code>null</code> permitted).
    332         *
    333         * @see #getSeriesLinesVisible(int)
    334         */
    335        public void setSeriesLinesVisible(int series, Boolean flag) {
    336            this.seriesLinesVisible.setBoolean(series, flag);
    337            fireChangeEvent();
    338        }
    339
    340        /**
    341         * Sets the 'lines visible' flag for a series and sends a
    342         * {@link RendererChangeEvent} to all registered listeners.
    343         *
    344         * @param series  the series index (zero-based).
    345         * @param visible  the flag.
    346         *
    347         * @see #getSeriesLinesVisible(int)
    348         */
    349        public void setSeriesLinesVisible(int series, boolean visible) {
    350            setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
    351        }
    352
    353        /**
    354         * Returns the base 'lines visible' attribute.
    355         *
    356         * @return The base flag.
    357         *
    358         * @see #getBaseLinesVisible()
    359         */
    360        public boolean getBaseLinesVisible() {
    361            return this.baseLinesVisible;
    362        }
    363
    364        /**
    365         * Sets the base 'lines visible' flag and sends a
    366         * {@link RendererChangeEvent} to all registered listeners.
    367         *
    368         * @param flag  the flag.
    369         *
    370         * @see #getBaseLinesVisible()
    371         */
    372        public void setBaseLinesVisible(boolean flag) {
    373            this.baseLinesVisible = flag;
    374            fireChangeEvent();
    375        }
    376
    377        // SHAPES VISIBLE
    378
    379        /**
    380         * Returns the flag used to control whether or not the shape for an item is
    381         * visible.
    382         *
    383         * @param series  the series index (zero-based).
    384         * @param item  the item index (zero-based).
    385         *
    386         * @return A boolean.
    387         */
    388        public boolean getItemShapeVisible(int series, int item) {
    389            Boolean flag = this.shapesVisible;
    390            if (flag == null) {
    391                flag = getSeriesShapesVisible(series);
    392            }
    393            if (flag != null) {
    394                return flag.booleanValue();
    395            }
    396            else {
    397                return this.baseShapesVisible;
    398            }
    399        }
    400
    401        /**
    402         * Returns the flag that controls whether the shapes are visible for the
    403         * items in ALL series.
    404         *
    405         * @return The flag (possibly <code>null</code>).
    406         *
    407         * @see #setShapesVisible(Boolean)
    408         *
    409         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    410         *     use the per-series and base (default) settings).
    411         */
    412        public Boolean getShapesVisible() {
    413            return this.shapesVisible;
    414        }
    415
    416        /**
    417         * Sets the 'shapes visible' for ALL series and sends a
    418         * {@link RendererChangeEvent} to all registered listeners.
    419         *
    420         * @param visible  the flag (<code>null</code> permitted).
    421         *
    422         * @see #getShapesVisible()
    423         *
    424         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    425         *     use the per-series and base (default) settings).
    426         */
    427        public void setShapesVisible(Boolean visible) {
    428            this.shapesVisible = visible;
    429            fireChangeEvent();
    430        }
    431
    432        /**
    433         * Sets the 'shapes visible' for ALL series and sends a
    434         * {@link RendererChangeEvent} to all registered listeners.
    435         *
    436         * @param visible  the flag.
    437         *
    438         * @see #getShapesVisible()
    439         *
    440         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    441         *     use the per-series and base (default) settings).
    442         */
    443        public void setShapesVisible(boolean visible) {
    444            setShapesVisible(BooleanUtilities.valueOf(visible));
    445        }
    446
    447        /**
    448         * Returns the flag used to control whether or not the shapes for a series
    449         * are visible.
    450         *
    451         * @param series  the series index (zero-based).
    452         *
    453         * @return A boolean.
    454         *
    455         * @see #setSeriesShapesVisible(int, Boolean)
    456         */
    457        public Boolean getSeriesShapesVisible(int series) {
    458            return this.seriesShapesVisible.getBoolean(series);
    459        }
    460
    461        /**
    462         * Sets the 'shapes visible' flag for a series and sends a
    463         * {@link RendererChangeEvent} to all registered listeners.
    464         *
    465         * @param series  the series index (zero-based).
    466         * @param visible  the flag.
    467         *
    468         * @see #getSeriesShapesVisible(int)
    469         */
    470        public void setSeriesShapesVisible(int series, boolean visible) {
    471            setSeriesShapesVisible(series, BooleanUtilities.valueOf(visible));
    472        }
    473
    474        /**
    475         * Sets the 'shapes visible' flag for a series and sends a
    476         * {@link RendererChangeEvent} to all registered listeners.
    477         *
    478         * @param series  the series index (zero-based).
    479         * @param flag  the flag.
    480         *
    481         * @see #getSeriesShapesVisible(int)
    482         */
    483        public void setSeriesShapesVisible(int series, Boolean flag) {
    484            this.seriesShapesVisible.setBoolean(series, flag);
    485            fireChangeEvent();
    486        }
    487
    488        /**
    489         * Returns the base 'shape visible' attribute.
    490         *
    491         * @return The base flag.
    492         *
    493         * @see #setBaseShapesVisible(boolean)
    494         */
    495        public boolean getBaseShapesVisible() {
    496            return this.baseShapesVisible;
    497        }
    498
    499        /**
    500         * Sets the base 'shapes visible' flag and sends a
    501         * {@link RendererChangeEvent} to all registered listeners.
    502         *
    503         * @param flag  the flag.
    504         *
    505         * @see #getBaseShapesVisible()
    506         */
    507        public void setBaseShapesVisible(boolean flag) {
    508            this.baseShapesVisible = flag;
    509            fireChangeEvent();
    510        }
    511
    512        /**
    513         * Returns <code>true</code> if outlines should be drawn for shapes, and
    514         * <code>false</code> otherwise.
    515         *
    516         * @return A boolean.
    517         *
    518         * @see #setDrawOutlines(boolean)
    519         */
    520        public boolean getDrawOutlines() {
    521            return this.drawOutlines;
    522        }
    523
    524        /**
    525         * Sets the flag that controls whether outlines are drawn for
    526         * shapes, and sends a {@link RendererChangeEvent} to all registered
    527         * listeners.
    528         * <P>
    529         * In some cases, shapes look better if they do NOT have an outline, but
    530         * this flag allows you to set your own preference.
    531         *
    532         * @param flag  the flag.
    533         *
    534         * @see #getDrawOutlines()
    535         */
    536        public void setDrawOutlines(boolean flag) {
    537            this.drawOutlines = flag;
    538            fireChangeEvent();
    539        }
    540
    541        /**
    542         * Returns the flag that controls whether the outline paint is used for
    543         * shape outlines.  If not, the regular series paint is used.
    544         *
    545         * @return A boolean.
    546         *
    547         * @see #setUseOutlinePaint(boolean)
    548         */
    549        public boolean getUseOutlinePaint() {
    550            return this.useOutlinePaint;
    551        }
    552
    553        /**
    554         * Sets the flag that controls whether the outline paint is used for shape
    555         * outlines, and sends a {@link RendererChangeEvent} to all registered
    556         * listeners.
    557         *
    558         * @param use  the flag.
    559         *
    560         * @see #getUseOutlinePaint()
    561         */
    562        public void setUseOutlinePaint(boolean use) {
    563            this.useOutlinePaint = use;
    564            fireChangeEvent();
    565        }
    566
    567        // SHAPES FILLED
    568
    569        /**
    570         * Returns the flag used to control whether or not the shape for an item
    571         * is filled. The default implementation passes control to the
    572         * <code>getSeriesShapesFilled</code> method. You can override this method
    573         * if you require different behaviour.
    574         *
    575         * @param series  the series index (zero-based).
    576         * @param item  the item index (zero-based).
    577         *
    578         * @return A boolean.
    579         */
    580        public boolean getItemShapeFilled(int series, int item) {
    581            return getSeriesShapesFilled(series);
    582        }
    583
    584        /**
    585         * Returns the flag used to control whether or not the shapes for a series
    586         * are filled.
    587         *
    588         * @param series  the series index (zero-based).
    589         *
    590         * @return A boolean.
    591         */
    592        public boolean getSeriesShapesFilled(int series) {
    593
    594            // return the overall setting, if there is one...
    595            if (this.shapesFilled != null) {
    596                return this.shapesFilled.booleanValue();
    597            }
    598
    599            // otherwise look up the paint table
    600            Boolean flag = this.seriesShapesFilled.getBoolean(series);
    601            if (flag != null) {
    602                return flag.booleanValue();
    603            }
    604            else {
    605                return this.baseShapesFilled;
    606            }
    607
    608        }
    609
    610        /**
    611         * Returns the flag that controls whether or not shapes are filled for
    612         * ALL series.
    613         *
    614         * @return A Boolean.
    615         *
    616         * @see #setShapesFilled(Boolean)
    617         *
    618         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    619         *     use the per-series and base (default) settings).
    620         */
    621        public Boolean getShapesFilled() {
    622            return this.shapesFilled;
    623        }
    624
    625        /**
    626         * Sets the 'shapes filled' for ALL series and sends a
    627         * {@link RendererChangeEvent} to all registered listeners.
    628         *
    629         * @param filled  the flag.
    630         *
    631         * @see #getShapesFilled()
    632         *
    633         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    634         *     use the per-series and base (default) settings).
    635         */
    636        public void setShapesFilled(boolean filled) {
    637            if (filled) {
    638                setShapesFilled(Boolean.TRUE);
    639            }
    640            else {
    641                setShapesFilled(Boolean.FALSE);
    642            }
    643        }
    644
    645        /**
    646         * Sets the 'shapes filled' for ALL series and sends a
    647         * {@link RendererChangeEvent} to all registered listeners.
    648         *
    649         * @param filled  the flag (<code>null</code> permitted).
    650         *
    651         * @see #getShapesFilled()
    652         *
    653         * @deprecated As of 1.0.7 (the override facility is unnecessary, just
    654         *     use the per-series and base (default) settings).
    655         */
    656        public void setShapesFilled(Boolean filled) {
    657            this.shapesFilled = filled;
    658            fireChangeEvent();
    659        }
    660
    661        /**
    662         * Sets the 'shapes filled' flag for a series and sends a
    663         * {@link RendererChangeEvent} to all registered listeners.
    664         *
    665         * @param series  the series index (zero-based).
    666         * @param filled  the flag.
    667         *
    668         * @see #getSeriesShapesFilled(int)
    669         */
    670        public void setSeriesShapesFilled(int series, Boolean filled) {
    671            this.seriesShapesFilled.setBoolean(series, filled);
    672            fireChangeEvent();
    673        }
    674
    675        /**
    676         * Sets the 'shapes filled' flag for a series and sends a
    677         * {@link RendererChangeEvent} to all registered listeners.
    678         *
    679         * @param series  the series index (zero-based).
    680         * @param filled  the flag.
    681         *
    682         * @see #getSeriesShapesFilled(int)
    683         */
    684        public void setSeriesShapesFilled(int series, boolean filled) {
    685            // delegate
    686            setSeriesShapesFilled(series, BooleanUtilities.valueOf(filled));
    687        }
    688
    689        /**
    690         * Returns the base 'shape filled' attribute.
    691         *
    692         * @return The base flag.
    693         *
    694         * @see #setBaseShapesFilled(boolean)
    695         */
    696        public boolean getBaseShapesFilled() {
    697            return this.baseShapesFilled;
    698        }
    699
    700        /**
    701         * Sets the base 'shapes filled' flag and sends a
    702         * {@link RendererChangeEvent} to all registered listeners.
    703         *
    704         * @param flag  the flag.
    705         *
    706         * @see #getBaseShapesFilled()
    707         */
    708        public void setBaseShapesFilled(boolean flag) {
    709            this.baseShapesFilled = flag;
    710            fireChangeEvent();
    711        }
    712
    713        /**
    714         * Returns <code>true</code> if the renderer should use the fill paint
    715         * setting to fill shapes, and <code>false</code> if it should just
    716         * use the regular paint.
    717         *
    718         * @return A boolean.
    719         *
    720         * @see #setUseFillPaint(boolean)
    721         */
    722        public boolean getUseFillPaint() {
    723            return this.useFillPaint;
    724        }
    725
    726        /**
    727         * Sets the flag that controls whether the fill paint is used to fill
    728         * shapes, and sends a {@link RendererChangeEvent} to all
    729         * registered listeners.
    730         *
    731         * @param flag  the flag.
    732         *
    733         * @see #getUseFillPaint()
    734         */
    735        public void setUseFillPaint(boolean flag) {
    736            this.useFillPaint = flag;
    737            fireChangeEvent();
    738        }
    739
    740        /**
    741         * Returns the flag that controls whether or not the x-position for each
    742         * data item is offset within the category according to the series.
    743         *
    744         * @return A boolean.
    745         *
    746         * @see #setUseSeriesOffset(boolean)
    747         *
    748         * @since 1.0.7
    749         */
    750        public boolean getUseSeriesOffset() {
    751            return this.useSeriesOffset;
    752        }
    753
    754        /**
    755         * Sets the flag that controls whether or not the x-position for each
    756         * data item is offset within its category according to the series, and
    757         * sends a {@link RendererChangeEvent} to all registered listeners.
    758         *
    759         * @param offset  the offset.
    760         *
    761         * @see #getUseSeriesOffset()
    762         *
    763         * @since 1.0.7
    764         */
    765        public void setUseSeriesOffset(boolean offset) {
    766            this.useSeriesOffset = offset;
    767            fireChangeEvent();
    768        }
    769
    770        /**
    771         * Returns the item margin, which is the gap between items within a
    772         * category (expressed as a percentage of the overall category width).
    773         * This can be used to match the offset alignment with the bars drawn by
    774         * a {@link BarRenderer}).
    775         *
    776         * @return The item margin.
    777         *
    778         * @see #setItemMargin(double)
    779         * @see #getUseSeriesOffset()
    780         *
    781         * @since 1.0.7
    782         */
    783        public double getItemMargin() {
    784            return this.itemMargin;
    785        }
    786
    787        /**
    788         * Sets the item margin, which is the gap between items within a category
    789         * (expressed as a percentage of the overall category width), and sends
    790         * a {@link RendererChangeEvent} to all registered listeners.
    791         *
    792         * @param margin  the margin (0.0 <= margin < 1.0).
    793         *
    794         * @see #getItemMargin()
    795         * @see #getUseSeriesOffset()
    796         *
    797         * @since 1.0.7
    798         */
    799        public void setItemMargin(double margin) {
    800            if (margin < 0.0 || margin >= 1.0) {
    801                throw new IllegalArgumentException("Requires 0.0 <= margin < 1.0.");
    802            }
    803            this.itemMargin = margin;
    804            fireChangeEvent();
    805        }
    806
    807        /**
    808         * Returns a legend item for a series.
    809         *
    810         * @param datasetIndex  the dataset index (zero-based).
    811         * @param series  the series index (zero-based).
    812         *
    813         * @return The legend item.
    814         */
    815        public LegendItem getLegendItem(int datasetIndex, int series) {
    816
    817            CategoryPlot cp = getPlot();
    818            if (cp == null) {
    819                return null;
    820            }
    821
    822            if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) {
    823                CategoryDataset dataset = cp.getDataset(datasetIndex);
    824                String label = getLegendItemLabelGenerator().generateLabel(
    825                        dataset, series);
    826                String description = label;
    827                String toolTipText = null;
    828                if (getLegendItemToolTipGenerator() != null) {
    829                    toolTipText = getLegendItemToolTipGenerator().generateLabel(
    830                            dataset, series);
    831                }
    832                String urlText = null;
    833                if (getLegendItemURLGenerator() != null) {
    834                    urlText = getLegendItemURLGenerator().generateLabel(
    835                            dataset, series);
    836                }
    837                Shape shape = lookupSeriesShape(series);
    838                Paint paint = lookupSeriesPaint(series);
    839                Paint fillPaint = (this.useFillPaint
    840                        ? getItemFillPaint(series, 0) : paint);
    841                boolean shapeOutlineVisible = this.drawOutlines;
    842                Paint outlinePaint = (this.useOutlinePaint
    843                        ? getItemOutlinePaint(series, 0) : paint);
    844                Stroke outlineStroke = lookupSeriesOutlineStroke(series);
    845                boolean lineVisible = getItemLineVisible(series, 0);
    846                boolean shapeVisible = getItemShapeVisible(series, 0);
    847                LegendItem result = new LegendItem(label, description, toolTipText,
    848                        urlText, shapeVisible, shape, getItemShapeFilled(series, 0),
    849                        fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke,
    850                        lineVisible, new Line2D.Double(-7.0, 0.0, 7.0, 0.0),
    851                        getItemStroke(series, 0), getItemPaint(series, 0));
    852                result.setDataset(dataset);
    853                result.setDatasetIndex(datasetIndex);
    854                result.setSeriesKey(dataset.getRowKey(series));
    855                result.setSeriesIndex(series);
    856                return result;
    857            }
    858            return null;
    859
    860        }
    861
    862        /**
    863         * This renderer uses two passes to draw the data.
    864         *
    865         * @return The pass count (<code>2</code> for this renderer).
    866         */
    867        public int getPassCount() {
    868            return 2;
    869        }
    870
    871        /**
    872         * Draw a single data item.
    873         *
    874         * @param g2  the graphics device.
    875         * @param state  the renderer state.
    876         * @param dataArea  the area in which the data is drawn.
    877         * @param plot  the plot.
    878         * @param domainAxis  the domain axis.
    879         * @param rangeAxis  the range axis.
    880         * @param dataset  the dataset.
    881         * @param row  the row index (zero-based).
    882         * @param column  the column index (zero-based).
    883         * @param pass  the pass index.
    884         */
    885        public void drawItem(Graphics2D g2, CategoryItemRendererState state,
    886                Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis,
    887                ValueAxis rangeAxis, CategoryDataset dataset, int row, int column,
    888                int pass) {
    889
    890            // do nothing if item is not visible
    891            if (!getItemVisible(row, column)) {
    892                return;
    893            }
    894
    895            // do nothing if both the line and shape are not visible
    896            if (!getItemLineVisible(row, column)
    897                    && !getItemShapeVisible(row, column)) {
    898                return;
    899            }
    900
    901            // nothing is drawn for null...
    902            Number v = dataset.getValue(row, column);
    903            if (v == null) {
    904                return;
    905            }
    906
    907            PlotOrientation orientation = plot.getOrientation();
    908
    909            // current data point...
    910            double x1;
    911            if (this.useSeriesOffset) {
    912                x1 = domainAxis.getCategorySeriesMiddle(dataset.getColumnKey(
    913                        column), dataset.getRowKey(row), dataset, this.itemMargin,
    914                        dataArea, plot.getDomainAxisEdge());
    915            }
    916            else {
    917                x1 = domainAxis.getCategoryMiddle(column, getColumnCount(),
    918                        dataArea, plot.getDomainAxisEdge());
    919            }
    920            double value = v.doubleValue();
    921            double y1 = rangeAxis.valueToJava2D(value, dataArea,
    922                    plot.getRangeAxisEdge());
    923
    924            if (pass == 0 && getItemLineVisible(row, column)) {
    925                if (column != 0) {
    926                    Number previousValue = dataset.getValue(row, column - 1);
    927                    if (previousValue != null) {
    928                        // previous data point...
    929                        double previous = previousValue.doubleValue();
    930                        double x0;
    931                        if (this.useSeriesOffset) {
    932                            x0 = domainAxis.getCategorySeriesMiddle(
    933                                    dataset.getColumnKey(column - 1),
    934                                    dataset.getRowKey(row), dataset,
    935                                    this.itemMargin, dataArea,
    936                                    plot.getDomainAxisEdge());
    937                        }
    938                        else {
    939                            x0 = domainAxis.getCategoryMiddle(column - 1,
    940                                    getColumnCount(), dataArea,
    941                                    plot.getDomainAxisEdge());
    942                        }
    943                        double y0 = rangeAxis.valueToJava2D(previous, dataArea,
    944                                plot.getRangeAxisEdge());
    945
    946                        Line2D line = null;
    947                        if (orientation == PlotOrientation.HORIZONTAL) {
    948                            line = new Line2D.Double(y0, x0, y1, x1);
    949                        }
    950                        else if (orientation == PlotOrientation.VERTICAL) {
    951                            line = new Line2D.Double(x0, y0, x1, y1);
    952                        }
    953                        g2.setPaint(getItemPaint(row, column));
    954                        g2.setStroke(getItemStroke(row, column));
    955                        g2.draw(line);
    956                    }
    957                }
    958            }
    959
    960            if (pass == 1) {
    961                Shape shape = getItemShape(row, column);
    962                if (orientation == PlotOrientation.HORIZONTAL) {
    963                    shape = ShapeUtilities.createTranslatedShape(shape, y1, x1);
    964                }
    965                else if (orientation == PlotOrientation.VERTICAL) {
    966                    shape = ShapeUtilities.createTranslatedShape(shape, x1, y1);
    967                }
    968
    969                if (getItemShapeVisible(row, column)) {
    970                    if (getItemShapeFilled(row, column)) {
    971                        if (this.useFillPaint) {
    972                            g2.setPaint(getItemFillPaint(row, column));
    973                        }
    974                        else {
    975                            g2.setPaint(getItemPaint(row, column));
    976                        }
    977                        g2.fill(shape);
    978                    }
    979                    if (this.drawOutlines) {
    980                        if (this.useOutlinePaint) {
    981                            g2.setPaint(getItemOutlinePaint(row, column));
    982                        }
    983                        else {
    984                            g2.setPaint(getItemPaint(row, column));
    985                        }
    986                        g2.setStroke(getItemOutlineStroke(row, column));
    987                        g2.draw(shape);
    988                    }
    989                }
    990
    991                // draw the item label if there is one...
    992                if (isItemLabelVisible(row, column)) {
    993                    if (orientation == PlotOrientation.HORIZONTAL) {
    994                        drawItemLabel(g2, orientation, dataset, row, column, y1,
    995                                x1, (value < 0.0));
    996                    }
    997                    else if (orientation == PlotOrientation.VERTICAL) {
    998                        drawItemLabel(g2, orientation, dataset, row, column, x1,
    999                                y1, (value < 0.0));
    1000                    }
    1001                }
    1002
    1003                // add an item entity, if this information is being collected
    1004                EntityCollection entities = state.getEntityCollection();
    1005                if (entities != null) {
    1006                    addItemEntity(entities, dataset, row, column, shape);
    1007                }
    1008            }
    1009
    1010        }
    1011
    1012        /**
    1013         * Tests this renderer for equality with an arbitrary object.
    1014         *
    1015         * @param obj  the object (<code>null</code> permitted).
    1016         *
    1017         * @return A boolean.
    1018         */
    1019        public boolean equals(Object obj) {
    1020
    1021            if (obj == this) {
    1022                return true;
    1023            }
    1024            if (!(obj instanceof LineAndShapeRenderer)) {
    1025                return false;
    1026            }
    1027
    1028            LineAndShapeRenderer that = (LineAndShapeRenderer) obj;
    1029            if (this.baseLinesVisible != that.baseLinesVisible) {
    1030                return false;
    1031            }
    1032            if (!ObjectUtilities.equal(this.seriesLinesVisible,
    1033                    that.seriesLinesVisible)) {
    1034                return false;
    1035            }
    1036            if (!ObjectUtilities.equal(this.linesVisible, that.linesVisible)) {
    1037                return false;
    1038            }
    1039            if (this.baseShapesVisible != that.baseShapesVisible) {
    1040                return false;
    1041            }
    1042            if (!ObjectUtilities.equal(this.seriesShapesVisible,
    1043                    that.seriesShapesVisible)) {
    1044                return false;
    1045            }
    1046            if (!ObjectUtilities.equal(this.shapesVisible, that.shapesVisible)) {
    1047                return false;
    1048            }
    1049            if (!ObjectUtilities.equal(this.shapesFilled, that.shapesFilled)) {
    1050                return false;
    1051            }
    1052            if (!ObjectUtilities.equal(this.seriesShapesFilled,
    1053                    that.seriesShapesFilled)) {
    1054                return false;
    1055            }
    1056            if (this.baseShapesFilled != that.baseShapesFilled) {
    1057                return false;
    1058            }
    1059            if (this.useOutlinePaint != that.useOutlinePaint) {
    1060                return false;
    1061            }
    1062            if (this.useSeriesOffset != that.useSeriesOffset) {
    1063                return false;
    1064            }
    1065            if (this.itemMargin != that.itemMargin) {
    1066                return false;
    1067            }
    1068            return super.equals(obj);
    1069        }
    1070
    1071        /**
    1072         * Returns an independent copy of the renderer.
    1073         *
    1074         * @return A clone.
    1075         *
    1076         * @throws CloneNotSupportedException  should not happen.
    1077         */
    1078        public Object clone() throws CloneNotSupportedException {
    1079            LineAndShapeRenderer clone = (LineAndShapeRenderer) super.clone();
    1080            clone.seriesLinesVisible
    1081                = (BooleanList) this.seriesLinesVisible.clone();
    1082            clone.seriesShapesVisible
    1083                = (BooleanList) this.seriesShapesVisible.clone();
    1084            clone.seriesShapesFilled
    1085                = (BooleanList) this.seriesShapesFilled.clone();
    1086            return clone;
    1087        }
    1088
    1089    }