1 /* =========================================================== 2 * JFreeChart : a free chart library for the Java(tm) platform 3 * =========================================================== 4 * 5 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 25 * Other names may be trademarks of their respective owners.] 26 * 27 * ------------ 28 * PiePlot.java 29 * ------------ 30 * (C) Copyright 2000-2013, by Andrzej Porebski and Contributors. 31 * 32 * Original Author: Andrzej Porebski; 33 * Contributor(s): David Gilbert (for Object Refinery Limited); 34 * Martin Cordova (percentages in labels); 35 * Richard Atkinson (URL support for image maps); 36 * Christian W. Zuckschwerdt; 37 * Arnaud Lelievre; 38 * Martin Hilpert (patch 1891849); 39 * Andreas Schroeder (very minor); 40 * Christoph Beck (bug 2121818); 41 * 42 * Changes 43 * ------- 44 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG); 45 * 18-Sep-2001 : Updated header (DG); 46 * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG); 47 * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart.java to 48 * Plot.java (DG); 49 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG); 50 * 13-Nov-2001 : Modified plot subclasses so that null axes are possible for 51 * pie plot (DG); 52 * 17-Nov-2001 : Added PieDataset interface and amended this class accordingly, 53 * and completed removal of BlankAxis class as it is no longer 54 * required (DG); 55 * 19-Nov-2001 : Changed 'drawCircle' property to 'circular' property (DG); 56 * 21-Nov-2001 : Added options for exploding pie sections and filled out range 57 * of properties (DG); 58 * Added option for percentages in chart labels, based on code 59 * by Martin Cordova (DG); 60 * 30-Nov-2001 : Changed default font from "Arial" --> "SansSerif" (DG); 61 * 12-Dec-2001 : Removed unnecessary 'throws' clause in constructor (DG); 62 * 13-Dec-2001 : Added tooltips (DG); 63 * 16-Jan-2002 : Renamed tooltips class (DG); 64 * 22-Jan-2002 : Fixed bug correlating legend labels with pie data (DG); 65 * 05-Feb-2002 : Added alpha-transparency to plot class, and updated 66 * constructors accordingly (DG); 67 * 06-Feb-2002 : Added optional background image and alpha-transparency to Plot 68 * and subclasses. Clipped drawing within plot area (DG); 69 * 26-Mar-2002 : Added an empty zoom method (DG); 70 * 18-Apr-2002 : PieDataset is no longer sorted (oldman); 71 * 23-Apr-2002 : Moved dataset from JFreeChart to Plot. Added 72 * getLegendItemLabels() method (DG); 73 * 19-Jun-2002 : Added attributes to control starting angle and direction 74 * (default is now clockwise) (DG); 75 * 25-Jun-2002 : Removed redundant imports (DG); 76 * 02-Jul-2002 : Fixed sign of percentage bug introduced in 0.9.2 (DG); 77 * 16-Jul-2002 : Added check for null dataset in getLegendItemLabels() (DG); 78 * 30-Jul-2002 : Moved summation code to DatasetUtilities (DG); 79 * 05-Aug-2002 : Added URL support for image maps - new member variable for 80 * urlGenerator, modified constructor and minor change to the 81 * draw method (RA); 82 * 18-Sep-2002 : Modified the percent label creation and added setters for the 83 * formatters (AS); 84 * 24-Sep-2002 : Added getLegendItems() method (DG); 85 * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG); 86 * 09-Oct-2002 : Added check for null entity collection (DG); 87 * 30-Oct-2002 : Changed PieDataset interface (DG); 88 * 18-Nov-2002 : Changed CategoryDataset to TableDataset (DG); 89 * 02-Jan-2003 : Fixed "no data" message (DG); 90 * 23-Jan-2003 : Modified to extract data from rows OR columns in 91 * CategoryDataset (DG); 92 * 14-Feb-2003 : Fixed label drawing so that foreground alpha does not apply 93 * (bug id 685536) (DG); 94 * 07-Mar-2003 : Modified to pass pieIndex on to PieSectionEntity and tooltip 95 * and URL generators (DG); 96 * 21-Mar-2003 : Added a minimum angle for drawing arcs 97 * (see bug id 620031) (DG); 98 * 24-Apr-2003 : Switched around PieDataset and KeyedValuesDataset (DG); 99 * 02-Jun-2003 : Fixed bug 721733 (DG); 100 * 30-Jul-2003 : Modified entity constructor (CZ); 101 * 19-Aug-2003 : Implemented Cloneable (DG); 102 * 29-Aug-2003 : Fixed bug 796936 (null pointer on setOutlinePaint()) (DG); 103 * 08-Sep-2003 : Added internationalization via use of properties 104 * resourceBundle (RFE 690236) (AL); 105 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 106 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 107 * 05-Nov-2003 : Fixed missing legend bug (DG); 108 * 10-Nov-2003 : Re-added the DatasetChangeListener to constructors (CZ); 109 * 29-Jan-2004 : Fixed clipping bug in draw() method (DG); 110 * 11-Mar-2004 : Major overhaul to improve labelling (DG); 111 * 31-Mar-2004 : Made an adjustment for the plot area when the label generator 112 * is null. Fixed null pointer exception when the label 113 * generator returns null for a label (DG); 114 * 06-Apr-2004 : Added getter, setter, serialization and draw support for 115 * labelBackgroundPaint (AS); 116 * 08-Apr-2004 : Added flag to control whether null values are ignored or 117 * not (DG); 118 * 15-Apr-2004 : Fixed some minor warnings from Eclipse (DG); 119 * 26-Apr-2004 : Added attributes for label outline and shadow (DG); 120 * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG); 121 * 04-Nov-2004 : Fixed null pointer exception with new LegendTitle class (DG); 122 * 09-Nov-2004 : Added user definable legend item shape (DG); 123 * 25-Nov-2004 : Added new legend label generator (DG); 124 * 20-Apr-2005 : Added a tool tip generator for legend labels (DG); 125 * 26-Apr-2005 : Removed LOGGER (DG); 126 * 05-May-2005 : Updated draw() method parameters (DG); 127 * 10-May-2005 : Added flag to control visibility of label linking lines, plus 128 * another flag to control the handling of zero values (DG); 129 * 08-Jun-2005 : Fixed bug in getLegendItems() method (not respecting flags 130 * for ignoring null and zero values), and fixed equals() method 131 * to handle GradientPaint (DG); 132 * 15-Jul-2005 : Added sectionOutlinesVisible attribute (DG); 133 * ------------- JFREECHART 1.0.x --------------------------------------------- 134 * 09-Jan-2006 : Fixed bug 1400442, inconsistent treatment of null and zero 135 * values in dataset (DG); 136 * 28-Feb-2006 : Fixed bug 1440415, bad distribution of pie section 137 * labels (DG); 138 * 27-Sep-2006 : Initialised baseSectionPaint correctly, added lookup methods 139 * for section paint, outline paint and outline stroke (DG); 140 * 27-Sep-2006 : Refactored paint and stroke methods to use keys rather than 141 * section indices (DG); 142 * 03-Oct-2006 : Replaced call to JRE 1.5 method (DG); 143 * 23-Nov-2006 : Added support for URLs for the legend items (DG); 144 * 24-Nov-2006 : Cloning fixes (DG); 145 * 17-Apr-2007 : Check for null label in legend items (DG); 146 * 19-Apr-2007 : Deprecated override settings (DG); 147 * 18-May-2007 : Set dataset for LegendItem (DG); 148 * 14-Jun-2007 : Added label distributor attribute (DG); 149 * 18-Jul-2007 : Added simple label option (DG); 150 * 21-Nov-2007 : Fixed labelling bugs, added debug code, restored default 151 * white background (DG); 152 * 19-Mar-2008 : Fixed IllegalArgumentException when drawing with null 153 * dataset (DG); 154 * 31-Mar-2008 : Adjust the label area for the interiorGap (DG); 155 * 31-Mar-2008 : Added quad and cubic curve label link lines - see patch 156 * 1891849 by Martin Hilpert (DG); 157 * 02-Jul-2008 : Added autoPopulate flags (DG); 158 * 15-Aug-2008 : Added methods to clear section attributes (DG); 159 * 15-Aug-2008 : Fixed bug 2051168 - problem with LegendItemEntity 160 * generation (DG); 161 * 23-Sep-2008 : Added getLabelLinkDepth() method - see bug 2121818 reported 162 * by Christoph Beck (DG); 163 * 18-Dec-2008 : Use ResourceBundleWrapper - see patch 1607918 by 164 * Jess Thrysoee (DG); 165 * 10-Jul-2009 : Added optional drop shadow generator (DG); 166 * 03-Sep-2009 : Fixed bug where sinmpleLabelOffset is ignored (DG); 167 * 04-Nov-2009 : Add mouse wheel rotation support (DG); 168 * 18-Oct-2011 : Fixed tooltip offset with shadow generator (DG); 169 * 20-Nov-2011 : Initialise shadow generator as null (DG); 170 * 01-Jul-2012 : General label once only in drawSimpleLabels() (DG); 171 * 02-Jul-2013 : Use ParamChecks (DG); 172 * 12-Sep-2013 : Check for KEY_SUPPRESS_SHADOW_GENERATION rendering hint (DG); 173 * 174 */ 175 176 package org.jfree.chart.plot; 177 178 import java.awt.AlphaComposite; 179 import java.awt.BasicStroke; 180 import java.awt.Color; 181 import java.awt.Composite; 182 import java.awt.Font; 183 import java.awt.FontMetrics; 184 import java.awt.Graphics2D; 185 import java.awt.Paint; 186 import java.awt.RadialGradientPaint; 187 import java.awt.Shape; 188 import java.awt.Stroke; 189 import java.awt.geom.Arc2D; 190 import java.awt.geom.CubicCurve2D; 191 import java.awt.geom.Ellipse2D; 192 import java.awt.geom.Line2D; 193 import java.awt.geom.Point2D; 194 import java.awt.geom.QuadCurve2D; 195 import java.awt.geom.Rectangle2D; 196 import java.awt.image.BufferedImage; 197 import java.io.IOException; 198 import java.io.ObjectInputStream; 199 import java.io.ObjectOutputStream; 200 import java.io.Serializable; 201 import java.util.Iterator; 202 import java.util.List; 203 import java.util.Map; 204 import java.util.ResourceBundle; 205 import java.util.TreeMap; 206 import org.jfree.chart.JFreeChart; 207 208 import org.jfree.chart.LegendItem; 209 import org.jfree.chart.LegendItemCollection; 210 import org.jfree.chart.PaintMap; 211 import org.jfree.chart.StrokeMap; 212 import org.jfree.chart.entity.EntityCollection; 213 import org.jfree.chart.entity.PieSectionEntity; 214 import org.jfree.chart.event.PlotChangeEvent; 215 import org.jfree.chart.labels.PieSectionLabelGenerator; 216 import org.jfree.chart.labels.PieToolTipGenerator; 217 import org.jfree.chart.labels.StandardPieSectionLabelGenerator; 218 import org.jfree.chart.urls.PieURLGenerator; 219 import org.jfree.chart.util.ParamChecks; 220 import org.jfree.chart.util.ResourceBundleWrapper; 221 import org.jfree.chart.util.ShadowGenerator; 222 import org.jfree.data.DefaultKeyedValues; 223 import org.jfree.data.KeyedValues; 224 import org.jfree.data.general.DatasetChangeEvent; 225 import org.jfree.data.general.DatasetUtilities; 226 import org.jfree.data.general.PieDataset; 227 import org.jfree.io.SerialUtilities; 228 import org.jfree.text.G2TextMeasurer; 229 import org.jfree.text.TextBlock; 230 import org.jfree.text.TextBox; 231 import org.jfree.text.TextUtilities; 232 import org.jfree.ui.RectangleAnchor; 233 import org.jfree.ui.RectangleInsets; 234 import org.jfree.ui.TextAnchor; 235 import org.jfree.util.ObjectUtilities; 236 import org.jfree.util.PaintUtilities; 237 import org.jfree.util.PublicCloneable; 238 import org.jfree.util.Rotation; 239 import org.jfree.util.ShapeUtilities; 240 import org.jfree.util.UnitType; 241 242 /** 243 * A plot that displays data in the form of a pie chart, using data from any 244 * class that implements the {@link PieDataset} interface. 245 * The example shown here is generated by the <code>PieChartDemo2.java</code> 246 * program included in the JFreeChart Demo Collection: 247 * <br><br> 248 * <img src="../../../../images/PiePlotSample.png" 249 * alt="PiePlotSample.png" /> 250 * <P> 251 * Special notes: 252 * <ol> 253 * <li>the default starting point is 12 o'clock and the pie sections proceed 254 * in a clockwise direction, but these settings can be changed;</li> 255 * <li>negative values in the dataset are ignored;</li> 256 * <li>there are utility methods for creating a {@link PieDataset} from a 257 * {@link org.jfree.data.category.CategoryDataset};</li> 258 * </ol> 259 * 260 * @see Plot 261 * @see PieDataset 262 */ 263 public class PiePlot extends Plot implements Cloneable, Serializable { 264 265 /** For serialization. */ 266 private static final long serialVersionUID = -795612466005590431L; 267 268 /** The default interior gap. */ 269 public static final double DEFAULT_INTERIOR_GAP = 0.08; 270 271 /** The maximum interior gap (currently 40%). */ 272 public static final double MAX_INTERIOR_GAP = 0.40; 273 274 /** The default starting angle for the pie chart. */ 275 public static final double DEFAULT_START_ANGLE = 90.0; 276 277 /** The default section label font. */ 278 public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif", 279 Font.PLAIN, 10); 280 281 /** The default section label paint. */ 282 public static final Paint DEFAULT_LABEL_PAINT = Color.black; 283 284 /** The default section label background paint. */ 285 public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(255, 286 255, 192); 287 288 /** The default section label outline paint. */ 289 public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.black; 290 291 /** The default section label outline stroke. */ 292 public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke( 293 0.5f); 294 295 /** The default section label shadow paint. */ 296 public static final Paint DEFAULT_LABEL_SHADOW_PAINT = new Color(151, 151, 297 151, 128); 298 299 /** The default minimum arc angle to draw. */ 300 public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001; 301 302 /** The dataset for the pie chart. */ 303 private PieDataset dataset; 304 305 /** The pie index (used by the {@link MultiplePiePlot} class). */ 306 private int pieIndex; 307 308 /** 309 * The amount of space left around the outside of the pie plot, expressed 310 * as a percentage of the plot area width and height. 311 */ 312 private double interiorGap; 313 314 /** Flag determining whether to draw an ellipse or a perfect circle. */ 315 private boolean circular; 316 317 /** The starting angle. */ 318 private double startAngle; 319 320 /** The direction for the pie segments. */ 321 private Rotation direction; 322 323 /** The section paint map. */ 324 private PaintMap sectionPaintMap; 325 326 /** The base section paint (fallback). */ 327 private transient Paint baseSectionPaint; 328 329 /** 330 * A flag that controls whether or not the section paint is auto-populated 331 * from the drawing supplier. 332 * 333 * @since 1.0.11 334 */ 335 private boolean autoPopulateSectionPaint; 336 337 /** 338 * A flag that controls whether or not an outline is drawn for each 339 * section in the plot. 340 */ 341 private boolean sectionOutlinesVisible; 342 343 /** The section outline paint map. */ 344 private PaintMap sectionOutlinePaintMap; 345 346 /** The base section outline paint (fallback). */ 347 private transient Paint baseSectionOutlinePaint; 348 349 /** 350 * A flag that controls whether or not the section outline paint is 351 * auto-populated from the drawing supplier. 352 * 353 * @since 1.0.11 354 */ 355 private boolean autoPopulateSectionOutlinePaint; 356 357 /** The section outline stroke map. */ 358 private StrokeMap sectionOutlineStrokeMap; 359 360 /** The base section outline stroke (fallback). */ 361 private transient Stroke baseSectionOutlineStroke; 362 363 /** 364 * A flag that controls whether or not the section outline stroke is 365 * auto-populated from the drawing supplier. 366 * 367 * @since 1.0.11 368 */ 369 private boolean autoPopulateSectionOutlineStroke; 370 371 /** The shadow paint. */ 372 private transient Paint shadowPaint = Color.gray; 373 374 /** The x-offset for the shadow effect. */ 375 private double shadowXOffset = 4.0f; 376 377 /** The y-offset for the shadow effect. */ 378 private double shadowYOffset = 4.0f; 379 380 /** The percentage amount to explode each pie section. */ 381 private Map explodePercentages; 382 383 /** The section label generator. */ 384 private PieSectionLabelGenerator labelGenerator; 385 386 /** The font used to display the section labels. */ 387 private Font labelFont; 388 389 /** The color used to draw the section labels. */ 390 private transient Paint labelPaint; 391 392 /** 393 * The color used to draw the background of the section labels. If this 394 * is <code>null</code>, the background is not filled. 395 */ 396 private transient Paint labelBackgroundPaint; 397 398 /** 399 * The paint used to draw the outline of the section labels 400 * (<code>null</code> permitted). 401 */ 402 private transient Paint labelOutlinePaint; 403 404 /** 405 * The stroke used to draw the outline of the section labels 406 * (<code>null</code> permitted). 407 */ 408 private transient Stroke labelOutlineStroke; 409 410 /** 411 * The paint used to draw the shadow for the section labels 412 * (<code>null</code> permitted). 413 */ 414 private transient Paint labelShadowPaint; 415 416 /** 417 * A flag that controls whether simple or extended labels are used. 418 * 419 * @since 1.0.7 420 */ 421 private boolean simpleLabels = true; 422 423 /** 424 * The padding between the labels and the label outlines. This is not 425 * allowed to be <code>null</code>. 426 * 427 * @since 1.0.7 428 */ 429 private RectangleInsets labelPadding; 430 431 /** 432 * The simple label offset. 433 * 434 * @since 1.0.7 435 */ 436 private RectangleInsets simpleLabelOffset; 437 438 /** The maximum label width as a percentage of the plot width. */ 439 private double maximumLabelWidth = 0.14; 440 441 /** 442 * The gap between the labels and the link corner, as a percentage of the 443 * plot width. 444 */ 445 private double labelGap = 0.025; 446 447 /** A flag that controls whether or not the label links are drawn. */ 448 private boolean labelLinksVisible; 449 450 /** 451 * The label link style. 452 * 453 * @since 1.0.10 454 */ 455 private PieLabelLinkStyle labelLinkStyle = PieLabelLinkStyle.STANDARD; 456 457 /** The link margin. */ 458 private double labelLinkMargin = 0.025; 459 460 /** The paint used for the label linking lines. */ 461 private transient Paint labelLinkPaint = Color.black; 462 463 /** The stroke used for the label linking lines. */ 464 private transient Stroke labelLinkStroke = new BasicStroke(0.5f); 465 466 /** 467 * The pie section label distributor. 468 * 469 * @since 1.0.6 470 */ 471 private AbstractPieLabelDistributor labelDistributor; 472 473 /** The tooltip generator. */ 474 private PieToolTipGenerator toolTipGenerator; 475 476 /** The URL generator. */ 477 private PieURLGenerator urlGenerator; 478 479 /** The legend label generator. */ 480 private PieSectionLabelGenerator legendLabelGenerator; 481 482 /** A tool tip generator for the legend. */ 483 private PieSectionLabelGenerator legendLabelToolTipGenerator; 484 485 /** 486 * A URL generator for the legend items (optional). 487 * 488 * @since 1.0.4. 489 */ 490 private PieURLGenerator legendLabelURLGenerator; 491 492 /** 493 * A flag that controls whether <code>null</code> values are ignored. 494 */ 495 private boolean ignoreNullValues; 496 497 /** 498 * A flag that controls whether zero values are ignored. 499 */ 500 private boolean ignoreZeroValues; 501 502 /** The legend item shape. */ 503 private transient Shape legendItemShape; 504 505 /** 506 * The smallest arc angle that will get drawn (this is to avoid a bug in 507 * various Java implementations that causes the JVM to crash). See this 508 * link for details: 509 * 510 * http://www.jfree.org/phpBB2/viewtopic.php?t=2707 511 * 512 * ...and this bug report in the Java Bug Parade: 513 * 514 * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html 515 */ 516 private double minimumArcAngleToDraw; 517 518 /** 519 * The shadow generator for the plot (<code>null</code> permitted). 520 * 521 * @since 1.0.14 522 */ 523 private ShadowGenerator shadowGenerator; 524 525 /** The resourceBundle for the localization. */ 526 protected static ResourceBundle localizationResources 527 = ResourceBundleWrapper.getBundle( 528 "org.jfree.chart.plot.LocalizationBundle"); 529 530 /** 531 * This debug flag controls whether or not an outline is drawn showing the 532 * interior of the plot region. This is drawn as a lightGray rectangle 533 * showing the padding provided by the 'interiorGap' setting. 534 */ 535 static final boolean DEBUG_DRAW_INTERIOR = false; 536 537 /** 538 * This debug flag controls whether or not an outline is drawn showing the 539 * link area (in blue) and link ellipse (in yellow). This controls where 540 * the label links have 'elbow' points. 541 */ 542 static final boolean DEBUG_DRAW_LINK_AREA = false; 543 544 /** 545 * This debug flag controls whether or not an outline is drawn showing 546 * the pie area (in green). 547 */ 548 static final boolean DEBUG_DRAW_PIE_AREA = false; 549 550 /** 551 * Creates a new plot. The dataset is initially set to <code>null</code>. 552 */ PiePlot()553 public PiePlot() { 554 this(null); 555 } 556 557 /** 558 * Creates a plot that will draw a pie chart for the specified dataset. 559 * 560 * @param dataset the dataset (<code>null</code> permitted). 561 */ PiePlot(PieDataset dataset)562 public PiePlot(PieDataset dataset) { 563 super(); 564 this.dataset = dataset; 565 if (dataset != null) { 566 dataset.addChangeListener(this); 567 } 568 this.pieIndex = 0; 569 570 this.interiorGap = DEFAULT_INTERIOR_GAP; 571 this.circular = true; 572 this.startAngle = DEFAULT_START_ANGLE; 573 this.direction = Rotation.CLOCKWISE; 574 this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW; 575 576 this.sectionPaint = null; 577 this.sectionPaintMap = new PaintMap(); 578 this.baseSectionPaint = Color.gray; 579 this.autoPopulateSectionPaint = true; 580 581 this.sectionOutlinesVisible = true; 582 this.sectionOutlinePaint = null; 583 this.sectionOutlinePaintMap = new PaintMap(); 584 this.baseSectionOutlinePaint = DEFAULT_OUTLINE_PAINT; 585 this.autoPopulateSectionOutlinePaint = false; 586 587 this.sectionOutlineStroke = null; 588 this.sectionOutlineStrokeMap = new StrokeMap(); 589 this.baseSectionOutlineStroke = DEFAULT_OUTLINE_STROKE; 590 this.autoPopulateSectionOutlineStroke = false; 591 592 this.explodePercentages = new TreeMap(); 593 594 this.labelGenerator = new StandardPieSectionLabelGenerator(); 595 this.labelFont = DEFAULT_LABEL_FONT; 596 this.labelPaint = DEFAULT_LABEL_PAINT; 597 this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT; 598 this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT; 599 this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE; 600 this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT; 601 this.labelLinksVisible = true; 602 this.labelDistributor = new PieLabelDistributor(0); 603 604 this.simpleLabels = false; 605 this.simpleLabelOffset = new RectangleInsets(UnitType.RELATIVE, 0.18, 606 0.18, 0.18, 0.18); 607 this.labelPadding = new RectangleInsets(2, 2, 2, 2); 608 609 this.toolTipGenerator = null; 610 this.urlGenerator = null; 611 this.legendLabelGenerator = new StandardPieSectionLabelGenerator(); 612 this.legendLabelToolTipGenerator = null; 613 this.legendLabelURLGenerator = null; 614 this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE; 615 616 this.ignoreNullValues = false; 617 this.ignoreZeroValues = false; 618 619 this.shadowGenerator = null; 620 } 621 622 /** 623 * Returns the dataset. 624 * 625 * @return The dataset (possibly <code>null</code>). 626 * 627 * @see #setDataset(PieDataset) 628 */ getDataset()629 public PieDataset getDataset() { 630 return this.dataset; 631 } 632 633 /** 634 * Sets the dataset and sends a {@link DatasetChangeEvent} to 'this'. 635 * 636 * @param dataset the dataset (<code>null</code> permitted). 637 * 638 * @see #getDataset() 639 */ setDataset(PieDataset dataset)640 public void setDataset(PieDataset dataset) { 641 // if there is an existing dataset, remove the plot from the list of 642 // change listeners... 643 PieDataset existing = this.dataset; 644 if (existing != null) { 645 existing.removeChangeListener(this); 646 } 647 648 // set the new dataset, and register the chart as a change listener... 649 this.dataset = dataset; 650 if (dataset != null) { 651 setDatasetGroup(dataset.getGroup()); 652 dataset.addChangeListener(this); 653 } 654 655 // send a dataset change event to self... 656 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 657 datasetChanged(event); 658 } 659 660 /** 661 * Returns the pie index (this is used by the {@link MultiplePiePlot} class 662 * to track subplots). 663 * 664 * @return The pie index. 665 * 666 * @see #setPieIndex(int) 667 */ getPieIndex()668 public int getPieIndex() { 669 return this.pieIndex; 670 } 671 672 /** 673 * Sets the pie index (this is used by the {@link MultiplePiePlot} class to 674 * track subplots). 675 * 676 * @param index the index. 677 * 678 * @see #getPieIndex() 679 */ setPieIndex(int index)680 public void setPieIndex(int index) { 681 this.pieIndex = index; 682 } 683 684 /** 685 * Returns the start angle for the first pie section. This is measured in 686 * degrees starting from 3 o'clock and measuring anti-clockwise. 687 * 688 * @return The start angle. 689 * 690 * @see #setStartAngle(double) 691 */ getStartAngle()692 public double getStartAngle() { 693 return this.startAngle; 694 } 695 696 /** 697 * Sets the starting angle and sends a {@link PlotChangeEvent} to all 698 * registered listeners. The initial default value is 90 degrees, which 699 * corresponds to 12 o'clock. A value of zero corresponds to 3 o'clock... 700 * this is the encoding used by Java's Arc2D class. 701 * 702 * @param angle the angle (in degrees). 703 * 704 * @see #getStartAngle() 705 */ setStartAngle(double angle)706 public void setStartAngle(double angle) { 707 this.startAngle = angle; 708 fireChangeEvent(); 709 } 710 711 /** 712 * Returns the direction in which the pie sections are drawn (clockwise or 713 * anti-clockwise). 714 * 715 * @return The direction (never <code>null</code>). 716 * 717 * @see #setDirection(Rotation) 718 */ getDirection()719 public Rotation getDirection() { 720 return this.direction; 721 } 722 723 /** 724 * Sets the direction in which the pie sections are drawn and sends a 725 * {@link PlotChangeEvent} to all registered listeners. 726 * 727 * @param direction the direction (<code>null</code> not permitted). 728 * 729 * @see #getDirection() 730 */ setDirection(Rotation direction)731 public void setDirection(Rotation direction) { 732 ParamChecks.nullNotPermitted(direction, "direction"); 733 this.direction = direction; 734 fireChangeEvent(); 735 736 } 737 738 /** 739 * Returns the interior gap, measured as a percentage of the available 740 * drawing space. 741 * 742 * @return The gap (as a percentage of the available drawing space). 743 * 744 * @see #setInteriorGap(double) 745 */ getInteriorGap()746 public double getInteriorGap() { 747 return this.interiorGap; 748 } 749 750 /** 751 * Sets the interior gap and sends a {@link PlotChangeEvent} to all 752 * registered listeners. This controls the space between the edges of the 753 * pie plot and the plot area itself (the region where the section labels 754 * appear). 755 * 756 * @param percent the gap (as a percentage of the available drawing space). 757 * 758 * @see #getInteriorGap() 759 */ setInteriorGap(double percent)760 public void setInteriorGap(double percent) { 761 762 if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) { 763 throw new IllegalArgumentException( 764 "Invalid 'percent' (" + percent + ") argument."); 765 } 766 767 if (this.interiorGap != percent) { 768 this.interiorGap = percent; 769 fireChangeEvent(); 770 } 771 772 } 773 774 /** 775 * Returns a flag indicating whether the pie chart is circular, or 776 * stretched into an elliptical shape. 777 * 778 * @return A flag indicating whether the pie chart is circular. 779 * 780 * @see #setCircular(boolean) 781 */ isCircular()782 public boolean isCircular() { 783 return this.circular; 784 } 785 786 /** 787 * A flag indicating whether the pie chart is circular, or stretched into 788 * an elliptical shape. 789 * 790 * @param flag the new value. 791 * 792 * @see #isCircular() 793 */ setCircular(boolean flag)794 public void setCircular(boolean flag) { 795 setCircular(flag, true); 796 } 797 798 /** 799 * Sets the circular attribute and, if requested, sends a 800 * {@link PlotChangeEvent} to all registered listeners. 801 * 802 * @param circular the new value of the flag. 803 * @param notify notify listeners? 804 * 805 * @see #isCircular() 806 */ setCircular(boolean circular, boolean notify)807 public void setCircular(boolean circular, boolean notify) { 808 this.circular = circular; 809 if (notify) { 810 fireChangeEvent(); 811 } 812 } 813 814 /** 815 * Returns the flag that controls whether <code>null</code> values in the 816 * dataset are ignored. 817 * 818 * @return A boolean. 819 * 820 * @see #setIgnoreNullValues(boolean) 821 */ getIgnoreNullValues()822 public boolean getIgnoreNullValues() { 823 return this.ignoreNullValues; 824 } 825 826 /** 827 * Sets a flag that controls whether <code>null</code> values are ignored, 828 * and sends a {@link PlotChangeEvent} to all registered listeners. At 829 * present, this only affects whether or not the key is presented in the 830 * legend. 831 * 832 * @param flag the flag. 833 * 834 * @see #getIgnoreNullValues() 835 * @see #setIgnoreZeroValues(boolean) 836 */ setIgnoreNullValues(boolean flag)837 public void setIgnoreNullValues(boolean flag) { 838 this.ignoreNullValues = flag; 839 fireChangeEvent(); 840 } 841 842 /** 843 * Returns the flag that controls whether zero values in the 844 * dataset are ignored. 845 * 846 * @return A boolean. 847 * 848 * @see #setIgnoreZeroValues(boolean) 849 */ getIgnoreZeroValues()850 public boolean getIgnoreZeroValues() { 851 return this.ignoreZeroValues; 852 } 853 854 /** 855 * Sets a flag that controls whether zero values are ignored, 856 * and sends a {@link PlotChangeEvent} to all registered listeners. This 857 * only affects whether or not a label appears for the non-visible 858 * pie section. 859 * 860 * @param flag the flag. 861 * 862 * @see #getIgnoreZeroValues() 863 * @see #setIgnoreNullValues(boolean) 864 */ setIgnoreZeroValues(boolean flag)865 public void setIgnoreZeroValues(boolean flag) { 866 this.ignoreZeroValues = flag; 867 fireChangeEvent(); 868 } 869 870 //// SECTION PAINT //////////////////////////////////////////////////////// 871 872 /** 873 * Returns the paint for the specified section. This is equivalent to 874 * <code>lookupSectionPaint(section, getAutoPopulateSectionPaint())</code>. 875 * 876 * @param key the section key. 877 * 878 * @return The paint for the specified section. 879 * 880 * @since 1.0.3 881 * 882 * @see #lookupSectionPaint(Comparable, boolean) 883 */ lookupSectionPaint(Comparable key)884 protected Paint lookupSectionPaint(Comparable key) { 885 return lookupSectionPaint(key, getAutoPopulateSectionPaint()); 886 } 887 888 /** 889 * Returns the paint for the specified section. The lookup involves these 890 * steps: 891 * <ul> 892 * <li>if {@link #getSectionPaint()} is non-<code>null</code>, return 893 * it;</li> 894 * <li>if {@link #getSectionPaint(int)} is non-<code>null</code> return 895 * it;</li> 896 * <li>if {@link #getSectionPaint(int)} is <code>null</code> but 897 * <code>autoPopulate</code> is <code>true</code>, attempt to fetch 898 * a new paint from the drawing supplier 899 * ({@link #getDrawingSupplier()}); 900 * <li>if all else fails, return {@link #getBaseSectionPaint()}. 901 * </ul> 902 * 903 * @param key the section key. 904 * @param autoPopulate a flag that controls whether the drawing supplier 905 * is used to auto-populate the section paint settings. 906 * 907 * @return The paint. 908 * 909 * @since 1.0.3 910 */ lookupSectionPaint(Comparable key, boolean autoPopulate)911 protected Paint lookupSectionPaint(Comparable key, boolean autoPopulate) { 912 913 // is there an override? 914 Paint result = getSectionPaint(); 915 if (result != null) { 916 return result; 917 } 918 919 // if not, check if there is a paint defined for the specified key 920 result = this.sectionPaintMap.getPaint(key); 921 if (result != null) { 922 return result; 923 } 924 925 // nothing defined - do we autoPopulate? 926 if (autoPopulate) { 927 DrawingSupplier ds = getDrawingSupplier(); 928 if (ds != null) { 929 result = ds.getNextPaint(); 930 this.sectionPaintMap.put(key, result); 931 } 932 else { 933 result = this.baseSectionPaint; 934 } 935 } 936 else { 937 result = this.baseSectionPaint; 938 } 939 return result; 940 } 941 942 /** 943 * Returns the paint for ALL sections in the plot. 944 * 945 * @return The paint (possibly <code>null</code>). 946 * 947 * @see #setSectionPaint(Paint) 948 * 949 * @deprecated Use {@link #getSectionPaint(Comparable)} and 950 * {@link #getBaseSectionPaint()}. Deprecated as of version 1.0.6. 951 */ getSectionPaint()952 public Paint getSectionPaint() { 953 return this.sectionPaint; 954 } 955 956 /** 957 * Sets the paint for ALL sections in the plot. If this is set to 958 * </code>null</code>, then a list of paints is used instead (to allow 959 * different colors to be used for each section). 960 * 961 * @param paint the paint (<code>null</code> permitted). 962 * 963 * @see #getSectionPaint() 964 * 965 * @deprecated Use {@link #setSectionPaint(Comparable, Paint)} and 966 * {@link #setBaseSectionPaint(Paint)}. Deprecated as of version 1.0.6. 967 */ setSectionPaint(Paint paint)968 public void setSectionPaint(Paint paint) { 969 this.sectionPaint = paint; 970 fireChangeEvent(); 971 } 972 973 /** 974 * Returns a key for the specified section. If there is no such section 975 * in the dataset, we generate a key. This is to provide some backward 976 * compatibility for the (now deprecated) methods that get/set attributes 977 * based on section indices. The preferred way of doing this now is to 978 * link the attributes directly to the section key (there are new methods 979 * for this, starting from version 1.0.3). 980 * 981 * @param section the section index. 982 * 983 * @return The key. 984 * 985 * @since 1.0.3 986 */ getSectionKey(int section)987 protected Comparable getSectionKey(int section) { 988 Comparable key = null; 989 if (this.dataset != null) { 990 if (section >= 0 && section < this.dataset.getItemCount()) { 991 key = this.dataset.getKey(section); 992 } 993 } 994 if (key == null) { 995 key = new Integer(section); 996 } 997 return key; 998 } 999 1000 /** 1001 * Returns the paint associated with the specified key, or 1002 * <code>null</code> if there is no paint associated with the key. 1003 * 1004 * @param key the key (<code>null</code> not permitted). 1005 * 1006 * @return The paint associated with the specified key, or 1007 * <code>null</code>. 1008 * 1009 * @throws IllegalArgumentException if <code>key</code> is 1010 * <code>null</code>. 1011 * 1012 * @see #setSectionPaint(Comparable, Paint) 1013 * 1014 * @since 1.0.3 1015 */ getSectionPaint(Comparable key)1016 public Paint getSectionPaint(Comparable key) { 1017 // null argument check delegated... 1018 return this.sectionPaintMap.getPaint(key); 1019 } 1020 1021 /** 1022 * Sets the paint associated with the specified key, and sends a 1023 * {@link PlotChangeEvent} to all registered listeners. 1024 * 1025 * @param key the key (<code>null</code> not permitted). 1026 * @param paint the paint. 1027 * 1028 * @throws IllegalArgumentException if <code>key</code> is 1029 * <code>null</code>. 1030 * 1031 * @see #getSectionPaint(Comparable) 1032 * 1033 * @since 1.0.3 1034 */ setSectionPaint(Comparable key, Paint paint)1035 public void setSectionPaint(Comparable key, Paint paint) { 1036 // null argument check delegated... 1037 this.sectionPaintMap.put(key, paint); 1038 fireChangeEvent(); 1039 } 1040 1041 /** 1042 * Clears the section paint settings for this plot and, if requested, sends 1043 * a {@link PlotChangeEvent} to all registered listeners. Be aware that 1044 * if the <code>autoPopulateSectionPaint</code> flag is set, the section 1045 * paints may be repopulated using the same colours as before. 1046 * 1047 * @param notify notify listeners? 1048 * 1049 * @since 1.0.11 1050 * 1051 * @see #autoPopulateSectionPaint 1052 */ clearSectionPaints(boolean notify)1053 public void clearSectionPaints(boolean notify) { 1054 this.sectionPaintMap.clear(); 1055 if (notify) { 1056 fireChangeEvent(); 1057 } 1058 } 1059 1060 /** 1061 * Returns the base section paint. This is used when no other paint is 1062 * defined, which is rare. The default value is <code>Color.gray</code>. 1063 * 1064 * @return The paint (never <code>null</code>). 1065 * 1066 * @see #setBaseSectionPaint(Paint) 1067 */ getBaseSectionPaint()1068 public Paint getBaseSectionPaint() { 1069 return this.baseSectionPaint; 1070 } 1071 1072 /** 1073 * Sets the base section paint and sends a {@link PlotChangeEvent} to all 1074 * registered listeners. 1075 * 1076 * @param paint the paint (<code>null</code> not permitted). 1077 * 1078 * @see #getBaseSectionPaint() 1079 */ setBaseSectionPaint(Paint paint)1080 public void setBaseSectionPaint(Paint paint) { 1081 ParamChecks.nullNotPermitted(paint, "paint"); 1082 this.baseSectionPaint = paint; 1083 fireChangeEvent(); 1084 } 1085 1086 /** 1087 * Returns the flag that controls whether or not the section paint is 1088 * auto-populated by the {@link #lookupSectionPaint(Comparable)} method. 1089 * 1090 * @return A boolean. 1091 * 1092 * @since 1.0.11 1093 */ getAutoPopulateSectionPaint()1094 public boolean getAutoPopulateSectionPaint() { 1095 return this.autoPopulateSectionPaint; 1096 } 1097 1098 /** 1099 * Sets the flag that controls whether or not the section paint is 1100 * auto-populated by the {@link #lookupSectionPaint(Comparable)} method, 1101 * and sends a {@link PlotChangeEvent} to all registered listeners. 1102 * 1103 * @param auto auto-populate? 1104 * 1105 * @since 1.0.11 1106 */ setAutoPopulateSectionPaint(boolean auto)1107 public void setAutoPopulateSectionPaint(boolean auto) { 1108 this.autoPopulateSectionPaint = auto; 1109 fireChangeEvent(); 1110 } 1111 1112 //// SECTION OUTLINE PAINT //////////////////////////////////////////////// 1113 1114 /** 1115 * Returns the flag that controls whether or not the outline is drawn for 1116 * each pie section. 1117 * 1118 * @return The flag that controls whether or not the outline is drawn for 1119 * each pie section. 1120 * 1121 * @see #setSectionOutlinesVisible(boolean) 1122 */ getSectionOutlinesVisible()1123 public boolean getSectionOutlinesVisible() { 1124 return this.sectionOutlinesVisible; 1125 } 1126 1127 /** 1128 * Sets the flag that controls whether or not the outline is drawn for 1129 * each pie section, and sends a {@link PlotChangeEvent} to all registered 1130 * listeners. 1131 * 1132 * @param visible the flag. 1133 * 1134 * @see #getSectionOutlinesVisible() 1135 */ setSectionOutlinesVisible(boolean visible)1136 public void setSectionOutlinesVisible(boolean visible) { 1137 this.sectionOutlinesVisible = visible; 1138 fireChangeEvent(); 1139 } 1140 1141 /** 1142 * Returns the outline paint for the specified section. This is equivalent 1143 * to <code>lookupSectionPaint(section, 1144 * getAutoPopulateSectionOutlinePaint())</code>. 1145 * 1146 * @param key the section key. 1147 * 1148 * @return The paint for the specified section. 1149 * 1150 * @since 1.0.3 1151 * 1152 * @see #lookupSectionOutlinePaint(Comparable, boolean) 1153 */ lookupSectionOutlinePaint(Comparable key)1154 protected Paint lookupSectionOutlinePaint(Comparable key) { 1155 return lookupSectionOutlinePaint(key, 1156 getAutoPopulateSectionOutlinePaint()); 1157 } 1158 1159 /** 1160 * Returns the outline paint for the specified section. The lookup 1161 * involves these steps: 1162 * <ul> 1163 * <li>if {@link #getSectionOutlinePaint()} is non-<code>null</code>, 1164 * return it;</li> 1165 * <li>otherwise, if {@link #getSectionOutlinePaint(int)} is 1166 * non-<code>null</code> return it;</li> 1167 * <li>if {@link #getSectionOutlinePaint(int)} is <code>null</code> but 1168 * <code>autoPopulate</code> is <code>true</code>, attempt to fetch 1169 * a new outline paint from the drawing supplier 1170 * ({@link #getDrawingSupplier()}); 1171 * <li>if all else fails, return {@link #getBaseSectionOutlinePaint()}. 1172 * </ul> 1173 * 1174 * @param key the section key. 1175 * @param autoPopulate a flag that controls whether the drawing supplier 1176 * is used to auto-populate the section outline paint settings. 1177 * 1178 * @return The paint. 1179 * 1180 * @since 1.0.3 1181 */ lookupSectionOutlinePaint(Comparable key, boolean autoPopulate)1182 protected Paint lookupSectionOutlinePaint(Comparable key, 1183 boolean autoPopulate) { 1184 1185 // is there an override? 1186 Paint result = getSectionOutlinePaint(); 1187 if (result != null) { 1188 return result; 1189 } 1190 1191 // if not, check if there is a paint defined for the specified key 1192 result = this.sectionOutlinePaintMap.getPaint(key); 1193 if (result != null) { 1194 return result; 1195 } 1196 1197 // nothing defined - do we autoPopulate? 1198 if (autoPopulate) { 1199 DrawingSupplier ds = getDrawingSupplier(); 1200 if (ds != null) { 1201 result = ds.getNextOutlinePaint(); 1202 this.sectionOutlinePaintMap.put(key, result); 1203 } 1204 else { 1205 result = this.baseSectionOutlinePaint; 1206 } 1207 } 1208 else { 1209 result = this.baseSectionOutlinePaint; 1210 } 1211 return result; 1212 } 1213 1214 /** 1215 * Returns the outline paint associated with the specified key, or 1216 * <code>null</code> if there is no paint associated with the key. 1217 * 1218 * @param key the key (<code>null</code> not permitted). 1219 * 1220 * @return The paint associated with the specified key, or 1221 * <code>null</code>. 1222 * 1223 * @throws IllegalArgumentException if <code>key</code> is 1224 * <code>null</code>. 1225 * 1226 * @see #setSectionOutlinePaint(Comparable, Paint) 1227 * 1228 * @since 1.0.3 1229 */ getSectionOutlinePaint(Comparable key)1230 public Paint getSectionOutlinePaint(Comparable key) { 1231 // null argument check delegated... 1232 return this.sectionOutlinePaintMap.getPaint(key); 1233 } 1234 1235 /** 1236 * Sets the outline paint associated with the specified key, and sends a 1237 * {@link PlotChangeEvent} to all registered listeners. 1238 * 1239 * @param key the key (<code>null</code> not permitted). 1240 * @param paint the paint. 1241 * 1242 * @throws IllegalArgumentException if <code>key</code> is 1243 * <code>null</code>. 1244 * 1245 * @see #getSectionOutlinePaint(Comparable) 1246 * 1247 * @since 1.0.3 1248 */ setSectionOutlinePaint(Comparable key, Paint paint)1249 public void setSectionOutlinePaint(Comparable key, Paint paint) { 1250 // null argument check delegated... 1251 this.sectionOutlinePaintMap.put(key, paint); 1252 fireChangeEvent(); 1253 } 1254 1255 /** 1256 * Clears the section outline paint settings for this plot and, if 1257 * requested, sends a {@link PlotChangeEvent} to all registered listeners. 1258 * Be aware that if the <code>autoPopulateSectionPaint</code> flag is set, 1259 * the section paints may be repopulated using the same colours as before. 1260 * 1261 * @param notify notify listeners? 1262 * 1263 * @since 1.0.11 1264 * 1265 * @see #autoPopulateSectionOutlinePaint 1266 */ clearSectionOutlinePaints(boolean notify)1267 public void clearSectionOutlinePaints(boolean notify) { 1268 this.sectionOutlinePaintMap.clear(); 1269 if (notify) { 1270 fireChangeEvent(); 1271 } 1272 } 1273 1274 /** 1275 * Returns the base section paint. This is used when no other paint is 1276 * available. 1277 * 1278 * @return The paint (never <code>null</code>). 1279 * 1280 * @see #setBaseSectionOutlinePaint(Paint) 1281 */ getBaseSectionOutlinePaint()1282 public Paint getBaseSectionOutlinePaint() { 1283 return this.baseSectionOutlinePaint; 1284 } 1285 1286 /** 1287 * Sets the base section paint. 1288 * 1289 * @param paint the paint (<code>null</code> not permitted). 1290 * 1291 * @see #getBaseSectionOutlinePaint() 1292 */ setBaseSectionOutlinePaint(Paint paint)1293 public void setBaseSectionOutlinePaint(Paint paint) { 1294 ParamChecks.nullNotPermitted(paint, "paint"); 1295 this.baseSectionOutlinePaint = paint; 1296 fireChangeEvent(); 1297 } 1298 1299 /** 1300 * Returns the flag that controls whether or not the section outline paint 1301 * is auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)} 1302 * method. 1303 * 1304 * @return A boolean. 1305 * 1306 * @since 1.0.11 1307 */ getAutoPopulateSectionOutlinePaint()1308 public boolean getAutoPopulateSectionOutlinePaint() { 1309 return this.autoPopulateSectionOutlinePaint; 1310 } 1311 1312 /** 1313 * Sets the flag that controls whether or not the section outline paint is 1314 * auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)} 1315 * method, and sends a {@link PlotChangeEvent} to all registered listeners. 1316 * 1317 * @param auto auto-populate? 1318 * 1319 * @since 1.0.11 1320 */ setAutoPopulateSectionOutlinePaint(boolean auto)1321 public void setAutoPopulateSectionOutlinePaint(boolean auto) { 1322 this.autoPopulateSectionOutlinePaint = auto; 1323 fireChangeEvent(); 1324 } 1325 1326 //// SECTION OUTLINE STROKE /////////////////////////////////////////////// 1327 1328 /** 1329 * Returns the outline stroke for the specified section. This is 1330 * equivalent to <code>lookupSectionOutlineStroke(section, 1331 * getAutoPopulateSectionOutlineStroke())</code>. 1332 * 1333 * @param key the section key. 1334 * 1335 * @return The stroke for the specified section. 1336 * 1337 * @since 1.0.3 1338 * 1339 * @see #lookupSectionOutlineStroke(Comparable, boolean) 1340 */ lookupSectionOutlineStroke(Comparable key)1341 protected Stroke lookupSectionOutlineStroke(Comparable key) { 1342 return lookupSectionOutlineStroke(key, 1343 getAutoPopulateSectionOutlineStroke()); 1344 } 1345 1346 /** 1347 * Returns the outline stroke for the specified section. The lookup 1348 * involves these steps: 1349 * <ul> 1350 * <li>if {@link #getSectionOutlineStroke()} is non-<code>null</code>, 1351 * return it;</li> 1352 * <li>otherwise, if {@link #getSectionOutlineStroke(int)} is 1353 * non-<code>null</code> return it;</li> 1354 * <li>if {@link #getSectionOutlineStroke(int)} is <code>null</code> but 1355 * <code>autoPopulate</code> is <code>true</code>, attempt to fetch 1356 * a new outline stroke from the drawing supplier 1357 * ({@link #getDrawingSupplier()}); 1358 * <li>if all else fails, return {@link #getBaseSectionOutlineStroke()}. 1359 * </ul> 1360 * 1361 * @param key the section key. 1362 * @param autoPopulate a flag that controls whether the drawing supplier 1363 * is used to auto-populate the section outline stroke settings. 1364 * 1365 * @return The stroke. 1366 * 1367 * @since 1.0.3 1368 */ lookupSectionOutlineStroke(Comparable key, boolean autoPopulate)1369 protected Stroke lookupSectionOutlineStroke(Comparable key, 1370 boolean autoPopulate) { 1371 1372 // is there an override? 1373 Stroke result = getSectionOutlineStroke(); 1374 if (result != null) { 1375 return result; 1376 } 1377 1378 // if not, check if there is a stroke defined for the specified key 1379 result = this.sectionOutlineStrokeMap.getStroke(key); 1380 if (result != null) { 1381 return result; 1382 } 1383 1384 // nothing defined - do we autoPopulate? 1385 if (autoPopulate) { 1386 DrawingSupplier ds = getDrawingSupplier(); 1387 if (ds != null) { 1388 result = ds.getNextOutlineStroke(); 1389 this.sectionOutlineStrokeMap.put(key, result); 1390 } 1391 else { 1392 result = this.baseSectionOutlineStroke; 1393 } 1394 } 1395 else { 1396 result = this.baseSectionOutlineStroke; 1397 } 1398 return result; 1399 } 1400 1401 /** 1402 * Returns the outline stroke associated with the specified key, or 1403 * <code>null</code> if there is no stroke associated with the key. 1404 * 1405 * @param key the key (<code>null</code> not permitted). 1406 * 1407 * @return The stroke associated with the specified key, or 1408 * <code>null</code>. 1409 * 1410 * @throws IllegalArgumentException if <code>key</code> is 1411 * <code>null</code>. 1412 * 1413 * @see #setSectionOutlineStroke(Comparable, Stroke) 1414 * 1415 * @since 1.0.3 1416 */ getSectionOutlineStroke(Comparable key)1417 public Stroke getSectionOutlineStroke(Comparable key) { 1418 // null argument check delegated... 1419 return this.sectionOutlineStrokeMap.getStroke(key); 1420 } 1421 1422 /** 1423 * Sets the outline stroke associated with the specified key, and sends a 1424 * {@link PlotChangeEvent} to all registered listeners. 1425 * 1426 * @param key the key (<code>null</code> not permitted). 1427 * @param stroke the stroke. 1428 * 1429 * @throws IllegalArgumentException if <code>key</code> is 1430 * <code>null</code>. 1431 * 1432 * @see #getSectionOutlineStroke(Comparable) 1433 * 1434 * @since 1.0.3 1435 */ setSectionOutlineStroke(Comparable key, Stroke stroke)1436 public void setSectionOutlineStroke(Comparable key, Stroke stroke) { 1437 // null argument check delegated... 1438 this.sectionOutlineStrokeMap.put(key, stroke); 1439 fireChangeEvent(); 1440 } 1441 1442 /** 1443 * Clears the section outline stroke settings for this plot and, if 1444 * requested, sends a {@link PlotChangeEvent} to all registered listeners. 1445 * Be aware that if the <code>autoPopulateSectionPaint</code> flag is set, 1446 * the section paints may be repopulated using the same colours as before. 1447 * 1448 * @param notify notify listeners? 1449 * 1450 * @since 1.0.11 1451 * 1452 * @see #autoPopulateSectionOutlineStroke 1453 */ clearSectionOutlineStrokes(boolean notify)1454 public void clearSectionOutlineStrokes(boolean notify) { 1455 this.sectionOutlineStrokeMap.clear(); 1456 if (notify) { 1457 fireChangeEvent(); 1458 } 1459 } 1460 1461 /** 1462 * Returns the base section stroke. This is used when no other stroke is 1463 * available. 1464 * 1465 * @return The stroke (never <code>null</code>). 1466 * 1467 * @see #setBaseSectionOutlineStroke(Stroke) 1468 */ getBaseSectionOutlineStroke()1469 public Stroke getBaseSectionOutlineStroke() { 1470 return this.baseSectionOutlineStroke; 1471 } 1472 1473 /** 1474 * Sets the base section stroke. 1475 * 1476 * @param stroke the stroke (<code>null</code> not permitted). 1477 * 1478 * @see #getBaseSectionOutlineStroke() 1479 */ setBaseSectionOutlineStroke(Stroke stroke)1480 public void setBaseSectionOutlineStroke(Stroke stroke) { 1481 ParamChecks.nullNotPermitted(stroke, "stroke"); 1482 this.baseSectionOutlineStroke = stroke; 1483 fireChangeEvent(); 1484 } 1485 1486 /** 1487 * Returns the flag that controls whether or not the section outline stroke 1488 * is auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)} 1489 * method. 1490 * 1491 * @return A boolean. 1492 * 1493 * @since 1.0.11 1494 */ getAutoPopulateSectionOutlineStroke()1495 public boolean getAutoPopulateSectionOutlineStroke() { 1496 return this.autoPopulateSectionOutlineStroke; 1497 } 1498 1499 /** 1500 * Sets the flag that controls whether or not the section outline stroke is 1501 * auto-populated by the {@link #lookupSectionOutlineStroke(Comparable)} 1502 * method, and sends a {@link PlotChangeEvent} to all registered listeners. 1503 * 1504 * @param auto auto-populate? 1505 * 1506 * @since 1.0.11 1507 */ setAutoPopulateSectionOutlineStroke(boolean auto)1508 public void setAutoPopulateSectionOutlineStroke(boolean auto) { 1509 this.autoPopulateSectionOutlineStroke = auto; 1510 fireChangeEvent(); 1511 } 1512 1513 /** 1514 * Returns the shadow paint. 1515 * 1516 * @return The paint (possibly <code>null</code>). 1517 * 1518 * @see #setShadowPaint(Paint) 1519 */ getShadowPaint()1520 public Paint getShadowPaint() { 1521 return this.shadowPaint; 1522 } 1523 1524 /** 1525 * Sets the shadow paint and sends a {@link PlotChangeEvent} to all 1526 * registered listeners. 1527 * 1528 * @param paint the paint (<code>null</code> permitted). 1529 * 1530 * @see #getShadowPaint() 1531 */ setShadowPaint(Paint paint)1532 public void setShadowPaint(Paint paint) { 1533 this.shadowPaint = paint; 1534 fireChangeEvent(); 1535 } 1536 1537 /** 1538 * Returns the x-offset for the shadow effect. 1539 * 1540 * @return The offset (in Java2D units). 1541 * 1542 * @see #setShadowXOffset(double) 1543 */ getShadowXOffset()1544 public double getShadowXOffset() { 1545 return this.shadowXOffset; 1546 } 1547 1548 /** 1549 * Sets the x-offset for the shadow effect and sends a 1550 * {@link PlotChangeEvent} to all registered listeners. 1551 * 1552 * @param offset the offset (in Java2D units). 1553 * 1554 * @see #getShadowXOffset() 1555 */ setShadowXOffset(double offset)1556 public void setShadowXOffset(double offset) { 1557 this.shadowXOffset = offset; 1558 fireChangeEvent(); 1559 } 1560 1561 /** 1562 * Returns the y-offset for the shadow effect. 1563 * 1564 * @return The offset (in Java2D units). 1565 * 1566 * @see #setShadowYOffset(double) 1567 */ getShadowYOffset()1568 public double getShadowYOffset() { 1569 return this.shadowYOffset; 1570 } 1571 1572 /** 1573 * Sets the y-offset for the shadow effect and sends a 1574 * {@link PlotChangeEvent} to all registered listeners. 1575 * 1576 * @param offset the offset (in Java2D units). 1577 * 1578 * @see #getShadowYOffset() 1579 */ setShadowYOffset(double offset)1580 public void setShadowYOffset(double offset) { 1581 this.shadowYOffset = offset; 1582 fireChangeEvent(); 1583 } 1584 1585 /** 1586 * Returns the amount that the section with the specified key should be 1587 * exploded. 1588 * 1589 * @param key the key (<code>null</code> not permitted). 1590 * 1591 * @return The amount that the section with the specified key should be 1592 * exploded. 1593 * 1594 * @throws IllegalArgumentException if <code>key</code> is 1595 * <code>null</code>. 1596 * 1597 * @since 1.0.3 1598 * 1599 * @see #setExplodePercent(Comparable, double) 1600 */ getExplodePercent(Comparable key)1601 public double getExplodePercent(Comparable key) { 1602 double result = 0.0; 1603 if (this.explodePercentages != null) { 1604 Number percent = (Number) this.explodePercentages.get(key); 1605 if (percent != null) { 1606 result = percent.doubleValue(); 1607 } 1608 } 1609 return result; 1610 } 1611 1612 /** 1613 * Sets the amount that a pie section should be exploded and sends a 1614 * {@link PlotChangeEvent} to all registered listeners. 1615 * 1616 * @param key the section key (<code>null</code> not permitted). 1617 * @param percent the explode percentage (0.30 = 30 percent). 1618 * 1619 * @since 1.0.3 1620 * 1621 * @see #getExplodePercent(Comparable) 1622 */ setExplodePercent(Comparable key, double percent)1623 public void setExplodePercent(Comparable key, double percent) { 1624 ParamChecks.nullNotPermitted(key, "key"); 1625 if (this.explodePercentages == null) { 1626 this.explodePercentages = new TreeMap(); 1627 } 1628 this.explodePercentages.put(key, new Double(percent)); 1629 fireChangeEvent(); 1630 } 1631 1632 /** 1633 * Returns the maximum explode percent. 1634 * 1635 * @return The percent. 1636 */ getMaximumExplodePercent()1637 public double getMaximumExplodePercent() { 1638 if (this.dataset == null) { 1639 return 0.0; 1640 } 1641 double result = 0.0; 1642 Iterator iterator = this.dataset.getKeys().iterator(); 1643 while (iterator.hasNext()) { 1644 Comparable key = (Comparable) iterator.next(); 1645 Number explode = (Number) this.explodePercentages.get(key); 1646 if (explode != null) { 1647 result = Math.max(result, explode.doubleValue()); 1648 } 1649 } 1650 return result; 1651 } 1652 1653 /** 1654 * Returns the section label generator. 1655 * 1656 * @return The generator (possibly <code>null</code>). 1657 * 1658 * @see #setLabelGenerator(PieSectionLabelGenerator) 1659 */ getLabelGenerator()1660 public PieSectionLabelGenerator getLabelGenerator() { 1661 return this.labelGenerator; 1662 } 1663 1664 /** 1665 * Sets the section label generator and sends a {@link PlotChangeEvent} to 1666 * all registered listeners. 1667 * 1668 * @param generator the generator (<code>null</code> permitted). 1669 * 1670 * @see #getLabelGenerator() 1671 */ setLabelGenerator(PieSectionLabelGenerator generator)1672 public void setLabelGenerator(PieSectionLabelGenerator generator) { 1673 this.labelGenerator = generator; 1674 fireChangeEvent(); 1675 } 1676 1677 /** 1678 * Returns the gap between the edge of the pie and the labels, expressed as 1679 * a percentage of the plot width. 1680 * 1681 * @return The gap (a percentage, where 0.05 = five percent). 1682 * 1683 * @see #setLabelGap(double) 1684 */ getLabelGap()1685 public double getLabelGap() { 1686 return this.labelGap; 1687 } 1688 1689 /** 1690 * Sets the gap between the edge of the pie and the labels (expressed as a 1691 * percentage of the plot width) and sends a {@link PlotChangeEvent} to all 1692 * registered listeners. 1693 * 1694 * @param gap the gap (a percentage, where 0.05 = five percent). 1695 * 1696 * @see #getLabelGap() 1697 */ setLabelGap(double gap)1698 public void setLabelGap(double gap) { 1699 this.labelGap = gap; 1700 fireChangeEvent(); 1701 } 1702 1703 /** 1704 * Returns the maximum label width as a percentage of the plot width. 1705 * 1706 * @return The width (a percentage, where 0.20 = 20 percent). 1707 * 1708 * @see #setMaximumLabelWidth(double) 1709 */ getMaximumLabelWidth()1710 public double getMaximumLabelWidth() { 1711 return this.maximumLabelWidth; 1712 } 1713 1714 /** 1715 * Sets the maximum label width as a percentage of the plot width and sends 1716 * a {@link PlotChangeEvent} to all registered listeners. 1717 * 1718 * @param width the width (a percentage, where 0.20 = 20 percent). 1719 * 1720 * @see #getMaximumLabelWidth() 1721 */ setMaximumLabelWidth(double width)1722 public void setMaximumLabelWidth(double width) { 1723 this.maximumLabelWidth = width; 1724 fireChangeEvent(); 1725 } 1726 1727 /** 1728 * Returns the flag that controls whether or not label linking lines are 1729 * visible. 1730 * 1731 * @return A boolean. 1732 * 1733 * @see #setLabelLinksVisible(boolean) 1734 */ getLabelLinksVisible()1735 public boolean getLabelLinksVisible() { 1736 return this.labelLinksVisible; 1737 } 1738 1739 /** 1740 * Sets the flag that controls whether or not label linking lines are 1741 * visible and sends a {@link PlotChangeEvent} to all registered listeners. 1742 * Please take care when hiding the linking lines - depending on the data 1743 * values, the labels can be displayed some distance away from the 1744 * corresponding pie section. 1745 * 1746 * @param visible the flag. 1747 * 1748 * @see #getLabelLinksVisible() 1749 */ setLabelLinksVisible(boolean visible)1750 public void setLabelLinksVisible(boolean visible) { 1751 this.labelLinksVisible = visible; 1752 fireChangeEvent(); 1753 } 1754 1755 /** 1756 * Returns the label link style. 1757 * 1758 * @return The label link style (never <code>null</code>). 1759 * 1760 * @see #setLabelLinkStyle(PieLabelLinkStyle) 1761 * 1762 * @since 1.0.10 1763 */ getLabelLinkStyle()1764 public PieLabelLinkStyle getLabelLinkStyle() { 1765 return this.labelLinkStyle; 1766 } 1767 1768 /** 1769 * Sets the label link style and sends a {@link PlotChangeEvent} to all 1770 * registered listeners. 1771 * 1772 * @param style the new style (<code>null</code> not permitted). 1773 * 1774 * @see #getLabelLinkStyle() 1775 * 1776 * @since 1.0.10 1777 */ setLabelLinkStyle(PieLabelLinkStyle style)1778 public void setLabelLinkStyle(PieLabelLinkStyle style) { 1779 ParamChecks.nullNotPermitted(style, "style"); 1780 this.labelLinkStyle = style; 1781 fireChangeEvent(); 1782 } 1783 1784 /** 1785 * Returns the margin (expressed as a percentage of the width or height) 1786 * between the edge of the pie and the link point. 1787 * 1788 * @return The link margin (as a percentage, where 0.05 is five percent). 1789 * 1790 * @see #setLabelLinkMargin(double) 1791 */ getLabelLinkMargin()1792 public double getLabelLinkMargin() { 1793 return this.labelLinkMargin; 1794 } 1795 1796 /** 1797 * Sets the link margin and sends a {@link PlotChangeEvent} to all 1798 * registered listeners. 1799 * 1800 * @param margin the margin. 1801 * 1802 * @see #getLabelLinkMargin() 1803 */ setLabelLinkMargin(double margin)1804 public void setLabelLinkMargin(double margin) { 1805 this.labelLinkMargin = margin; 1806 fireChangeEvent(); 1807 } 1808 1809 /** 1810 * Returns the paint used for the lines that connect pie sections to their 1811 * corresponding labels. 1812 * 1813 * @return The paint (never <code>null</code>). 1814 * 1815 * @see #setLabelLinkPaint(Paint) 1816 */ getLabelLinkPaint()1817 public Paint getLabelLinkPaint() { 1818 return this.labelLinkPaint; 1819 } 1820 1821 /** 1822 * Sets the paint used for the lines that connect pie sections to their 1823 * corresponding labels, and sends a {@link PlotChangeEvent} to all 1824 * registered listeners. 1825 * 1826 * @param paint the paint (<code>null</code> not permitted). 1827 * 1828 * @see #getLabelLinkPaint() 1829 */ setLabelLinkPaint(Paint paint)1830 public void setLabelLinkPaint(Paint paint) { 1831 ParamChecks.nullNotPermitted(paint, "paint"); 1832 this.labelLinkPaint = paint; 1833 fireChangeEvent(); 1834 } 1835 1836 /** 1837 * Returns the stroke used for the label linking lines. 1838 * 1839 * @return The stroke. 1840 * 1841 * @see #setLabelLinkStroke(Stroke) 1842 */ getLabelLinkStroke()1843 public Stroke getLabelLinkStroke() { 1844 return this.labelLinkStroke; 1845 } 1846 1847 /** 1848 * Sets the link stroke and sends a {@link PlotChangeEvent} to all 1849 * registered listeners. 1850 * 1851 * @param stroke the stroke. 1852 * 1853 * @see #getLabelLinkStroke() 1854 */ setLabelLinkStroke(Stroke stroke)1855 public void setLabelLinkStroke(Stroke stroke) { 1856 ParamChecks.nullNotPermitted(stroke, "stroke"); 1857 this.labelLinkStroke = stroke; 1858 fireChangeEvent(); 1859 } 1860 1861 /** 1862 * Returns the distance that the end of the label link is embedded into 1863 * the plot, expressed as a percentage of the plot's radius. 1864 * <br><br> 1865 * This method is overridden in the {@link RingPlot} class to resolve 1866 * bug 2121818. 1867 * 1868 * @return <code>0.10</code>. 1869 * 1870 * @since 1.0.12 1871 */ getLabelLinkDepth()1872 protected double getLabelLinkDepth() { 1873 return 0.1; 1874 } 1875 1876 /** 1877 * Returns the section label font. 1878 * 1879 * @return The font (never <code>null</code>). 1880 * 1881 * @see #setLabelFont(Font) 1882 */ getLabelFont()1883 public Font getLabelFont() { 1884 return this.labelFont; 1885 } 1886 1887 /** 1888 * Sets the section label font and sends a {@link PlotChangeEvent} to all 1889 * registered listeners. 1890 * 1891 * @param font the font (<code>null</code> not permitted). 1892 * 1893 * @see #getLabelFont() 1894 */ setLabelFont(Font font)1895 public void setLabelFont(Font font) { 1896 ParamChecks.nullNotPermitted(font, "font"); 1897 this.labelFont = font; 1898 fireChangeEvent(); 1899 } 1900 1901 /** 1902 * Returns the section label paint. 1903 * 1904 * @return The paint (never <code>null</code>). 1905 * 1906 * @see #setLabelPaint(Paint) 1907 */ getLabelPaint()1908 public Paint getLabelPaint() { 1909 return this.labelPaint; 1910 } 1911 1912 /** 1913 * Sets the section label paint and sends a {@link PlotChangeEvent} to all 1914 * registered listeners. 1915 * 1916 * @param paint the paint (<code>null</code> not permitted). 1917 * 1918 * @see #getLabelPaint() 1919 */ setLabelPaint(Paint paint)1920 public void setLabelPaint(Paint paint) { 1921 ParamChecks.nullNotPermitted(paint, "paint"); 1922 this.labelPaint = paint; 1923 fireChangeEvent(); 1924 } 1925 1926 /** 1927 * Returns the section label background paint. 1928 * 1929 * @return The paint (possibly <code>null</code>). 1930 * 1931 * @see #setLabelBackgroundPaint(Paint) 1932 */ getLabelBackgroundPaint()1933 public Paint getLabelBackgroundPaint() { 1934 return this.labelBackgroundPaint; 1935 } 1936 1937 /** 1938 * Sets the section label background paint and sends a 1939 * {@link PlotChangeEvent} to all registered listeners. 1940 * 1941 * @param paint the paint (<code>null</code> permitted). 1942 * 1943 * @see #getLabelBackgroundPaint() 1944 */ setLabelBackgroundPaint(Paint paint)1945 public void setLabelBackgroundPaint(Paint paint) { 1946 this.labelBackgroundPaint = paint; 1947 fireChangeEvent(); 1948 } 1949 1950 /** 1951 * Returns the section label outline paint. 1952 * 1953 * @return The paint (possibly <code>null</code>). 1954 * 1955 * @see #setLabelOutlinePaint(Paint) 1956 */ getLabelOutlinePaint()1957 public Paint getLabelOutlinePaint() { 1958 return this.labelOutlinePaint; 1959 } 1960 1961 /** 1962 * Sets the section label outline paint and sends a 1963 * {@link PlotChangeEvent} to all registered listeners. 1964 * 1965 * @param paint the paint (<code>null</code> permitted). 1966 * 1967 * @see #getLabelOutlinePaint() 1968 */ setLabelOutlinePaint(Paint paint)1969 public void setLabelOutlinePaint(Paint paint) { 1970 this.labelOutlinePaint = paint; 1971 fireChangeEvent(); 1972 } 1973 1974 /** 1975 * Returns the section label outline stroke. 1976 * 1977 * @return The stroke (possibly <code>null</code>). 1978 * 1979 * @see #setLabelOutlineStroke(Stroke) 1980 */ getLabelOutlineStroke()1981 public Stroke getLabelOutlineStroke() { 1982 return this.labelOutlineStroke; 1983 } 1984 1985 /** 1986 * Sets the section label outline stroke and sends a 1987 * {@link PlotChangeEvent} to all registered listeners. 1988 * 1989 * @param stroke the stroke (<code>null</code> permitted). 1990 * 1991 * @see #getLabelOutlineStroke() 1992 */ setLabelOutlineStroke(Stroke stroke)1993 public void setLabelOutlineStroke(Stroke stroke) { 1994 this.labelOutlineStroke = stroke; 1995 fireChangeEvent(); 1996 } 1997 1998 /** 1999 * Returns the section label shadow paint. 2000 * 2001 * @return The paint (possibly <code>null</code>). 2002 * 2003 * @see #setLabelShadowPaint(Paint) 2004 */ getLabelShadowPaint()2005 public Paint getLabelShadowPaint() { 2006 return this.labelShadowPaint; 2007 } 2008 2009 /** 2010 * Sets the section label shadow paint and sends a {@link PlotChangeEvent} 2011 * to all registered listeners. 2012 * 2013 * @param paint the paint (<code>null</code> permitted). 2014 * 2015 * @see #getLabelShadowPaint() 2016 */ setLabelShadowPaint(Paint paint)2017 public void setLabelShadowPaint(Paint paint) { 2018 this.labelShadowPaint = paint; 2019 fireChangeEvent(); 2020 } 2021 2022 /** 2023 * Returns the label padding. 2024 * 2025 * @return The label padding (never <code>null</code>). 2026 * 2027 * @since 1.0.7 2028 * 2029 * @see #setLabelPadding(RectangleInsets) 2030 */ getLabelPadding()2031 public RectangleInsets getLabelPadding() { 2032 return this.labelPadding; 2033 } 2034 2035 /** 2036 * Sets the padding between each label and its outline and sends a 2037 * {@link PlotChangeEvent} to all registered listeners. 2038 * 2039 * @param padding the padding (<code>null</code> not permitted). 2040 * 2041 * @since 1.0.7 2042 * 2043 * @see #getLabelPadding() 2044 */ setLabelPadding(RectangleInsets padding)2045 public void setLabelPadding(RectangleInsets padding) { 2046 ParamChecks.nullNotPermitted(padding, "padding"); 2047 this.labelPadding = padding; 2048 fireChangeEvent(); 2049 } 2050 2051 /** 2052 * Returns the flag that controls whether simple or extended labels are 2053 * displayed on the plot. 2054 * 2055 * @return A boolean. 2056 * 2057 * @since 1.0.7 2058 */ getSimpleLabels()2059 public boolean getSimpleLabels() { 2060 return this.simpleLabels; 2061 } 2062 2063 /** 2064 * Sets the flag that controls whether simple or extended labels are 2065 * displayed on the plot, and sends a {@link PlotChangeEvent} to all 2066 * registered listeners. 2067 * 2068 * @param simple the new flag value. 2069 * 2070 * @since 1.0.7 2071 */ setSimpleLabels(boolean simple)2072 public void setSimpleLabels(boolean simple) { 2073 this.simpleLabels = simple; 2074 fireChangeEvent(); 2075 } 2076 2077 /** 2078 * Returns the offset used for the simple labels, if they are displayed. 2079 * 2080 * @return The offset (never <code>null</code>). 2081 * 2082 * @since 1.0.7 2083 * 2084 * @see #setSimpleLabelOffset(RectangleInsets) 2085 */ getSimpleLabelOffset()2086 public RectangleInsets getSimpleLabelOffset() { 2087 return this.simpleLabelOffset; 2088 } 2089 2090 /** 2091 * Sets the offset for the simple labels and sends a 2092 * {@link PlotChangeEvent} to all registered listeners. 2093 * 2094 * @param offset the offset (<code>null</code> not permitted). 2095 * 2096 * @since 1.0.7 2097 * 2098 * @see #getSimpleLabelOffset() 2099 */ setSimpleLabelOffset(RectangleInsets offset)2100 public void setSimpleLabelOffset(RectangleInsets offset) { 2101 ParamChecks.nullNotPermitted(offset, "offset"); 2102 this.simpleLabelOffset = offset; 2103 fireChangeEvent(); 2104 } 2105 2106 /** 2107 * Returns the object responsible for the vertical layout of the pie 2108 * section labels. 2109 * 2110 * @return The label distributor (never <code>null</code>). 2111 * 2112 * @since 1.0.6 2113 */ getLabelDistributor()2114 public AbstractPieLabelDistributor getLabelDistributor() { 2115 return this.labelDistributor; 2116 } 2117 2118 /** 2119 * Sets the label distributor and sends a {@link PlotChangeEvent} to all 2120 * registered listeners. 2121 * 2122 * @param distributor the distributor (<code>null</code> not permitted). 2123 * 2124 * @since 1.0.6 2125 */ setLabelDistributor(AbstractPieLabelDistributor distributor)2126 public void setLabelDistributor(AbstractPieLabelDistributor distributor) { 2127 ParamChecks.nullNotPermitted(distributor, "distributor"); 2128 this.labelDistributor = distributor; 2129 fireChangeEvent(); 2130 } 2131 2132 /** 2133 * Returns the tool tip generator, an object that is responsible for 2134 * generating the text items used for tool tips by the plot. If the 2135 * generator is <code>null</code>, no tool tips will be created. 2136 * 2137 * @return The generator (possibly <code>null</code>). 2138 * 2139 * @see #setToolTipGenerator(PieToolTipGenerator) 2140 */ getToolTipGenerator()2141 public PieToolTipGenerator getToolTipGenerator() { 2142 return this.toolTipGenerator; 2143 } 2144 2145 /** 2146 * Sets the tool tip generator and sends a {@link PlotChangeEvent} to all 2147 * registered listeners. Set the generator to <code>null</code> if you 2148 * don't want any tool tips. 2149 * 2150 * @param generator the generator (<code>null</code> permitted). 2151 * 2152 * @see #getToolTipGenerator() 2153 */ setToolTipGenerator(PieToolTipGenerator generator)2154 public void setToolTipGenerator(PieToolTipGenerator generator) { 2155 this.toolTipGenerator = generator; 2156 fireChangeEvent(); 2157 } 2158 2159 /** 2160 * Returns the URL generator. 2161 * 2162 * @return The generator (possibly <code>null</code>). 2163 * 2164 * @see #setURLGenerator(PieURLGenerator) 2165 */ getURLGenerator()2166 public PieURLGenerator getURLGenerator() { 2167 return this.urlGenerator; 2168 } 2169 2170 /** 2171 * Sets the URL generator and sends a {@link PlotChangeEvent} to all 2172 * registered listeners. 2173 * 2174 * @param generator the generator (<code>null</code> permitted). 2175 * 2176 * @see #getURLGenerator() 2177 */ setURLGenerator(PieURLGenerator generator)2178 public void setURLGenerator(PieURLGenerator generator) { 2179 this.urlGenerator = generator; 2180 fireChangeEvent(); 2181 } 2182 2183 /** 2184 * Returns the minimum arc angle that will be drawn. Pie sections for an 2185 * angle smaller than this are not drawn, to avoid a JDK bug. 2186 * 2187 * @return The minimum angle. 2188 * 2189 * @see #setMinimumArcAngleToDraw(double) 2190 */ getMinimumArcAngleToDraw()2191 public double getMinimumArcAngleToDraw() { 2192 return this.minimumArcAngleToDraw; 2193 } 2194 2195 /** 2196 * Sets the minimum arc angle that will be drawn. Pie sections for an 2197 * angle smaller than this are not drawn, to avoid a JDK bug. See this 2198 * link for details: 2199 * <br><br> 2200 * <a href="http://www.jfree.org/phpBB2/viewtopic.php?t=2707"> 2201 * http://www.jfree.org/phpBB2/viewtopic.php?t=2707</a> 2202 * <br><br> 2203 * ...and this bug report in the Java Bug Parade: 2204 * <br><br> 2205 * <a href= 2206 * "http://developer.java.sun.com/developer/bugParade/bugs/4836495.html"> 2207 * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html</a> 2208 * 2209 * @param angle the minimum angle. 2210 * 2211 * @see #getMinimumArcAngleToDraw() 2212 */ setMinimumArcAngleToDraw(double angle)2213 public void setMinimumArcAngleToDraw(double angle) { 2214 this.minimumArcAngleToDraw = angle; 2215 } 2216 2217 /** 2218 * Returns the shape used for legend items. 2219 * 2220 * @return The shape (never <code>null</code>). 2221 * 2222 * @see #setLegendItemShape(Shape) 2223 */ getLegendItemShape()2224 public Shape getLegendItemShape() { 2225 return this.legendItemShape; 2226 } 2227 2228 /** 2229 * Sets the shape used for legend items and sends a {@link PlotChangeEvent} 2230 * to all registered listeners. 2231 * 2232 * @param shape the shape (<code>null</code> not permitted). 2233 * 2234 * @see #getLegendItemShape() 2235 */ setLegendItemShape(Shape shape)2236 public void setLegendItemShape(Shape shape) { 2237 ParamChecks.nullNotPermitted(shape, "shape"); 2238 this.legendItemShape = shape; 2239 fireChangeEvent(); 2240 } 2241 2242 /** 2243 * Returns the legend label generator. 2244 * 2245 * @return The legend label generator (never <code>null</code>). 2246 * 2247 * @see #setLegendLabelGenerator(PieSectionLabelGenerator) 2248 */ getLegendLabelGenerator()2249 public PieSectionLabelGenerator getLegendLabelGenerator() { 2250 return this.legendLabelGenerator; 2251 } 2252 2253 /** 2254 * Sets the legend label generator and sends a {@link PlotChangeEvent} to 2255 * all registered listeners. 2256 * 2257 * @param generator the generator (<code>null</code> not permitted). 2258 * 2259 * @see #getLegendLabelGenerator() 2260 */ setLegendLabelGenerator(PieSectionLabelGenerator generator)2261 public void setLegendLabelGenerator(PieSectionLabelGenerator generator) { 2262 ParamChecks.nullNotPermitted(generator, "generator"); 2263 this.legendLabelGenerator = generator; 2264 fireChangeEvent(); 2265 } 2266 2267 /** 2268 * Returns the legend label tool tip generator. 2269 * 2270 * @return The legend label tool tip generator (possibly <code>null</code>). 2271 * 2272 * @see #setLegendLabelToolTipGenerator(PieSectionLabelGenerator) 2273 */ getLegendLabelToolTipGenerator()2274 public PieSectionLabelGenerator getLegendLabelToolTipGenerator() { 2275 return this.legendLabelToolTipGenerator; 2276 } 2277 2278 /** 2279 * Sets the legend label tool tip generator and sends a 2280 * {@link PlotChangeEvent} to all registered listeners. 2281 * 2282 * @param generator the generator (<code>null</code> permitted). 2283 * 2284 * @see #getLegendLabelToolTipGenerator() 2285 */ setLegendLabelToolTipGenerator( PieSectionLabelGenerator generator)2286 public void setLegendLabelToolTipGenerator( 2287 PieSectionLabelGenerator generator) { 2288 this.legendLabelToolTipGenerator = generator; 2289 fireChangeEvent(); 2290 } 2291 2292 /** 2293 * Returns the legend label URL generator. 2294 * 2295 * @return The legend label URL generator (possibly <code>null</code>). 2296 * 2297 * @see #setLegendLabelURLGenerator(PieURLGenerator) 2298 * 2299 * @since 1.0.4 2300 */ getLegendLabelURLGenerator()2301 public PieURLGenerator getLegendLabelURLGenerator() { 2302 return this.legendLabelURLGenerator; 2303 } 2304 2305 /** 2306 * Sets the legend label URL generator and sends a 2307 * {@link PlotChangeEvent} to all registered listeners. 2308 * 2309 * @param generator the generator (<code>null</code> permitted). 2310 * 2311 * @see #getLegendLabelURLGenerator() 2312 * 2313 * @since 1.0.4 2314 */ setLegendLabelURLGenerator(PieURLGenerator generator)2315 public void setLegendLabelURLGenerator(PieURLGenerator generator) { 2316 this.legendLabelURLGenerator = generator; 2317 fireChangeEvent(); 2318 } 2319 2320 /** 2321 * Returns the shadow generator for the plot, if any. 2322 * 2323 * @return The shadow generator (possibly <code>null</code>). 2324 * 2325 * @since 1.0.14 2326 */ getShadowGenerator()2327 public ShadowGenerator getShadowGenerator() { 2328 return this.shadowGenerator; 2329 } 2330 2331 /** 2332 * Sets the shadow generator for the plot and sends a 2333 * {@link PlotChangeEvent} to all registered listeners. Note that this is 2334 * a bitmap drop-shadow generation facility and is separate from the 2335 * vector based show option that is controlled via the 2336 * {@link #setShadowPaint(java.awt.Paint)} method. 2337 * 2338 * @param generator the generator (<code>null</code> permitted). 2339 * 2340 * @since 1.0.14 2341 */ setShadowGenerator(ShadowGenerator generator)2342 public void setShadowGenerator(ShadowGenerator generator) { 2343 this.shadowGenerator = generator; 2344 fireChangeEvent(); 2345 } 2346 2347 /** 2348 * Handles a mouse wheel rotation (this method is intended for use by the 2349 * {@link org.jfree.chart.MouseWheelHandler} class). 2350 * 2351 * @param rotateClicks the number of rotate clicks on the the mouse wheel. 2352 * 2353 * @since 1.0.14 2354 */ handleMouseWheelRotation(int rotateClicks)2355 public void handleMouseWheelRotation(int rotateClicks) { 2356 setStartAngle(this.startAngle + rotateClicks * 4.0); 2357 } 2358 2359 /** 2360 * Initialises the drawing procedure. This method will be called before 2361 * the first item is rendered, giving the plot an opportunity to initialise 2362 * any state information it wants to maintain. 2363 * 2364 * @param g2 the graphics device. 2365 * @param plotArea the plot area (<code>null</code> not permitted). 2366 * @param plot the plot. 2367 * @param index the secondary index (<code>null</code> for primary 2368 * renderer). 2369 * @param info collects chart rendering information for return to caller. 2370 * 2371 * @return A state object (maintains state information relevant to one 2372 * chart drawing). 2373 */ initialise(Graphics2D g2, Rectangle2D plotArea, PiePlot plot, Integer index, PlotRenderingInfo info)2374 public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea, 2375 PiePlot plot, Integer index, PlotRenderingInfo info) { 2376 2377 PiePlotState state = new PiePlotState(info); 2378 state.setPassesRequired(2); 2379 if (this.dataset != null) { 2380 state.setTotal(DatasetUtilities.calculatePieDatasetTotal( 2381 plot.getDataset())); 2382 } 2383 state.setLatestAngle(plot.getStartAngle()); 2384 return state; 2385 2386 } 2387 2388 /** 2389 * Draws the plot on a Java 2D graphics device (such as the screen or a 2390 * printer). 2391 * 2392 * @param g2 the graphics device. 2393 * @param area the area within which the plot should be drawn. 2394 * @param anchor the anchor point (<code>null</code> permitted). 2395 * @param parentState the state from the parent plot, if there is one. 2396 * @param info collects info about the drawing 2397 * (<code>null</code> permitted). 2398 */ 2399 @Override draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info)2400 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 2401 PlotState parentState, PlotRenderingInfo info) { 2402 2403 // adjust for insets... 2404 RectangleInsets insets = getInsets(); 2405 insets.trim(area); 2406 2407 if (info != null) { 2408 info.setPlotArea(area); 2409 info.setDataArea(area); 2410 } 2411 2412 drawBackground(g2, area); 2413 drawOutline(g2, area); 2414 2415 Shape savedClip = g2.getClip(); 2416 g2.clip(area); 2417 2418 Composite originalComposite = g2.getComposite(); 2419 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 2420 getForegroundAlpha())); 2421 2422 if (!DatasetUtilities.isEmptyOrNull(this.dataset)) { 2423 Graphics2D savedG2 = g2; 2424 boolean suppressShadow = Boolean.TRUE.equals(g2.getRenderingHint( 2425 JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION)); 2426 BufferedImage dataImage = null; 2427 if (this.shadowGenerator != null && !suppressShadow) { 2428 dataImage = new BufferedImage((int) area.getWidth(), 2429 (int) area.getHeight(), BufferedImage.TYPE_INT_ARGB); 2430 g2 = dataImage.createGraphics(); 2431 g2.translate(-area.getX(), -area.getY()); 2432 g2.setRenderingHints(savedG2.getRenderingHints()); 2433 } 2434 drawPie(g2, area, info); 2435 if (this.shadowGenerator != null && !suppressShadow) { 2436 BufferedImage shadowImage 2437 = this.shadowGenerator.createDropShadow(dataImage); 2438 g2 = savedG2; 2439 g2.drawImage(shadowImage, (int) area.getX() 2440 + this.shadowGenerator.calculateOffsetX(), 2441 (int) area.getY() 2442 + this.shadowGenerator.calculateOffsetY(), null); 2443 g2.drawImage(dataImage, (int) area.getX(), (int) area.getY(), 2444 null); 2445 } 2446 } 2447 else { 2448 drawNoDataMessage(g2, area); 2449 } 2450 2451 g2.setClip(savedClip); 2452 g2.setComposite(originalComposite); 2453 2454 drawOutline(g2, area); 2455 2456 } 2457 2458 /** 2459 * Draws the pie. 2460 * 2461 * @param g2 the graphics device. 2462 * @param plotArea the plot area. 2463 * @param info chart rendering info. 2464 */ drawPie(Graphics2D g2, Rectangle2D plotArea, PlotRenderingInfo info)2465 protected void drawPie(Graphics2D g2, Rectangle2D plotArea, 2466 PlotRenderingInfo info) { 2467 2468 PiePlotState state = initialise(g2, plotArea, this, null, info); 2469 2470 // adjust the plot area for interior spacing and labels... 2471 double labelReserve = 0.0; 2472 if (this.labelGenerator != null && !this.simpleLabels) { 2473 labelReserve = this.labelGap + this.maximumLabelWidth; 2474 } 2475 double gapHorizontal = plotArea.getWidth() * labelReserve * 2.0; 2476 double gapVertical = plotArea.getHeight() * this.interiorGap * 2.0; 2477 2478 2479 if (DEBUG_DRAW_INTERIOR) { 2480 double hGap = plotArea.getWidth() * this.interiorGap; 2481 double vGap = plotArea.getHeight() * this.interiorGap; 2482 2483 double igx1 = plotArea.getX() + hGap; 2484 double igx2 = plotArea.getMaxX() - hGap; 2485 double igy1 = plotArea.getY() + vGap; 2486 double igy2 = plotArea.getMaxY() - vGap; 2487 g2.setPaint(Color.gray); 2488 g2.draw(new Rectangle2D.Double(igx1, igy1, igx2 - igx1, 2489 igy2 - igy1)); 2490 } 2491 2492 double linkX = plotArea.getX() + gapHorizontal / 2; 2493 double linkY = plotArea.getY() + gapVertical / 2; 2494 double linkW = plotArea.getWidth() - gapHorizontal; 2495 double linkH = plotArea.getHeight() - gapVertical; 2496 2497 // make the link area a square if the pie chart is to be circular... 2498 if (this.circular) { 2499 double min = Math.min(linkW, linkH) / 2; 2500 linkX = (linkX + linkX + linkW) / 2 - min; 2501 linkY = (linkY + linkY + linkH) / 2 - min; 2502 linkW = 2 * min; 2503 linkH = 2 * min; 2504 } 2505 2506 // the link area defines the dog leg points for the linking lines to 2507 // the labels 2508 Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY, linkW, 2509 linkH); 2510 state.setLinkArea(linkArea); 2511 2512 if (DEBUG_DRAW_LINK_AREA) { 2513 g2.setPaint(Color.blue); 2514 g2.draw(linkArea); 2515 g2.setPaint(Color.yellow); 2516 g2.draw(new Ellipse2D.Double(linkArea.getX(), linkArea.getY(), 2517 linkArea.getWidth(), linkArea.getHeight())); 2518 } 2519 2520 // the explode area defines the max circle/ellipse for the exploded 2521 // pie sections. it is defined by shrinking the linkArea by the 2522 // linkMargin factor. 2523 double lm = 0.0; 2524 if (!this.simpleLabels) { 2525 lm = this.labelLinkMargin; 2526 } 2527 double hh = linkArea.getWidth() * lm * 2.0; 2528 double vv = linkArea.getHeight() * lm * 2.0; 2529 Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh / 2.0, 2530 linkY + vv / 2.0, linkW - hh, linkH - vv); 2531 2532 state.setExplodedPieArea(explodeArea); 2533 2534 // the pie area defines the circle/ellipse for regular pie sections. 2535 // it is defined by shrinking the explodeArea by the explodeMargin 2536 // factor. 2537 double maximumExplodePercent = getMaximumExplodePercent(); 2538 double percent = maximumExplodePercent / (1.0 + maximumExplodePercent); 2539 2540 double h1 = explodeArea.getWidth() * percent; 2541 double v1 = explodeArea.getHeight() * percent; 2542 Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX() 2543 + h1 / 2.0, explodeArea.getY() + v1 / 2.0, 2544 explodeArea.getWidth() - h1, explodeArea.getHeight() - v1); 2545 2546 if (DEBUG_DRAW_PIE_AREA) { 2547 g2.setPaint(Color.green); 2548 g2.draw(pieArea); 2549 } 2550 state.setPieArea(pieArea); 2551 state.setPieCenterX(pieArea.getCenterX()); 2552 state.setPieCenterY(pieArea.getCenterY()); 2553 state.setPieWRadius(pieArea.getWidth() / 2.0); 2554 state.setPieHRadius(pieArea.getHeight() / 2.0); 2555 2556 // plot the data (unless the dataset is null)... 2557 if ((this.dataset != null) && (this.dataset.getKeys().size() > 0)) { 2558 2559 List keys = this.dataset.getKeys(); 2560 double totalValue = DatasetUtilities.calculatePieDatasetTotal( 2561 this.dataset); 2562 2563 int passesRequired = state.getPassesRequired(); 2564 for (int pass = 0; pass < passesRequired; pass++) { 2565 double runningTotal = 0.0; 2566 for (int section = 0; section < keys.size(); section++) { 2567 Number n = this.dataset.getValue(section); 2568 if (n != null) { 2569 double value = n.doubleValue(); 2570 if (value > 0.0) { 2571 runningTotal += value; 2572 drawItem(g2, section, explodeArea, state, pass); 2573 } 2574 } 2575 } 2576 } 2577 if (this.simpleLabels) { 2578 drawSimpleLabels(g2, keys, totalValue, plotArea, linkArea, 2579 state); 2580 } 2581 else { 2582 drawLabels(g2, keys, totalValue, plotArea, linkArea, state); 2583 } 2584 2585 } 2586 else { 2587 drawNoDataMessage(g2, plotArea); 2588 } 2589 } 2590 2591 /** 2592 * Draws a single data item. 2593 * 2594 * @param g2 the graphics device (<code>null</code> not permitted). 2595 * @param section the section index. 2596 * @param dataArea the data plot area. 2597 * @param state state information for one chart. 2598 * @param currentPass the current pass index. 2599 */ drawItem(Graphics2D g2, int section, Rectangle2D dataArea, PiePlotState state, int currentPass)2600 protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, 2601 PiePlotState state, int currentPass) { 2602 2603 Number n = this.dataset.getValue(section); 2604 if (n == null) { 2605 return; 2606 } 2607 double value = n.doubleValue(); 2608 double angle1 = 0.0; 2609 double angle2 = 0.0; 2610 2611 if (this.direction == Rotation.CLOCKWISE) { 2612 angle1 = state.getLatestAngle(); 2613 angle2 = angle1 - value / state.getTotal() * 360.0; 2614 } 2615 else if (this.direction == Rotation.ANTICLOCKWISE) { 2616 angle1 = state.getLatestAngle(); 2617 angle2 = angle1 + value / state.getTotal() * 360.0; 2618 } 2619 else { 2620 throw new IllegalStateException("Rotation type not recognised."); 2621 } 2622 2623 double angle = (angle2 - angle1); 2624 if (Math.abs(angle) > getMinimumArcAngleToDraw()) { 2625 double ep = 0.0; 2626 double mep = getMaximumExplodePercent(); 2627 if (mep > 0.0) { 2628 ep = getExplodePercent(section) / mep; 2629 } 2630 Rectangle2D arcBounds = getArcBounds(state.getPieArea(), 2631 state.getExplodedPieArea(), angle1, angle, ep); 2632 Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1, angle, 2633 Arc2D.PIE); 2634 2635 if (currentPass == 0) { 2636 if (this.shadowPaint != null && this.shadowGenerator == null) { 2637 Shape shadowArc = ShapeUtilities.createTranslatedShape( 2638 arc, (float) this.shadowXOffset, 2639 (float) this.shadowYOffset); 2640 g2.setPaint(this.shadowPaint); 2641 g2.fill(shadowArc); 2642 } 2643 } 2644 else if (currentPass == 1) { 2645 Comparable key = getSectionKey(section); 2646 Paint paint = lookupSectionPaint(key, state); 2647 g2.setPaint(paint); 2648 g2.fill(arc); 2649 2650 Paint outlinePaint = lookupSectionOutlinePaint(key); 2651 Stroke outlineStroke = lookupSectionOutlineStroke(key); 2652 if (this.sectionOutlinesVisible) { 2653 g2.setPaint(outlinePaint); 2654 g2.setStroke(outlineStroke); 2655 g2.draw(arc); 2656 } 2657 2658 // update the linking line target for later 2659 // add an entity for the pie section 2660 if (state.getInfo() != null) { 2661 EntityCollection entities = state.getEntityCollection(); 2662 if (entities != null) { 2663 String tip = null; 2664 if (this.toolTipGenerator != null) { 2665 tip = this.toolTipGenerator.generateToolTip( 2666 this.dataset, key); 2667 } 2668 String url = null; 2669 if (this.urlGenerator != null) { 2670 url = this.urlGenerator.generateURL(this.dataset, 2671 key, this.pieIndex); 2672 } 2673 PieSectionEntity entity = new PieSectionEntity( 2674 arc, this.dataset, this.pieIndex, section, key, 2675 tip, url); 2676 entities.add(entity); 2677 } 2678 } 2679 } 2680 } 2681 state.setLatestAngle(angle2); 2682 } 2683 2684 /** 2685 * Draws the pie section labels in the simple form. 2686 * 2687 * @param g2 the graphics device. 2688 * @param keys the section keys. 2689 * @param totalValue the total value for all sections in the pie. 2690 * @param plotArea the plot area. 2691 * @param pieArea the area containing the pie. 2692 * @param state the plot state. 2693 * 2694 * @since 1.0.7 2695 */ drawSimpleLabels(Graphics2D g2, List keys, double totalValue, Rectangle2D plotArea, Rectangle2D pieArea, PiePlotState state)2696 protected void drawSimpleLabels(Graphics2D g2, List keys, 2697 double totalValue, Rectangle2D plotArea, Rectangle2D pieArea, 2698 PiePlotState state) { 2699 2700 Composite originalComposite = g2.getComposite(); 2701 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 2702 1.0f)); 2703 2704 Rectangle2D labelsArea = this.simpleLabelOffset.createInsetRectangle( 2705 pieArea); 2706 double runningTotal = 0.0; 2707 Iterator iterator = keys.iterator(); 2708 while (iterator.hasNext()) { 2709 Comparable key = (Comparable) iterator.next(); 2710 boolean include; 2711 double v = 0.0; 2712 Number n = getDataset().getValue(key); 2713 if (n == null) { 2714 include = !getIgnoreNullValues(); 2715 } 2716 else { 2717 v = n.doubleValue(); 2718 include = getIgnoreZeroValues() ? v > 0.0 : v >= 0.0; 2719 } 2720 2721 if (include) { 2722 runningTotal = runningTotal + v; 2723 // work out the mid angle (0 - 90 and 270 - 360) = right, 2724 // otherwise left 2725 double mid = getStartAngle() + (getDirection().getFactor() 2726 * ((runningTotal - v / 2.0) * 360) / totalValue); 2727 2728 Arc2D arc = new Arc2D.Double(labelsArea, getStartAngle(), 2729 mid - getStartAngle(), Arc2D.OPEN); 2730 int x = (int) arc.getEndPoint().getX(); 2731 int y = (int) arc.getEndPoint().getY(); 2732 2733 PieSectionLabelGenerator myLabelGenerator = getLabelGenerator(); 2734 if (myLabelGenerator == null) { 2735 continue; 2736 } 2737 String label = myLabelGenerator.generateSectionLabel( 2738 this.dataset, key); 2739 if (label == null) { 2740 continue; 2741 } 2742 g2.setFont(this.labelFont); 2743 FontMetrics fm = g2.getFontMetrics(); 2744 Rectangle2D bounds = TextUtilities.getTextBounds(label, g2, fm); 2745 Rectangle2D out = this.labelPadding.createOutsetRectangle( 2746 bounds); 2747 Shape bg = ShapeUtilities.createTranslatedShape(out, 2748 x - bounds.getCenterX(), y - bounds.getCenterY()); 2749 if (this.labelShadowPaint != null 2750 && this.shadowGenerator == null) { 2751 Shape shadow = ShapeUtilities.createTranslatedShape(bg, 2752 this.shadowXOffset, this.shadowYOffset); 2753 g2.setPaint(this.labelShadowPaint); 2754 g2.fill(shadow); 2755 } 2756 if (this.labelBackgroundPaint != null) { 2757 g2.setPaint(this.labelBackgroundPaint); 2758 g2.fill(bg); 2759 } 2760 if (this.labelOutlinePaint != null 2761 && this.labelOutlineStroke != null) { 2762 g2.setPaint(this.labelOutlinePaint); 2763 g2.setStroke(this.labelOutlineStroke); 2764 g2.draw(bg); 2765 } 2766 2767 g2.setPaint(this.labelPaint); 2768 g2.setFont(this.labelFont); 2769 TextUtilities.drawAlignedString(label, g2, x, y, 2770 TextAnchor.CENTER); 2771 2772 } 2773 } 2774 2775 g2.setComposite(originalComposite); 2776 2777 } 2778 2779 /** 2780 * Draws the labels for the pie sections. 2781 * 2782 * @param g2 the graphics device. 2783 * @param keys the keys. 2784 * @param totalValue the total value. 2785 * @param plotArea the plot area. 2786 * @param linkArea the link area. 2787 * @param state the state. 2788 */ drawLabels(Graphics2D g2, List keys, double totalValue, Rectangle2D plotArea, Rectangle2D linkArea, PiePlotState state)2789 protected void drawLabels(Graphics2D g2, List keys, double totalValue, 2790 Rectangle2D plotArea, Rectangle2D linkArea, 2791 PiePlotState state) { 2792 2793 Composite originalComposite = g2.getComposite(); 2794 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 2795 1.0f)); 2796 2797 // classify the keys according to which side the label will appear... 2798 DefaultKeyedValues leftKeys = new DefaultKeyedValues(); 2799 DefaultKeyedValues rightKeys = new DefaultKeyedValues(); 2800 2801 double runningTotal = 0.0; 2802 Iterator iterator = keys.iterator(); 2803 while (iterator.hasNext()) { 2804 Comparable key = (Comparable) iterator.next(); 2805 boolean include; 2806 double v = 0.0; 2807 Number n = this.dataset.getValue(key); 2808 if (n == null) { 2809 include = !this.ignoreNullValues; 2810 } 2811 else { 2812 v = n.doubleValue(); 2813 include = this.ignoreZeroValues ? v > 0.0 : v >= 0.0; 2814 } 2815 2816 if (include) { 2817 runningTotal = runningTotal + v; 2818 // work out the mid angle (0 - 90 and 270 - 360) = right, 2819 // otherwise left 2820 double mid = this.startAngle + (this.direction.getFactor() 2821 * ((runningTotal - v / 2.0) * 360) / totalValue); 2822 if (Math.cos(Math.toRadians(mid)) < 0.0) { 2823 leftKeys.addValue(key, new Double(mid)); 2824 } 2825 else { 2826 rightKeys.addValue(key, new Double(mid)); 2827 } 2828 } 2829 } 2830 2831 g2.setFont(getLabelFont()); 2832 2833 // calculate the max label width from the plot dimensions, because 2834 // a circular pie can leave a lot more room for labels... 2835 double marginX = plotArea.getX(); 2836 double gap = plotArea.getWidth() * this.labelGap; 2837 double ww = linkArea.getX() - gap - marginX; 2838 float labelWidth = (float) this.labelPadding.trimWidth(ww); 2839 2840 // draw the labels... 2841 if (this.labelGenerator != null) { 2842 drawLeftLabels(leftKeys, g2, plotArea, linkArea, labelWidth, 2843 state); 2844 drawRightLabels(rightKeys, g2, plotArea, linkArea, labelWidth, 2845 state); 2846 } 2847 g2.setComposite(originalComposite); 2848 2849 } 2850 2851 /** 2852 * Draws the left labels. 2853 * 2854 * @param leftKeys a collection of keys and angles (to the middle of the 2855 * section, in degrees) for the sections on the left side of the 2856 * plot. 2857 * @param g2 the graphics device. 2858 * @param plotArea the plot area. 2859 * @param linkArea the link area. 2860 * @param maxLabelWidth the maximum label width. 2861 * @param state the state. 2862 */ drawLeftLabels(KeyedValues leftKeys, Graphics2D g2, Rectangle2D plotArea, Rectangle2D linkArea, float maxLabelWidth, PiePlotState state)2863 protected void drawLeftLabels(KeyedValues leftKeys, Graphics2D g2, 2864 Rectangle2D plotArea, Rectangle2D linkArea, 2865 float maxLabelWidth, PiePlotState state) { 2866 2867 this.labelDistributor.clear(); 2868 double lGap = plotArea.getWidth() * this.labelGap; 2869 double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0; 2870 for (int i = 0; i < leftKeys.getItemCount(); i++) { 2871 String label = this.labelGenerator.generateSectionLabel( 2872 this.dataset, leftKeys.getKey(i)); 2873 if (label != null) { 2874 TextBlock block = TextUtilities.createTextBlock(label, 2875 this.labelFont, this.labelPaint, maxLabelWidth, 2876 new G2TextMeasurer(g2)); 2877 TextBox labelBox = new TextBox(block); 2878 labelBox.setBackgroundPaint(this.labelBackgroundPaint); 2879 labelBox.setOutlinePaint(this.labelOutlinePaint); 2880 labelBox.setOutlineStroke(this.labelOutlineStroke); 2881 if (this.shadowGenerator == null) { 2882 labelBox.setShadowPaint(this.labelShadowPaint); 2883 } 2884 else { 2885 labelBox.setShadowPaint(null); 2886 } 2887 labelBox.setInteriorGap(this.labelPadding); 2888 double theta = Math.toRadians( 2889 leftKeys.getValue(i).doubleValue()); 2890 double baseY = state.getPieCenterY() - Math.sin(theta) 2891 * verticalLinkRadius; 2892 double hh = labelBox.getHeight(g2); 2893 2894 this.labelDistributor.addPieLabelRecord(new PieLabelRecord( 2895 leftKeys.getKey(i), theta, baseY, labelBox, hh, 2896 lGap / 2.0 + lGap / 2.0 * -Math.cos(theta), 1.0 2897 - getLabelLinkDepth() 2898 + getExplodePercent(leftKeys.getKey(i)))); 2899 } 2900 } 2901 double hh = plotArea.getHeight(); 2902 double gap = hh * getInteriorGap(); 2903 this.labelDistributor.distributeLabels(plotArea.getMinY() + gap, 2904 hh - 2 * gap); 2905 for (int i = 0; i < this.labelDistributor.getItemCount(); i++) { 2906 drawLeftLabel(g2, state, 2907 this.labelDistributor.getPieLabelRecord(i)); 2908 } 2909 } 2910 2911 /** 2912 * Draws the right labels. 2913 * 2914 * @param keys the keys. 2915 * @param g2 the graphics device. 2916 * @param plotArea the plot area. 2917 * @param linkArea the link area. 2918 * @param maxLabelWidth the maximum label width. 2919 * @param state the state. 2920 */ drawRightLabels(KeyedValues keys, Graphics2D g2, Rectangle2D plotArea, Rectangle2D linkArea, float maxLabelWidth, PiePlotState state)2921 protected void drawRightLabels(KeyedValues keys, Graphics2D g2, 2922 Rectangle2D plotArea, Rectangle2D linkArea, 2923 float maxLabelWidth, PiePlotState state) { 2924 2925 // draw the right labels... 2926 this.labelDistributor.clear(); 2927 double lGap = plotArea.getWidth() * this.labelGap; 2928 double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0; 2929 2930 for (int i = 0; i < keys.getItemCount(); i++) { 2931 String label = this.labelGenerator.generateSectionLabel( 2932 this.dataset, keys.getKey(i)); 2933 2934 if (label != null) { 2935 TextBlock block = TextUtilities.createTextBlock(label, 2936 this.labelFont, this.labelPaint, maxLabelWidth, 2937 new G2TextMeasurer(g2)); 2938 TextBox labelBox = new TextBox(block); 2939 labelBox.setBackgroundPaint(this.labelBackgroundPaint); 2940 labelBox.setOutlinePaint(this.labelOutlinePaint); 2941 labelBox.setOutlineStroke(this.labelOutlineStroke); 2942 if (this.shadowGenerator == null) { 2943 labelBox.setShadowPaint(this.labelShadowPaint); 2944 } 2945 else { 2946 labelBox.setShadowPaint(null); 2947 } 2948 labelBox.setInteriorGap(this.labelPadding); 2949 double theta = Math.toRadians(keys.getValue(i).doubleValue()); 2950 double baseY = state.getPieCenterY() 2951 - Math.sin(theta) * verticalLinkRadius; 2952 double hh = labelBox.getHeight(g2); 2953 this.labelDistributor.addPieLabelRecord(new PieLabelRecord( 2954 keys.getKey(i), theta, baseY, labelBox, hh, 2955 lGap / 2.0 + lGap / 2.0 * Math.cos(theta), 2956 1.0 - getLabelLinkDepth() 2957 + getExplodePercent(keys.getKey(i)))); 2958 } 2959 } 2960 double hh = plotArea.getHeight(); 2961 double gap = 0.00; //hh * getInteriorGap(); 2962 this.labelDistributor.distributeLabels(plotArea.getMinY() + gap, 2963 hh - 2 * gap); 2964 for (int i = 0; i < this.labelDistributor.getItemCount(); i++) { 2965 drawRightLabel(g2, state, 2966 this.labelDistributor.getPieLabelRecord(i)); 2967 } 2968 2969 } 2970 2971 /** 2972 * Returns a collection of legend items for the pie chart. 2973 * 2974 * @return The legend items (never <code>null</code>). 2975 */ 2976 @Override getLegendItems()2977 public LegendItemCollection getLegendItems() { 2978 2979 LegendItemCollection result = new LegendItemCollection(); 2980 if (this.dataset == null) { 2981 return result; 2982 } 2983 List keys = this.dataset.getKeys(); 2984 int section = 0; 2985 Shape shape = getLegendItemShape(); 2986 Iterator iterator = keys.iterator(); 2987 while (iterator.hasNext()) { 2988 Comparable key = (Comparable) iterator.next(); 2989 Number n = this.dataset.getValue(key); 2990 boolean include; 2991 if (n == null) { 2992 include = !this.ignoreNullValues; 2993 } 2994 else { 2995 double v = n.doubleValue(); 2996 if (v == 0.0) { 2997 include = !this.ignoreZeroValues; 2998 } 2999 else { 3000 include = v > 0.0; 3001 } 3002 } 3003 if (include) { 3004 String label = this.legendLabelGenerator.generateSectionLabel( 3005 this.dataset, key); 3006 if (label != null) { 3007 String description = label; 3008 String toolTipText = null; 3009 if (this.legendLabelToolTipGenerator != null) { 3010 toolTipText = this.legendLabelToolTipGenerator 3011 .generateSectionLabel(this.dataset, key); 3012 } 3013 String urlText = null; 3014 if (this.legendLabelURLGenerator != null) { 3015 urlText = this.legendLabelURLGenerator.generateURL( 3016 this.dataset, key, this.pieIndex); 3017 } 3018 Paint paint = lookupSectionPaint(key); 3019 Paint outlinePaint = lookupSectionOutlinePaint(key); 3020 Stroke outlineStroke = lookupSectionOutlineStroke(key); 3021 LegendItem item = new LegendItem(label, description, 3022 toolTipText, urlText, true, shape, true, paint, 3023 true, outlinePaint, outlineStroke, 3024 false, // line not visible 3025 new Line2D.Float(), new BasicStroke(), Color.black); 3026 item.setDataset(getDataset()); 3027 item.setSeriesIndex(this.dataset.getIndex(key)); 3028 item.setSeriesKey(key); 3029 result.add(item); 3030 } 3031 section++; 3032 } 3033 else { 3034 section++; 3035 } 3036 } 3037 return result; 3038 } 3039 3040 /** 3041 * Returns a short string describing the type of plot. 3042 * 3043 * @return The plot type. 3044 */ 3045 @Override getPlotType()3046 public String getPlotType() { 3047 return localizationResources.getString("Pie_Plot"); 3048 } 3049 3050 /** 3051 * Returns a rectangle that can be used to create a pie section (taking 3052 * into account the amount by which the pie section is 'exploded'). 3053 * 3054 * @param unexploded the area inside which the unexploded pie sections are 3055 * drawn. 3056 * @param exploded the area inside which the exploded pie sections are 3057 * drawn. 3058 * @param angle the start angle. 3059 * @param extent the extent of the arc. 3060 * @param explodePercent the amount by which the pie section is exploded. 3061 * 3062 * @return A rectangle that can be used to create a pie section. 3063 */ getArcBounds(Rectangle2D unexploded, Rectangle2D exploded, double angle, double extent, double explodePercent)3064 protected Rectangle2D getArcBounds(Rectangle2D unexploded, 3065 Rectangle2D exploded, 3066 double angle, double extent, 3067 double explodePercent) { 3068 3069 if (explodePercent == 0.0) { 3070 return unexploded; 3071 } 3072 Arc2D arc1 = new Arc2D.Double(unexploded, angle, extent / 2, 3073 Arc2D.OPEN); 3074 Point2D point1 = arc1.getEndPoint(); 3075 Arc2D.Double arc2 = new Arc2D.Double(exploded, angle, extent / 2, 3076 Arc2D.OPEN); 3077 Point2D point2 = arc2.getEndPoint(); 3078 double deltaX = (point1.getX() - point2.getX()) * explodePercent; 3079 double deltaY = (point1.getY() - point2.getY()) * explodePercent; 3080 return new Rectangle2D.Double(unexploded.getX() - deltaX, 3081 unexploded.getY() - deltaY, unexploded.getWidth(), 3082 unexploded.getHeight()); 3083 } 3084 3085 /** 3086 * Draws a section label on the left side of the pie chart. 3087 * 3088 * @param g2 the graphics device. 3089 * @param state the state. 3090 * @param record the label record. 3091 */ drawLeftLabel(Graphics2D g2, PiePlotState state, PieLabelRecord record)3092 protected void drawLeftLabel(Graphics2D g2, PiePlotState state, 3093 PieLabelRecord record) { 3094 3095 double anchorX = state.getLinkArea().getMinX(); 3096 double targetX = anchorX - record.getGap(); 3097 double targetY = record.getAllocatedY(); 3098 3099 if (this.labelLinksVisible) { 3100 double theta = record.getAngle(); 3101 double linkX = state.getPieCenterX() + Math.cos(theta) 3102 * state.getPieWRadius() * record.getLinkPercent(); 3103 double linkY = state.getPieCenterY() - Math.sin(theta) 3104 * state.getPieHRadius() * record.getLinkPercent(); 3105 double elbowX = state.getPieCenterX() + Math.cos(theta) 3106 * state.getLinkArea().getWidth() / 2.0; 3107 double elbowY = state.getPieCenterY() - Math.sin(theta) 3108 * state.getLinkArea().getHeight() / 2.0; 3109 double anchorY = elbowY; 3110 g2.setPaint(this.labelLinkPaint); 3111 g2.setStroke(this.labelLinkStroke); 3112 PieLabelLinkStyle style = getLabelLinkStyle(); 3113 if (style.equals(PieLabelLinkStyle.STANDARD)) { 3114 g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY)); 3115 g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY)); 3116 g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY)); 3117 } 3118 else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) { 3119 QuadCurve2D q = new QuadCurve2D.Float(); 3120 q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY); 3121 g2.draw(q); 3122 g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY)); 3123 } 3124 else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) { 3125 CubicCurve2D c = new CubicCurve2D .Float(); 3126 c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY, 3127 linkX, linkY); 3128 g2.draw(c); 3129 } 3130 } 3131 TextBox tb = record.getLabel(); 3132 tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.RIGHT); 3133 3134 } 3135 3136 /** 3137 * Draws a section label on the right side of the pie chart. 3138 * 3139 * @param g2 the graphics device. 3140 * @param state the state. 3141 * @param record the label record. 3142 */ drawRightLabel(Graphics2D g2, PiePlotState state, PieLabelRecord record)3143 protected void drawRightLabel(Graphics2D g2, PiePlotState state, 3144 PieLabelRecord record) { 3145 3146 double anchorX = state.getLinkArea().getMaxX(); 3147 double targetX = anchorX + record.getGap(); 3148 double targetY = record.getAllocatedY(); 3149 3150 if (this.labelLinksVisible) { 3151 double theta = record.getAngle(); 3152 double linkX = state.getPieCenterX() + Math.cos(theta) 3153 * state.getPieWRadius() * record.getLinkPercent(); 3154 double linkY = state.getPieCenterY() - Math.sin(theta) 3155 * state.getPieHRadius() * record.getLinkPercent(); 3156 double elbowX = state.getPieCenterX() + Math.cos(theta) 3157 * state.getLinkArea().getWidth() / 2.0; 3158 double elbowY = state.getPieCenterY() - Math.sin(theta) 3159 * state.getLinkArea().getHeight() / 2.0; 3160 double anchorY = elbowY; 3161 g2.setPaint(this.labelLinkPaint); 3162 g2.setStroke(this.labelLinkStroke); 3163 PieLabelLinkStyle style = getLabelLinkStyle(); 3164 if (style.equals(PieLabelLinkStyle.STANDARD)) { 3165 g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY)); 3166 g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY)); 3167 g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY)); 3168 } 3169 else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) { 3170 QuadCurve2D q = new QuadCurve2D.Float(); 3171 q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY); 3172 g2.draw(q); 3173 g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY)); 3174 } 3175 else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) { 3176 CubicCurve2D c = new CubicCurve2D .Float(); 3177 c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY, 3178 linkX, linkY); 3179 g2.draw(c); 3180 } 3181 } 3182 3183 TextBox tb = record.getLabel(); 3184 tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.LEFT); 3185 3186 } 3187 3188 /** 3189 * Returns the center for the specified section. 3190 * Checks to see if the section is exploded and recalculates the 3191 * new center if so. 3192 * 3193 * @param state PiePlotState 3194 * @param key section key. 3195 * 3196 * @return The center for the specified section. 3197 * 3198 * @since 1.0.14 3199 */ getArcCenter(PiePlotState state, Comparable key)3200 protected Point2D getArcCenter(PiePlotState state, Comparable key) { 3201 Point2D center = new Point2D.Double(state.getPieCenterX(), state 3202 .getPieCenterY()); 3203 3204 double ep = getExplodePercent(key); 3205 double mep = getMaximumExplodePercent(); 3206 if (mep > 0.0) { 3207 ep = ep / mep; 3208 } 3209 if (ep != 0) { 3210 Rectangle2D pieArea = state.getPieArea(); 3211 Rectangle2D expPieArea = state.getExplodedPieArea(); 3212 double angle1, angle2; 3213 Number n = this.dataset.getValue(key); 3214 double value = n.doubleValue(); 3215 3216 if (this.direction == Rotation.CLOCKWISE) { 3217 angle1 = state.getLatestAngle(); 3218 angle2 = angle1 - value / state.getTotal() * 360.0; 3219 } else if (this.direction == Rotation.ANTICLOCKWISE) { 3220 angle1 = state.getLatestAngle(); 3221 angle2 = angle1 + value / state.getTotal() * 360.0; 3222 } else { 3223 throw new IllegalStateException("Rotation type not recognised."); 3224 } 3225 double angle = (angle2 - angle1); 3226 3227 Arc2D arc1 = new Arc2D.Double(pieArea, angle1, angle / 2, 3228 Arc2D.OPEN); 3229 Point2D point1 = arc1.getEndPoint(); 3230 Arc2D.Double arc2 = new Arc2D.Double(expPieArea, angle1, angle / 2, 3231 Arc2D.OPEN); 3232 Point2D point2 = arc2.getEndPoint(); 3233 double deltaX = (point1.getX() - point2.getX()) * ep; 3234 double deltaY = (point1.getY() - point2.getY()) * ep; 3235 3236 center = new Point2D.Double(state.getPieCenterX() - deltaX, 3237 state.getPieCenterY() - deltaY); 3238 3239 } 3240 return center; 3241 } 3242 3243 /** 3244 * Returns the paint for the specified section. This is equivalent to 3245 * <code>lookupSectionPaint(section)</code>. 3246 * Checks to see if the user set the Paint to be of type RadialGradientPaint 3247 * If so it adjusts the center and radius to match the Pie 3248 * 3249 * @param key the section key. 3250 * @param state PiePlotState. 3251 * 3252 * @return The paint for the specified section. 3253 * 3254 * @since 1.0.14 3255 */ lookupSectionPaint(Comparable key, PiePlotState state)3256 protected Paint lookupSectionPaint(Comparable key, PiePlotState state) { 3257 Paint paint = lookupSectionPaint(key, getAutoPopulateSectionPaint()); 3258 // for a RadialGradientPaint we adjust the center and radius to match 3259 // the current pie segment... 3260 if (paint instanceof RadialGradientPaint) { 3261 RadialGradientPaint rgp = (RadialGradientPaint) paint; 3262 Point2D center = getArcCenter(state, key); 3263 float radius = (float) Math.max(state.getPieHRadius(), 3264 state.getPieWRadius()); 3265 float[] fractions = rgp.getFractions(); 3266 Color[] colors = rgp.getColors(); 3267 paint = new RadialGradientPaint(center, radius, fractions, colors); 3268 } 3269 return paint; 3270 } 3271 3272 /** 3273 * Tests this plot for equality with an arbitrary object. Note that the 3274 * plot's dataset is NOT included in the test for equality. 3275 * 3276 * @param obj the object to test against (<code>null</code> permitted). 3277 * 3278 * @return <code>true</code> or <code>false</code>. 3279 */ 3280 @Override equals(Object obj)3281 public boolean equals(Object obj) { 3282 if (obj == this) { 3283 return true; 3284 } 3285 if (!(obj instanceof PiePlot)) { 3286 return false; 3287 } 3288 if (!super.equals(obj)) { 3289 return false; 3290 } 3291 PiePlot that = (PiePlot) obj; 3292 if (this.pieIndex != that.pieIndex) { 3293 return false; 3294 } 3295 if (this.interiorGap != that.interiorGap) { 3296 return false; 3297 } 3298 if (this.circular != that.circular) { 3299 return false; 3300 } 3301 if (this.startAngle != that.startAngle) { 3302 return false; 3303 } 3304 if (this.direction != that.direction) { 3305 return false; 3306 } 3307 if (this.ignoreZeroValues != that.ignoreZeroValues) { 3308 return false; 3309 } 3310 if (this.ignoreNullValues != that.ignoreNullValues) { 3311 return false; 3312 } 3313 if (!PaintUtilities.equal(this.sectionPaint, that.sectionPaint)) { 3314 return false; 3315 } 3316 if (!ObjectUtilities.equal(this.sectionPaintMap, 3317 that.sectionPaintMap)) { 3318 return false; 3319 } 3320 if (!PaintUtilities.equal(this.baseSectionPaint, 3321 that.baseSectionPaint)) { 3322 return false; 3323 } 3324 if (this.sectionOutlinesVisible != that.sectionOutlinesVisible) { 3325 return false; 3326 } 3327 if (!PaintUtilities.equal(this.sectionOutlinePaint, 3328 that.sectionOutlinePaint)) { 3329 return false; 3330 } 3331 if (!ObjectUtilities.equal(this.sectionOutlinePaintMap, 3332 that.sectionOutlinePaintMap)) { 3333 return false; 3334 } 3335 if (!PaintUtilities.equal(this.baseSectionOutlinePaint, 3336 that.baseSectionOutlinePaint)) { 3337 return false; 3338 } 3339 if (!ObjectUtilities.equal(this.sectionOutlineStroke, 3340 that.sectionOutlineStroke)) { 3341 return false; 3342 } 3343 if (!ObjectUtilities.equal(this.sectionOutlineStrokeMap, 3344 that.sectionOutlineStrokeMap)) { 3345 return false; 3346 } 3347 if (!ObjectUtilities.equal(this.baseSectionOutlineStroke, 3348 that.baseSectionOutlineStroke)) { 3349 return false; 3350 } 3351 if (!PaintUtilities.equal(this.shadowPaint, that.shadowPaint)) { 3352 return false; 3353 } 3354 if (!(this.shadowXOffset == that.shadowXOffset)) { 3355 return false; 3356 } 3357 if (!(this.shadowYOffset == that.shadowYOffset)) { 3358 return false; 3359 } 3360 if (!ObjectUtilities.equal(this.explodePercentages, 3361 that.explodePercentages)) { 3362 return false; 3363 } 3364 if (!ObjectUtilities.equal(this.labelGenerator, 3365 that.labelGenerator)) { 3366 return false; 3367 } 3368 if (!ObjectUtilities.equal(this.labelFont, that.labelFont)) { 3369 return false; 3370 } 3371 if (!PaintUtilities.equal(this.labelPaint, that.labelPaint)) { 3372 return false; 3373 } 3374 if (!PaintUtilities.equal(this.labelBackgroundPaint, 3375 that.labelBackgroundPaint)) { 3376 return false; 3377 } 3378 if (!PaintUtilities.equal(this.labelOutlinePaint, 3379 that.labelOutlinePaint)) { 3380 return false; 3381 } 3382 if (!ObjectUtilities.equal(this.labelOutlineStroke, 3383 that.labelOutlineStroke)) { 3384 return false; 3385 } 3386 if (!PaintUtilities.equal(this.labelShadowPaint, 3387 that.labelShadowPaint)) { 3388 return false; 3389 } 3390 if (this.simpleLabels != that.simpleLabels) { 3391 return false; 3392 } 3393 if (!this.simpleLabelOffset.equals(that.simpleLabelOffset)) { 3394 return false; 3395 } 3396 if (!this.labelPadding.equals(that.labelPadding)) { 3397 return false; 3398 } 3399 if (!(this.maximumLabelWidth == that.maximumLabelWidth)) { 3400 return false; 3401 } 3402 if (!(this.labelGap == that.labelGap)) { 3403 return false; 3404 } 3405 if (!(this.labelLinkMargin == that.labelLinkMargin)) { 3406 return false; 3407 } 3408 if (this.labelLinksVisible != that.labelLinksVisible) { 3409 return false; 3410 } 3411 if (!this.labelLinkStyle.equals(that.labelLinkStyle)) { 3412 return false; 3413 } 3414 if (!PaintUtilities.equal(this.labelLinkPaint, that.labelLinkPaint)) { 3415 return false; 3416 } 3417 if (!ObjectUtilities.equal(this.labelLinkStroke, 3418 that.labelLinkStroke)) { 3419 return false; 3420 } 3421 if (!ObjectUtilities.equal(this.toolTipGenerator, 3422 that.toolTipGenerator)) { 3423 return false; 3424 } 3425 if (!ObjectUtilities.equal(this.urlGenerator, that.urlGenerator)) { 3426 return false; 3427 } 3428 if (!(this.minimumArcAngleToDraw == that.minimumArcAngleToDraw)) { 3429 return false; 3430 } 3431 if (!ShapeUtilities.equal(this.legendItemShape, that.legendItemShape)) { 3432 return false; 3433 } 3434 if (!ObjectUtilities.equal(this.legendLabelGenerator, 3435 that.legendLabelGenerator)) { 3436 return false; 3437 } 3438 if (!ObjectUtilities.equal(this.legendLabelToolTipGenerator, 3439 that.legendLabelToolTipGenerator)) { 3440 return false; 3441 } 3442 if (!ObjectUtilities.equal(this.legendLabelURLGenerator, 3443 that.legendLabelURLGenerator)) { 3444 return false; 3445 } 3446 if (this.autoPopulateSectionPaint != that.autoPopulateSectionPaint) { 3447 return false; 3448 } 3449 if (this.autoPopulateSectionOutlinePaint 3450 != that.autoPopulateSectionOutlinePaint) { 3451 return false; 3452 } 3453 if (this.autoPopulateSectionOutlineStroke 3454 != that.autoPopulateSectionOutlineStroke) { 3455 return false; 3456 } 3457 if (!ObjectUtilities.equal(this.shadowGenerator, 3458 that.shadowGenerator)) { 3459 return false; 3460 } 3461 // can't find any difference... 3462 return true; 3463 } 3464 3465 /** 3466 * Returns a clone of the plot. 3467 * 3468 * @return A clone. 3469 * 3470 * @throws CloneNotSupportedException if some component of the plot does 3471 * not support cloning. 3472 */ 3473 @Override clone()3474 public Object clone() throws CloneNotSupportedException { 3475 PiePlot clone = (PiePlot) super.clone(); 3476 if (clone.dataset != null) { 3477 clone.dataset.addChangeListener(clone); 3478 } 3479 if (this.urlGenerator instanceof PublicCloneable) { 3480 clone.urlGenerator = (PieURLGenerator) ObjectUtilities.clone( 3481 this.urlGenerator); 3482 } 3483 clone.legendItemShape = ShapeUtilities.clone(this.legendItemShape); 3484 if (this.legendLabelGenerator != null) { 3485 clone.legendLabelGenerator = (PieSectionLabelGenerator) 3486 ObjectUtilities.clone(this.legendLabelGenerator); 3487 } 3488 if (this.legendLabelToolTipGenerator != null) { 3489 clone.legendLabelToolTipGenerator = (PieSectionLabelGenerator) 3490 ObjectUtilities.clone(this.legendLabelToolTipGenerator); 3491 } 3492 if (this.legendLabelURLGenerator instanceof PublicCloneable) { 3493 clone.legendLabelURLGenerator = (PieURLGenerator) 3494 ObjectUtilities.clone(this.legendLabelURLGenerator); 3495 } 3496 return clone; 3497 } 3498 3499 /** 3500 * Provides serialization support. 3501 * 3502 * @param stream the output stream. 3503 * 3504 * @throws IOException if there is an I/O error. 3505 */ writeObject(ObjectOutputStream stream)3506 private void writeObject(ObjectOutputStream stream) throws IOException { 3507 stream.defaultWriteObject(); 3508 SerialUtilities.writePaint(this.sectionPaint, stream); 3509 SerialUtilities.writePaint(this.baseSectionPaint, stream); 3510 SerialUtilities.writePaint(this.sectionOutlinePaint, stream); 3511 SerialUtilities.writePaint(this.baseSectionOutlinePaint, stream); 3512 SerialUtilities.writeStroke(this.sectionOutlineStroke, stream); 3513 SerialUtilities.writeStroke(this.baseSectionOutlineStroke, stream); 3514 SerialUtilities.writePaint(this.shadowPaint, stream); 3515 SerialUtilities.writePaint(this.labelPaint, stream); 3516 SerialUtilities.writePaint(this.labelBackgroundPaint, stream); 3517 SerialUtilities.writePaint(this.labelOutlinePaint, stream); 3518 SerialUtilities.writeStroke(this.labelOutlineStroke, stream); 3519 SerialUtilities.writePaint(this.labelShadowPaint, stream); 3520 SerialUtilities.writePaint(this.labelLinkPaint, stream); 3521 SerialUtilities.writeStroke(this.labelLinkStroke, stream); 3522 SerialUtilities.writeShape(this.legendItemShape, stream); 3523 } 3524 3525 /** 3526 * Provides serialization support. 3527 * 3528 * @param stream the input stream. 3529 * 3530 * @throws IOException if there is an I/O error. 3531 * @throws ClassNotFoundException if there is a classpath problem. 3532 */ readObject(ObjectInputStream stream)3533 private void readObject(ObjectInputStream stream) 3534 throws IOException, ClassNotFoundException { 3535 stream.defaultReadObject(); 3536 this.sectionPaint = SerialUtilities.readPaint(stream); 3537 this.baseSectionPaint = SerialUtilities.readPaint(stream); 3538 this.sectionOutlinePaint = SerialUtilities.readPaint(stream); 3539 this.baseSectionOutlinePaint = SerialUtilities.readPaint(stream); 3540 this.sectionOutlineStroke = SerialUtilities.readStroke(stream); 3541 this.baseSectionOutlineStroke = SerialUtilities.readStroke(stream); 3542 this.shadowPaint = SerialUtilities.readPaint(stream); 3543 this.labelPaint = SerialUtilities.readPaint(stream); 3544 this.labelBackgroundPaint = SerialUtilities.readPaint(stream); 3545 this.labelOutlinePaint = SerialUtilities.readPaint(stream); 3546 this.labelOutlineStroke = SerialUtilities.readStroke(stream); 3547 this.labelShadowPaint = SerialUtilities.readPaint(stream); 3548 this.labelLinkPaint = SerialUtilities.readPaint(stream); 3549 this.labelLinkStroke = SerialUtilities.readStroke(stream); 3550 this.legendItemShape = SerialUtilities.readShape(stream); 3551 } 3552 3553 // DEPRECATED FIELDS AND METHODS... 3554 3555 /** 3556 * The paint for ALL sections (overrides list). 3557 * 3558 * @deprecated This field is redundant, it is sufficient to use 3559 * sectionPaintMap and baseSectionPaint. Deprecated as of version 3560 * 1.0.6. 3561 */ 3562 private transient Paint sectionPaint; 3563 3564 /** 3565 * The outline paint for ALL sections (overrides list). 3566 * 3567 * @deprecated This field is redundant, it is sufficient to use 3568 * sectionOutlinePaintMap and baseSectionOutlinePaint. Deprecated as 3569 * of version 1.0.6. 3570 */ 3571 private transient Paint sectionOutlinePaint; 3572 3573 /** 3574 * The outline stroke for ALL sections (overrides list). 3575 * 3576 * @deprecated This field is redundant, it is sufficient to use 3577 * sectionOutlineStrokeMap and baseSectionOutlineStroke. Deprecated as 3578 * of version 1.0.6. 3579 */ 3580 private transient Stroke sectionOutlineStroke; 3581 3582 /** 3583 * Returns the paint for the specified section. 3584 * 3585 * @param section the section index (zero-based). 3586 * 3587 * @return The paint (never <code>null</code>). 3588 * 3589 * @deprecated Use {@link #getSectionPaint(Comparable)} instead. 3590 */ getSectionPaint(int section)3591 public Paint getSectionPaint(int section) { 3592 Comparable key = getSectionKey(section); 3593 return getSectionPaint(key); 3594 } 3595 3596 /** 3597 * Sets the paint used to fill a section of the pie and sends a 3598 * {@link PlotChangeEvent} to all registered listeners. 3599 * 3600 * @param section the section index (zero-based). 3601 * @param paint the paint (<code>null</code> permitted). 3602 * 3603 * @deprecated Use {@link #setSectionPaint(Comparable, Paint)} instead. 3604 */ setSectionPaint(int section, Paint paint)3605 public void setSectionPaint(int section, Paint paint) { 3606 Comparable key = getSectionKey(section); 3607 setSectionPaint(key, paint); 3608 } 3609 3610 /** 3611 * Returns the outline paint for ALL sections in the plot. 3612 * 3613 * @return The paint (possibly <code>null</code>). 3614 * 3615 * @see #setSectionOutlinePaint(Paint) 3616 * 3617 * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} and 3618 * {@link #getBaseSectionOutlinePaint()}. Deprecated as of version 3619 * 1.0.6. 3620 */ getSectionOutlinePaint()3621 public Paint getSectionOutlinePaint() { 3622 return this.sectionOutlinePaint; 3623 } 3624 3625 /** 3626 * Sets the outline paint for ALL sections in the plot. If this is set to 3627 * </code>null</code>, then a list of paints is used instead (to allow 3628 * different colors to be used for each section). 3629 * 3630 * @param paint the paint (<code>null</code> permitted). 3631 * 3632 * @see #getSectionOutlinePaint() 3633 * 3634 * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Paint)} and 3635 * {@link #setBaseSectionOutlinePaint(Paint)}. Deprecated as of 3636 * version 1.0.6. 3637 */ setSectionOutlinePaint(Paint paint)3638 public void setSectionOutlinePaint(Paint paint) { 3639 this.sectionOutlinePaint = paint; 3640 fireChangeEvent(); 3641 } 3642 3643 /** 3644 * Returns the paint for the specified section. 3645 * 3646 * @param section the section index (zero-based). 3647 * 3648 * @return The paint (possibly <code>null</code>). 3649 * 3650 * @deprecated Use {@link #getSectionOutlinePaint(Comparable)} instead. 3651 */ getSectionOutlinePaint(int section)3652 public Paint getSectionOutlinePaint(int section) { 3653 Comparable key = getSectionKey(section); 3654 return getSectionOutlinePaint(key); 3655 } 3656 3657 /** 3658 * Sets the paint used to fill a section of the pie and sends a 3659 * {@link PlotChangeEvent} to all registered listeners. 3660 * 3661 * @param section the section index (zero-based). 3662 * @param paint the paint (<code>null</code> permitted). 3663 * 3664 * @deprecated Use {@link #setSectionOutlinePaint(Comparable, Paint)} 3665 * instead. 3666 */ setSectionOutlinePaint(int section, Paint paint)3667 public void setSectionOutlinePaint(int section, Paint paint) { 3668 Comparable key = getSectionKey(section); 3669 setSectionOutlinePaint(key, paint); 3670 } 3671 3672 /** 3673 * Returns the outline stroke for ALL sections in the plot. 3674 * 3675 * @return The stroke (possibly <code>null</code>). 3676 * 3677 * @see #setSectionOutlineStroke(Stroke) 3678 * 3679 * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} and 3680 * {@link #getBaseSectionOutlineStroke()}. Deprecated as of version 3681 * 1.0.6. 3682 */ getSectionOutlineStroke()3683 public Stroke getSectionOutlineStroke() { 3684 return this.sectionOutlineStroke; 3685 } 3686 3687 /** 3688 * Sets the outline stroke for ALL sections in the plot. If this is set to 3689 * </code>null</code>, then a list of paints is used instead (to allow 3690 * different colors to be used for each section). 3691 * 3692 * @param stroke the stroke (<code>null</code> permitted). 3693 * 3694 * @see #getSectionOutlineStroke() 3695 * 3696 * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)} and 3697 * {@link #setBaseSectionOutlineStroke(Stroke)}. Deprecated as of 3698 * version 1.0.6. 3699 */ setSectionOutlineStroke(Stroke stroke)3700 public void setSectionOutlineStroke(Stroke stroke) { 3701 this.sectionOutlineStroke = stroke; 3702 fireChangeEvent(); 3703 } 3704 3705 /** 3706 * Returns the stroke for the specified section. 3707 * 3708 * @param section the section index (zero-based). 3709 * 3710 * @return The stroke (possibly <code>null</code>). 3711 * 3712 * @deprecated Use {@link #getSectionOutlineStroke(Comparable)} instead. 3713 */ getSectionOutlineStroke(int section)3714 public Stroke getSectionOutlineStroke(int section) { 3715 Comparable key = getSectionKey(section); 3716 return getSectionOutlineStroke(key); 3717 } 3718 3719 /** 3720 * Sets the stroke used to fill a section of the pie and sends a 3721 * {@link PlotChangeEvent} to all registered listeners. 3722 * 3723 * @param section the section index (zero-based). 3724 * @param stroke the stroke (<code>null</code> permitted). 3725 * 3726 * @deprecated Use {@link #setSectionOutlineStroke(Comparable, Stroke)} 3727 * instead. 3728 */ setSectionOutlineStroke(int section, Stroke stroke)3729 public void setSectionOutlineStroke(int section, Stroke stroke) { 3730 Comparable key = getSectionKey(section); 3731 setSectionOutlineStroke(key, stroke); 3732 } 3733 3734 /** 3735 * Returns the amount that a section should be 'exploded'. 3736 * 3737 * @param section the section number. 3738 * 3739 * @return The amount that a section should be 'exploded'. 3740 * 3741 * @deprecated Use {@link #getExplodePercent(Comparable)} instead. 3742 */ getExplodePercent(int section)3743 public double getExplodePercent(int section) { 3744 Comparable key = getSectionKey(section); 3745 return getExplodePercent(key); 3746 } 3747 3748 /** 3749 * Sets the amount that a pie section should be exploded and sends a 3750 * {@link PlotChangeEvent} to all registered listeners. 3751 * 3752 * @param section the section index. 3753 * @param percent the explode percentage (0.30 = 30 percent). 3754 * 3755 * @deprecated Use {@link #setExplodePercent(Comparable, double)} instead. 3756 */ setExplodePercent(int section, double percent)3757 public void setExplodePercent(int section, double percent) { 3758 Comparable key = getSectionKey(section); 3759 setExplodePercent(key, percent); 3760 } 3761 3762 } 3763