Source for org.jfree.chart.renderer.category.LineAndShapeRenderer

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