    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     * CategoryStepRenderer.java
    029     * -------------------------
    030     *
    031     * (C) Copyright 2004, 2005, by Brian Cole and Contributors.
    032     *
    033     * Original Author:  Brian Cole;
    034     * Contributor(s):   David Gilbert (for Object Refinery Limited);
    035     *
    036     * $Id: CategoryStepRenderer.java,v 1.5.2.1 2005/10/25 20:54:16 mungady Exp $
    037     *
    038     * Changes
    039     * -------
    040     * 21-Apr-2004 : Version 1, contributed by Brian Cole (DG);
    041     * 22-Apr-2004 : Fixed Checkstyle complaints (DG);
    042     * 05-Nov-2004 : Modified drawItem() signature (DG);
    043     * 08-Mar-2005 : Added equals() method (DG);
    044     *
    045     */
    046
    047    package org.jfree.chart.renderer.category;
    048
    049    import java.awt.Graphics2D;
    050    import java.awt.geom.Line2D;
    051    import java.awt.geom.Rectangle2D;
    052    import java.io.Serializable;
    053
    054    import org.jfree.chart.axis.CategoryAxis;
    055    import org.jfree.chart.axis.ValueAxis;
    056    import org.jfree.chart.event.RendererChangeEvent;
    057    import org.jfree.chart.plot.CategoryPlot;
    058    import org.jfree.chart.plot.PlotOrientation;
    059    import org.jfree.chart.renderer.xy.XYStepRenderer;
    060    import org.jfree.data.category.CategoryDataset;
    061    import org.jfree.util.PublicCloneable;
    062
    063    /**
    064     * A "step" renderer similar to {@link XYStepRenderer} but
    065     * that can be used with the {@link CategoryPlot} class.
    066     *
    067     * @author Brian Cole
    068     */
    069    public class CategoryStepRenderer extends AbstractCategoryItemRenderer
    070                                      implements Cloneable, PublicCloneable,
    071                                                 Serializable {
    072
    073        /** For serialization. */
    074        private static final long serialVersionUID = -5121079703118261470L;
    075
    076        /** The stagger width. */
    077        public static final int STAGGER_WIDTH = 5; // could make this configurable
    078
    079        /**
    080         * A flag that controls whether or not the steps for multiple series are
    081         * staggered.
    082         */
    083        private boolean stagger = false;
    084
    085        /** A working line - need to remove this. */
    086        private transient Line2D line = new Line2D.Double(0.0, 0.0, 0.0, 0.0);
    087
    088        /**
    089         * Creates a new renderer (stagger defaults to <code>false</code>).
    090         */
    091        public CategoryStepRenderer() {
    092            this(false);
    093        }
    094
    095        /**
    096         * Creates a new renderer.
    097         *
    098         * @param stagger  should the horizontal part of the step be staggered by
    099         *                 series?
    100         */
    101        public CategoryStepRenderer(boolean stagger) {
    102            this.stagger = stagger;
    103        }
    104
    105        /**
    106         * Returns the flag that controls whether the series steps are staggered.
    107         *
    108         * @return A boolean.
    109         */
    110        public boolean getStagger() {
    111            return this.stagger;
    112        }
    113
    114        /**
    115         * Sets the flag that controls whether or not the series steps are
    116         * staggered and sends a {@link RendererChangeEvent} to all registered
    117         * listeners.
    118         *
    119         * @param shouldStagger  a boolean.
    120         */
    121        public void setStagger(boolean shouldStagger) {
    122            this.stagger = shouldStagger;
    123            notifyListeners(new RendererChangeEvent(this));
    124        }
    125
    126        /**
    127         * Draws the line.
    128         *
    129         * @param g2  the graphics device.
    130         * @param orientation  the plot orientation.
    131         * @param x0  the x-coordinate for the start of the line.
    132         * @param y0  the y-coordinate for the start of the line.
    133         * @param x1  the x-coordinate for the end of the line.
    134         * @param y1  the y-coordinate for the end of the line.
    135         */
    136        protected void drawLine(Graphics2D g2, PlotOrientation orientation,
    137                                double x0, double y0, double x1, double y1) {
    138
    139            if (orientation == PlotOrientation.VERTICAL) {
    140                this.line.setLine(x0, y0, x1, y1);
    141                g2.draw(this.line);
    142            }
    143            else if (orientation == PlotOrientation.HORIZONTAL) {
    144                this.line.setLine(y0, x0, y1, x1); // switch x and y
    145                g2.draw(this.line);
    146            }
    147            // else unknown orientation (complain?)
    148        }
    149
    150        /**
    151         * Draw a single data item.
    152         *
    153         * @param g2  the graphics device.
    154         * @param state  the renderer state.
    155         * @param dataArea  the area in which the data is drawn.
    156         * @param plot  the plot.
    157         * @param domainAxis  the domain axis.
    158         * @param rangeAxis  the range axis.
    159         * @param dataset  the dataset.
    160         * @param row  the row index (zero-based).
    161         * @param column  the column index (zero-based).
    162         * @param pass  the pass index.
    163         */
    164        public void drawItem(Graphics2D g2,
    165                             CategoryItemRendererState state,
    166                             Rectangle2D dataArea,
    167                             CategoryPlot plot,
    168                             CategoryAxis domainAxis,
    169                             ValueAxis rangeAxis,
    170                             CategoryDataset dataset,
    171                             int row,
    172                             int column,
    173                             int pass) {
    174
    175            Number value = dataset.getValue(row, column);
    176            if (value == null) {
    177                return;
    178            }
    179            PlotOrientation orientation = plot.getOrientation();
    180
    181            // current data point...
    182            double x1s = domainAxis.getCategoryStart(
    183                column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
    184            );
    185            double x1 = domainAxis.getCategoryMiddle(
    186                column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
    187            );
    188            double x1e = 2 * x1 - x1s; // or: x1s + 2*(x1-x1s)
    189            double y1 = rangeAxis.valueToJava2D(
    190                value.doubleValue(), dataArea, plot.getRangeAxisEdge()
    191            );
    192            g2.setPaint(getItemPaint(row, column));
    193            g2.setStroke(getItemStroke(row, column));
    194
    195            if (column != 0) {
    196                Number previousValue = dataset.getValue(row, column - 1);
    197                if (previousValue != null) {
    198                    // previous data point...
    199                    double previous = previousValue.doubleValue();
    200                    double x0s = domainAxis.getCategoryStart(
    201                        column - 1, getColumnCount(), dataArea,
    202                        plot.getDomainAxisEdge()
    203                    );
    204                    double x0 = domainAxis.getCategoryMiddle(
    205                        column - 1, getColumnCount(), dataArea,
    206                        plot.getDomainAxisEdge()
    207                    );
    208                    double x0e = 2 * x0 - x0s; // or: x0s + 2*(x0-x0s)
    209                    double y0 = rangeAxis.valueToJava2D(
    210                        previous, dataArea, plot.getRangeAxisEdge()
    211                    );
    212                    if (getStagger()) {
    213                        int xStagger = row * STAGGER_WIDTH;
    214                        if (xStagger > (x1s - x0e)) {
    215                            xStagger = (int) (x1s - x0e);
    216                        }
    217                        x1s = x0e + xStagger;
    218                    }
    219                    drawLine(g2, orientation, x0e, y0, x1s, y0);
    220                        // extend x0's flat bar
    221
    222                    drawLine(g2, orientation, x1s, y0, x1s, y1); // upright bar
    223               }
    224           }
    225           drawLine(g2, orientation, x1s, y1, x1e, y1); // x1's flat bar
    226
    227           // draw the item labels if there are any...
    228           if (isItemLabelVisible(row, column)) {
    229                drawItemLabel(
    230                    g2, orientation, dataset, row, column, x1, y1,
    231                    (value.doubleValue() < 0.0)
    232                );
    233           }
    234        /* This is how LineAndShapeRenderer.drawItem() handles tips and URLs, but
    235           I omit it due to time pressure. It shouldn't be hard to put back
    236     in.
    237
    238           // collect entity and tool tip information...
    239           if (state.getInfo() != null) {
    240               EntityCollection entities =
    241     state.getInfo().getOwner().getEntityCollection();
    242               if (entities != null && shape != null) {
    243                   String tip = null;
    244                   CategoryItemLabelGenerator generator =
    245     getItemLabelGenerator(row, column);
    246                   if (generator != null) {
    247                       tip = generator.generateToolTip(dataset, row, column);
    248                   }
    249                   String url = null;
    250                   if (getItemURLGenerator(row, column) != null)
    251                   url = getItemURLGenerator(row, column).generateURL(dataset, row,
    252                     column);
    253                   }
    254                   CategoryItemEntity entity = new CategoryItemEntity(
    255                       shape, tip, url, dataset, row,
    256     dataset.getColumnKey(column), column);
    257                   entities.addEntity(entity);
    258               }
    259           }
    260        */
    261
    262        }
    263
    264        /**
    265         * Tests this renderer for equality with an arbitrary object.
    266         *
    267         * @param obj  the object (<code>null</code> permitted).
    268         *
    269         * @return A boolean.
    270         */
    271        public boolean equals(Object obj) {
    272            if (obj == this) {
    273                return true;
    274            }
    275            if (!(obj instanceof CategoryStepRenderer)) {
    276                return false;
    277            }
    278            if (!super.equals(obj)) {
    279                return false;
    280            }
    281            CategoryStepRenderer that = (CategoryStepRenderer) obj;
    282            if (this.stagger != that.stagger) {
    283                return false;
    284            }
    285            return true;
    286        }
    287
    288    }