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