Frames | No Frames |
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: }