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 * CategoryPlot.java 029 * ----------------- 030 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Jeremy Bowman; 034 * Arnaud Lelievre; 035 * Richard West, Advanced Micro Devices, Inc.; 036 * 037 * Changes 038 * ------- 039 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG); 040 * 21-Aug-2001 : Added standard header. Fixed DOS encoding problem (DG); 041 * 18-Sep-2001 : Updated header (DG); 042 * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG); 043 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG); 044 * 23-Oct-2001 : Changed intro and trail gaps on bar plots to use percentage of 045 * available space rather than a fixed number of units (DG); 046 * 12-Dec-2001 : Changed constructors to protected (DG); 047 * 13-Dec-2001 : Added tooltips (DG); 048 * 16-Jan-2002 : Increased maximum intro and trail gap percents, plus added 049 * some argument checking code. Thanks to Taoufik Romdhane for 050 * suggesting this (DG); 051 * 05-Feb-2002 : Added accessor methods for the tooltip generator, incorporated 052 * alpha-transparency for Plot and subclasses (DG); 053 * 06-Mar-2002 : Updated import statements (DG); 054 * 14-Mar-2002 : Renamed BarPlot.java --> CategoryPlot.java, and changed code 055 * to use the CategoryItemRenderer interface (DG); 056 * 22-Mar-2002 : Dropped the getCategories() method (DG); 057 * 23-Apr-2002 : Moved the dataset from the JFreeChart class to the Plot 058 * class (DG); 059 * 29-Apr-2002 : New methods to support printing values at the end of bars, 060 * contributed by Jeremy Bowman (DG); 061 * 11-May-2002 : New methods for label visibility and overlaid plot support, 062 * contributed by Jeremy Bowman (DG); 063 * 06-Jun-2002 : Removed the tooltip generator, this is now stored with the 064 * renderer. Moved constants into the CategoryPlotConstants 065 * interface. Updated Javadoc comments (DG); 066 * 10-Jun-2002 : Overridden datasetChanged() method to update the upper and 067 * lower bound on the range axis (if necessary), updated 068 * Javadocs (DG); 069 * 25-Jun-2002 : Removed redundant imports (DG); 070 * 20-Aug-2002 : Changed the constructor for Marker (DG); 071 * 28-Aug-2002 : Added listener notification to setDomainAxis() and 072 * setRangeAxis() (DG); 073 * 23-Sep-2002 : Added getLegendItems() method and fixed errors reported by 074 * Checkstyle (DG); 075 * 28-Oct-2002 : Changes to the CategoryDataset interface (DG); 076 * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG); 077 * 07-Nov-2002 : Renamed labelXXX as valueLabelXXX (DG); 078 * 18-Nov-2002 : Added grid settings for both domain and range axis (previously 079 * these were set in the axes) (DG); 080 * 19-Nov-2002 : Added axis location parameters to constructor (DG); 081 * 17-Jan-2003 : Moved to com.jrefinery.chart.plot package (DG); 082 * 14-Feb-2003 : Fixed bug in auto-range calculation for secondary axis (DG); 083 * 26-Mar-2003 : Implemented Serializable (DG); 084 * 02-May-2003 : Moved render() method up from subclasses. Added secondary 085 * range markers. Added an attribute to control the dataset 086 * rendering order. Added a drawAnnotations() method. Changed 087 * the axis location from an int to an AxisLocation (DG); 088 * 07-May-2003 : Merged HorizontalCategoryPlot and VerticalCategoryPlot into 089 * this class (DG); 090 * 02-Jun-2003 : Removed check for range axis compatibility (DG); 091 * 04-Jul-2003 : Added a domain gridline position attribute (DG); 092 * 21-Jul-2003 : Moved DrawingSupplier to Plot superclass (DG); 093 * 19-Aug-2003 : Added equals() method and implemented Cloneable (DG); 094 * 01-Sep-2003 : Fixed bug 797466 (no change event when secondary dataset 095 * changes) (DG); 096 * 02-Sep-2003 : Fixed bug 795209 (wrong dataset checked in render2 method) and 097 * 790407 (initialise method) (DG); 098 * 08-Sep-2003 : Added internationalization via use of properties 099 * resourceBundle (RFE 690236) (AL); 100 * 08-Sep-2003 : Fixed bug (wrong secondary range axis being used). Changed 101 * ValueAxis API (DG); 102 * 10-Sep-2003 : Fixed bug in setRangeAxis() method (DG); 103 * 15-Sep-2003 : Fixed two bugs in serialization, implemented 104 * PublicCloneable (DG); 105 * 23-Oct-2003 : Added event notification for changes to renderer (DG); 106 * 26-Nov-2003 : Fixed bug (849645) in clearRangeMarkers() method (DG); 107 * 03-Dec-2003 : Modified draw method to accept anchor (DG); 108 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 109 * 10-Mar-2004 : Fixed bug in axis range calculation when secondary renderer is 110 * stacked (DG); 111 * 12-May-2004 : Added fixed legend items (DG); 112 * 19-May-2004 : Added check for null legend item from renderer (DG); 113 * 02-Jun-2004 : Updated the DatasetRenderingOrder class (DG); 114 * 05-Nov-2004 : Renamed getDatasetsMappedToRangeAxis() 115 * --> datasetsMappedToRangeAxis(), and ensured that returned 116 * list doesn't contain null datasets (DG); 117 * 12-Nov-2004 : Implemented new Zoomable interface (DG); 118 * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() in 119 * CategoryItemRenderer (DG); 120 * 04-May-2005 : Fixed serialization of range markers (DG); 121 * 05-May-2005 : Updated draw() method parameters (DG); 122 * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per 123 * RFE 1183100 (DG); 124 * 01-Jun-2005 : Upon deserialization, register plot as a listener with its 125 * axes, dataset(s) and renderer(s) - see patch 1209475 (DG); 126 * 02-Jun-2005 : Added support for domain markers (DG); 127 * 06-Jun-2005 : Fixed equals() method for use with GradientPaint (DG); 128 * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG); 129 * 16-Jun-2005 : Added getDomainAxisCount() and getRangeAxisCount() methods, to 130 * match XYPlot (see RFE 1220495) (DG); 131 * ------------- JFREECHART 1.0.x --------------------------------------------- 132 * 11-Jan-2006 : Added configureRangeAxes() to rendererChanged(), since the 133 * renderer might influence the axis range (DG); 134 * 27-Jan-2006 : Added various null argument checks (DG); 135 * 18-Aug-2006 : Added getDatasetCount() method, plus a fix for bug drawing 136 * category labels, thanks to Adriaan Joubert (1277726) (DG); 137 * 05-Sep-2006 : Added MarkerChangeEvent support (DG); 138 * 30-Oct-2006 : Added getDomainAxisIndex(), datasetsMappedToDomainAxis() and 139 * getCategoriesForAxis() methods (DG); 140 * 22-Nov-2006 : Fire PlotChangeEvent from setColumnRenderingOrder() and 141 * setRowRenderingOrder() (DG); 142 * 29-Nov-2006 : Fix for bug 1605207 (IntervalMarker exceeds bounds of data 143 * area) (DG); 144 * 26-Feb-2007 : Fix for bug 1669218 (setDomainAxisLocation() notify argument 145 * ignored) (DG); 146 * 13-Mar-2007 : Added null argument checks for setRangeCrosshairPaint() and 147 * setRangeCrosshairStroke(), fixed clipping for 148 * annotations (DG); 149 * 07-Jun-2007 : Override drawBackground() for new GradientPaint handling (DG); 150 * 10-Jul-2007 : Added getRangeAxisIndex(ValueAxis) method (DG); 151 * 24-Sep-2007 : Implemented new zoom methods (DG); 152 * 25-Oct-2007 : Added some argument checks (DG); 153 * 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain 154 * and range markers (DG); 155 * 14-Nov-2007 : Added missing event notifications (DG); 156 * 25-Mar-2008 : Added new methods with optional notification - see patch 157 * 1913751 (DG); 158 * 07-Apr-2008 : Fixed NPE in removeDomainMarker() and 159 * removeRangeMarker() (DG); 160 * 23-Apr-2008 : Fixed equals() and clone() methods (DG); 161 * 26-Jun-2008 : Fixed crosshair support (DG); 162 * 10-Jul-2008 : Fixed outline visibility for 3D renderers (DG); 163 * 12-Aug-2008 : Added rendererCount() method (DG); 164 * 165 */ 166 167 package org.jfree.chart.plot; 168 169 import java.awt.AlphaComposite; 170 import java.awt.BasicStroke; 171 import java.awt.Color; 172 import java.awt.Composite; 173 import java.awt.Font; 174 import java.awt.Graphics2D; 175 import java.awt.Paint; 176 import java.awt.Shape; 177 import java.awt.Stroke; 178 import java.awt.geom.Line2D; 179 import java.awt.geom.Point2D; 180 import java.awt.geom.Rectangle2D; 181 import java.io.IOException; 182 import java.io.ObjectInputStream; 183 import java.io.ObjectOutputStream; 184 import java.io.Serializable; 185 import java.util.ArrayList; 186 import java.util.Collection; 187 import java.util.Collections; 188 import java.util.HashMap; 189 import java.util.Iterator; 190 import java.util.List; 191 import java.util.Map; 192 import java.util.ResourceBundle; 193 import java.util.Set; 194 195 import org.jfree.chart.LegendItem; 196 import org.jfree.chart.LegendItemCollection; 197 import org.jfree.chart.annotations.CategoryAnnotation; 198 import org.jfree.chart.axis.Axis; 199 import org.jfree.chart.axis.AxisCollection; 200 import org.jfree.chart.axis.AxisLocation; 201 import org.jfree.chart.axis.AxisSpace; 202 import org.jfree.chart.axis.AxisState; 203 import org.jfree.chart.axis.CategoryAnchor; 204 import org.jfree.chart.axis.CategoryAxis; 205 import org.jfree.chart.axis.ValueAxis; 206 import org.jfree.chart.axis.ValueTick; 207 import org.jfree.chart.event.ChartChangeEventType; 208 import org.jfree.chart.event.PlotChangeEvent; 209 import org.jfree.chart.event.RendererChangeEvent; 210 import org.jfree.chart.event.RendererChangeListener; 211 import org.jfree.chart.renderer.category.CategoryItemRenderer; 212 import org.jfree.chart.renderer.category.CategoryItemRendererState; 213 import org.jfree.data.Range; 214 import org.jfree.data.category.CategoryDataset; 215 import org.jfree.data.general.Dataset; 216 import org.jfree.data.general.DatasetChangeEvent; 217 import org.jfree.data.general.DatasetUtilities; 218 import org.jfree.io.SerialUtilities; 219 import org.jfree.ui.Layer; 220 import org.jfree.ui.RectangleEdge; 221 import org.jfree.ui.RectangleInsets; 222 import org.jfree.util.ObjectList; 223 import org.jfree.util.ObjectUtilities; 224 import org.jfree.util.PaintUtilities; 225 import org.jfree.util.PublicCloneable; 226 import org.jfree.util.ShapeUtilities; 227 import org.jfree.util.SortOrder; 228 229 /** 230 * A general plotting class that uses data from a {@link CategoryDataset} and 231 * renders each data item using a {@link CategoryItemRenderer}. 232 */ 233 public class CategoryPlot extends Plot implements ValueAxisPlot, 234 Zoomable, RendererChangeListener, Cloneable, PublicCloneable, 235 Serializable { 236 237 /** For serialization. */ 238 private static final long serialVersionUID = -3537691700434728188L; 239 240 /** 241 * The default visibility of the grid lines plotted against the domain 242 * axis. 243 */ 244 public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false; 245 246 /** 247 * The default visibility of the grid lines plotted against the range 248 * axis. 249 */ 250 public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true; 251 252 /** The default grid line stroke. */ 253 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, 254 BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[] 255 {2.0f, 2.0f}, 0.0f); 256 257 /** The default grid line paint. */ 258 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray; 259 260 /** The default value label font. */ 261 public static final Font DEFAULT_VALUE_LABEL_FONT = new Font("SansSerif", 262 Font.PLAIN, 10); 263 264 /** 265 * The default crosshair visibility. 266 * 267 * @since 1.0.5 268 */ 269 public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false; 270 271 /** 272 * The default crosshair stroke. 273 * 274 * @since 1.0.5 275 */ 276 public static final Stroke DEFAULT_CROSSHAIR_STROKE 277 = DEFAULT_GRIDLINE_STROKE; 278 279 /** 280 * The default crosshair paint. 281 * 282 * @since 1.0.5 283 */ 284 public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue; 285 286 /** The resourceBundle for the localization. */ 287 protected static ResourceBundle localizationResources 288 = ResourceBundle.getBundle( 289 "org.jfree.chart.plot.LocalizationBundle"); 290 291 /** The plot orientation. */ 292 private PlotOrientation orientation; 293 294 /** The offset between the data area and the axes. */ 295 private RectangleInsets axisOffset; 296 297 /** Storage for the domain axes. */ 298 private ObjectList domainAxes; 299 300 /** Storage for the domain axis locations. */ 301 private ObjectList domainAxisLocations; 302 303 /** 304 * A flag that controls whether or not the shared domain axis is drawn 305 * (only relevant when the plot is being used as a subplot). 306 */ 307 private boolean drawSharedDomainAxis; 308 309 /** Storage for the range axes. */ 310 private ObjectList rangeAxes; 311 312 /** Storage for the range axis locations. */ 313 private ObjectList rangeAxisLocations; 314 315 /** Storage for the datasets. */ 316 private ObjectList datasets; 317 318 /** Storage for keys that map datasets to domain axes. */ 319 private ObjectList datasetToDomainAxisMap; 320 321 /** Storage for keys that map datasets to range axes. */ 322 private ObjectList datasetToRangeAxisMap; 323 324 /** Storage for the renderers. */ 325 private ObjectList renderers; 326 327 /** The dataset rendering order. */ 328 private DatasetRenderingOrder renderingOrder 329 = DatasetRenderingOrder.REVERSE; 330 331 /** 332 * Controls the order in which the columns are traversed when rendering the 333 * data items. 334 */ 335 private SortOrder columnRenderingOrder = SortOrder.ASCENDING; 336 337 /** 338 * Controls the order in which the rows are traversed when rendering the 339 * data items. 340 */ 341 private SortOrder rowRenderingOrder = SortOrder.ASCENDING; 342 343 /** 344 * A flag that controls whether the grid-lines for the domain axis are 345 * visible. 346 */ 347 private boolean domainGridlinesVisible; 348 349 /** The position of the domain gridlines relative to the category. */ 350 private CategoryAnchor domainGridlinePosition; 351 352 /** The stroke used to draw the domain grid-lines. */ 353 private transient Stroke domainGridlineStroke; 354 355 /** The paint used to draw the domain grid-lines. */ 356 private transient Paint domainGridlinePaint; 357 358 /** 359 * A flag that controls whether the grid-lines for the range axis are 360 * visible. 361 */ 362 private boolean rangeGridlinesVisible; 363 364 /** The stroke used to draw the range axis grid-lines. */ 365 private transient Stroke rangeGridlineStroke; 366 367 /** The paint used to draw the range axis grid-lines. */ 368 private transient Paint rangeGridlinePaint; 369 370 /** The anchor value. */ 371 private double anchorValue; 372 373 /** 374 * The index for the dataset that the crosshairs are linked to (this 375 * determines which axes the crosshairs are plotted against). 376 * 377 * @since 1.0.11 378 */ 379 private int crosshairDatasetIndex; 380 381 /** 382 * A flag that controls the visibility of the domain crosshair. 383 * 384 * @since 1.0.11 385 */ 386 private boolean domainCrosshairVisible; 387 388 /** 389 * The row key for the crosshair point. 390 * 391 * @since 1.0.11 392 */ 393 private Comparable domainCrosshairRowKey; 394 395 /** 396 * The column key for the crosshair point. 397 * 398 * @since 1.0.11 399 */ 400 private Comparable domainCrosshairColumnKey; 401 402 /** 403 * The stroke used to draw the domain crosshair if it is visible. 404 * 405 * @since 1.0.11 406 */ 407 private transient Stroke domainCrosshairStroke; 408 409 /** 410 * The paint used to draw the domain crosshair if it is visible. 411 * 412 * @since 1.0.11 413 */ 414 private transient Paint domainCrosshairPaint; 415 416 /** A flag that controls whether or not a range crosshair is drawn. */ 417 private boolean rangeCrosshairVisible; 418 419 /** The range crosshair value. */ 420 private double rangeCrosshairValue; 421 422 /** The pen/brush used to draw the crosshair (if any). */ 423 private transient Stroke rangeCrosshairStroke; 424 425 /** The color used to draw the crosshair (if any). */ 426 private transient Paint rangeCrosshairPaint; 427 428 /** 429 * A flag that controls whether or not the crosshair locks onto actual 430 * data points. 431 */ 432 private boolean rangeCrosshairLockedOnData = true; 433 434 /** A map containing lists of markers for the domain axes. */ 435 private Map foregroundDomainMarkers; 436 437 /** A map containing lists of markers for the domain axes. */ 438 private Map backgroundDomainMarkers; 439 440 /** A map containing lists of markers for the range axes. */ 441 private Map foregroundRangeMarkers; 442 443 /** A map containing lists of markers for the range axes. */ 444 private Map backgroundRangeMarkers; 445 446 /** 447 * A (possibly empty) list of annotations for the plot. The list should 448 * be initialised in the constructor and never allowed to be 449 * <code>null</code>. 450 */ 451 private List annotations; 452 453 /** 454 * The weight for the plot (only relevant when the plot is used as a subplot 455 * within a combined plot). 456 */ 457 private int weight; 458 459 /** The fixed space for the domain axis. */ 460 private AxisSpace fixedDomainAxisSpace; 461 462 /** The fixed space for the range axis. */ 463 private AxisSpace fixedRangeAxisSpace; 464 465 /** 466 * An optional collection of legend items that can be returned by the 467 * getLegendItems() method. 468 */ 469 private LegendItemCollection fixedLegendItems; 470 471 /** 472 * Default constructor. 473 */ 474 public CategoryPlot() { 475 this(null, null, null, null); 476 } 477 478 /** 479 * Creates a new plot. 480 * 481 * @param dataset the dataset (<code>null</code> permitted). 482 * @param domainAxis the domain axis (<code>null</code> permitted). 483 * @param rangeAxis the range axis (<code>null</code> permitted). 484 * @param renderer the item renderer (<code>null</code> permitted). 485 * 486 */ 487 public CategoryPlot(CategoryDataset dataset, 488 CategoryAxis domainAxis, 489 ValueAxis rangeAxis, 490 CategoryItemRenderer renderer) { 491 492 super(); 493 494 this.orientation = PlotOrientation.VERTICAL; 495 496 // allocate storage for dataset, axes and renderers 497 this.domainAxes = new ObjectList(); 498 this.domainAxisLocations = new ObjectList(); 499 this.rangeAxes = new ObjectList(); 500 this.rangeAxisLocations = new ObjectList(); 501 502 this.datasetToDomainAxisMap = new ObjectList(); 503 this.datasetToRangeAxisMap = new ObjectList(); 504 505 this.renderers = new ObjectList(); 506 507 this.datasets = new ObjectList(); 508 this.datasets.set(0, dataset); 509 if (dataset != null) { 510 dataset.addChangeListener(this); 511 } 512 513 this.axisOffset = RectangleInsets.ZERO_INSETS; 514 515 setDomainAxisLocation(AxisLocation.BOTTOM_OR_LEFT, false); 516 setRangeAxisLocation(AxisLocation.TOP_OR_LEFT, false); 517 518 this.renderers.set(0, renderer); 519 if (renderer != null) { 520 renderer.setPlot(this); 521 renderer.addChangeListener(this); 522 } 523 524 this.domainAxes.set(0, domainAxis); 525 this.mapDatasetToDomainAxis(0, 0); 526 if (domainAxis != null) { 527 domainAxis.setPlot(this); 528 domainAxis.addChangeListener(this); 529 } 530 this.drawSharedDomainAxis = false; 531 532 this.rangeAxes.set(0, rangeAxis); 533 this.mapDatasetToRangeAxis(0, 0); 534 if (rangeAxis != null) { 535 rangeAxis.setPlot(this); 536 rangeAxis.addChangeListener(this); 537 } 538 539 configureDomainAxes(); 540 configureRangeAxes(); 541 542 this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE; 543 this.domainGridlinePosition = CategoryAnchor.MIDDLE; 544 this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE; 545 this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT; 546 547 this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE; 548 this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE; 549 this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT; 550 551 this.foregroundDomainMarkers = new HashMap(); 552 this.backgroundDomainMarkers = new HashMap(); 553 this.foregroundRangeMarkers = new HashMap(); 554 this.backgroundRangeMarkers = new HashMap(); 555 556 Marker baseline = new ValueMarker(0.0, new Color(0.8f, 0.8f, 0.8f, 557 0.5f), new BasicStroke(1.0f), new Color(0.85f, 0.85f, 0.95f, 558 0.5f), new BasicStroke(1.0f), 0.6f); 559 addRangeMarker(baseline, Layer.BACKGROUND); 560 561 this.anchorValue = 0.0; 562 563 this.domainCrosshairVisible = false; 564 this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; 565 this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; 566 567 this.rangeCrosshairVisible = DEFAULT_CROSSHAIR_VISIBLE; 568 this.rangeCrosshairValue = 0.0; 569 this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; 570 this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; 571 572 this.annotations = new java.util.ArrayList(); 573 574 } 575 576 /** 577 * Returns a string describing the type of plot. 578 * 579 * @return The type. 580 */ 581 public String getPlotType() { 582 return localizationResources.getString("Category_Plot"); 583 } 584 585 /** 586 * Returns the orientation of the plot. 587 * 588 * @return The orientation of the plot (never <code>null</code>). 589 * 590 * @see #setOrientation(PlotOrientation) 591 */ 592 public PlotOrientation getOrientation() { 593 return this.orientation; 594 } 595 596 /** 597 * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to 598 * all registered listeners. 599 * 600 * @param orientation the orientation (<code>null</code> not permitted). 601 * 602 * @see #getOrientation() 603 */ 604 public void setOrientation(PlotOrientation orientation) { 605 if (orientation == null) { 606 throw new IllegalArgumentException("Null 'orientation' argument."); 607 } 608 this.orientation = orientation; 609 fireChangeEvent(); 610 } 611 612 /** 613 * Returns the axis offset. 614 * 615 * @return The axis offset (never <code>null</code>). 616 * 617 * @see #setAxisOffset(RectangleInsets) 618 */ 619 public RectangleInsets getAxisOffset() { 620 return this.axisOffset; 621 } 622 623 /** 624 * Sets the axis offsets (gap between the data area and the axes) and 625 * sends a {@link PlotChangeEvent} to all registered listeners. 626 * 627 * @param offset the offset (<code>null</code> not permitted). 628 * 629 * @see #getAxisOffset() 630 */ 631 public void setAxisOffset(RectangleInsets offset) { 632 if (offset == null) { 633 throw new IllegalArgumentException("Null 'offset' argument."); 634 } 635 this.axisOffset = offset; 636 fireChangeEvent(); 637 } 638 639 /** 640 * Returns the domain axis for the plot. If the domain axis for this plot 641 * is <code>null</code>, then the method will return the parent plot's 642 * domain axis (if there is a parent plot). 643 * 644 * @return The domain axis (<code>null</code> permitted). 645 * 646 * @see #setDomainAxis(CategoryAxis) 647 */ 648 public CategoryAxis getDomainAxis() { 649 return getDomainAxis(0); 650 } 651 652 /** 653 * Returns a domain axis. 654 * 655 * @param index the axis index. 656 * 657 * @return The axis (<code>null</code> possible). 658 * 659 * @see #setDomainAxis(int, CategoryAxis) 660 */ 661 public CategoryAxis getDomainAxis(int index) { 662 CategoryAxis result = null; 663 if (index < this.domainAxes.size()) { 664 result = (CategoryAxis) this.domainAxes.get(index); 665 } 666 if (result == null) { 667 Plot parent = getParent(); 668 if (parent instanceof CategoryPlot) { 669 CategoryPlot cp = (CategoryPlot) parent; 670 result = cp.getDomainAxis(index); 671 } 672 } 673 return result; 674 } 675 676 /** 677 * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to 678 * all registered listeners. 679 * 680 * @param axis the axis (<code>null</code> permitted). 681 * 682 * @see #getDomainAxis() 683 */ 684 public void setDomainAxis(CategoryAxis axis) { 685 setDomainAxis(0, axis); 686 } 687 688 /** 689 * Sets a domain axis and sends a {@link PlotChangeEvent} to all 690 * registered listeners. 691 * 692 * @param index the axis index. 693 * @param axis the axis (<code>null</code> permitted). 694 * 695 * @see #getDomainAxis(int) 696 */ 697 public void setDomainAxis(int index, CategoryAxis axis) { 698 setDomainAxis(index, axis, true); 699 } 700 701 /** 702 * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to 703 * all registered listeners. 704 * 705 * @param index the axis index. 706 * @param axis the axis (<code>null</code> permitted). 707 * @param notify notify listeners? 708 */ 709 public void setDomainAxis(int index, CategoryAxis axis, boolean notify) { 710 CategoryAxis existing = (CategoryAxis) this.domainAxes.get(index); 711 if (existing != null) { 712 existing.removeChangeListener(this); 713 } 714 if (axis != null) { 715 axis.setPlot(this); 716 } 717 this.domainAxes.set(index, axis); 718 if (axis != null) { 719 axis.configure(); 720 axis.addChangeListener(this); 721 } 722 if (notify) { 723 fireChangeEvent(); 724 } 725 } 726 727 /** 728 * Sets the domain axes for this plot and sends a {@link PlotChangeEvent} 729 * to all registered listeners. 730 * 731 * @param axes the axes (<code>null</code> not permitted). 732 * 733 * @see #setRangeAxes(ValueAxis[]) 734 */ 735 public void setDomainAxes(CategoryAxis[] axes) { 736 for (int i = 0; i < axes.length; i++) { 737 setDomainAxis(i, axes[i], false); 738 } 739 fireChangeEvent(); 740 } 741 742 /** 743 * Returns the index of the specified axis, or <code>-1</code> if the axis 744 * is not assigned to the plot. 745 * 746 * @param axis the axis (<code>null</code> not permitted). 747 * 748 * @return The axis index. 749 * 750 * @see #getDomainAxis(int) 751 * @see #getRangeAxisIndex(ValueAxis) 752 * 753 * @since 1.0.3 754 */ 755 public int getDomainAxisIndex(CategoryAxis axis) { 756 if (axis == null) { 757 throw new IllegalArgumentException("Null 'axis' argument."); 758 } 759 return this.domainAxes.indexOf(axis); 760 } 761 762 /** 763 * Returns the domain axis location for the primary domain axis. 764 * 765 * @return The location (never <code>null</code>). 766 * 767 * @see #getRangeAxisLocation() 768 */ 769 public AxisLocation getDomainAxisLocation() { 770 return getDomainAxisLocation(0); 771 } 772 773 /** 774 * Returns the location for a domain axis. 775 * 776 * @param index the axis index. 777 * 778 * @return The location. 779 * 780 * @see #setDomainAxisLocation(int, AxisLocation) 781 */ 782 public AxisLocation getDomainAxisLocation(int index) { 783 AxisLocation result = null; 784 if (index < this.domainAxisLocations.size()) { 785 result = (AxisLocation) this.domainAxisLocations.get(index); 786 } 787 if (result == null) { 788 result = AxisLocation.getOpposite(getDomainAxisLocation(0)); 789 } 790 return result; 791 } 792 793 /** 794 * Sets the location of the domain axis and sends a {@link PlotChangeEvent} 795 * to all registered listeners. 796 * 797 * @param location the axis location (<code>null</code> not permitted). 798 * 799 * @see #getDomainAxisLocation() 800 * @see #setDomainAxisLocation(int, AxisLocation) 801 */ 802 public void setDomainAxisLocation(AxisLocation location) { 803 // delegate... 804 setDomainAxisLocation(0, location, true); 805 } 806 807 /** 808 * Sets the location of the domain axis and, if requested, sends a 809 * {@link PlotChangeEvent} to all registered listeners. 810 * 811 * @param location the axis location (<code>null</code> not permitted). 812 * @param notify a flag that controls whether listeners are notified. 813 */ 814 public void setDomainAxisLocation(AxisLocation location, boolean notify) { 815 // delegate... 816 setDomainAxisLocation(0, location, notify); 817 } 818 819 /** 820 * Sets the location for a domain axis and sends a {@link PlotChangeEvent} 821 * to all registered listeners. 822 * 823 * @param index the axis index. 824 * @param location the location. 825 * 826 * @see #getDomainAxisLocation(int) 827 * @see #setRangeAxisLocation(int, AxisLocation) 828 */ 829 public void setDomainAxisLocation(int index, AxisLocation location) { 830 // delegate... 831 setDomainAxisLocation(index, location, true); 832 } 833 834 /** 835 * Sets the location for a domain axis and sends a {@link PlotChangeEvent} 836 * to all registered listeners. 837 * 838 * @param index the axis index. 839 * @param location the location. 840 * @param notify notify listeners? 841 * 842 * @since 1.0.5 843 * 844 * @see #getDomainAxisLocation(int) 845 * @see #setRangeAxisLocation(int, AxisLocation, boolean) 846 */ 847 public void setDomainAxisLocation(int index, AxisLocation location, 848 boolean notify) { 849 if (index == 0 && location == null) { 850 throw new IllegalArgumentException( 851 "Null 'location' for index 0 not permitted."); 852 } 853 this.domainAxisLocations.set(index, location); 854 if (notify) { 855 fireChangeEvent(); 856 } 857 } 858 859 /** 860 * Returns the domain axis edge. This is derived from the axis location 861 * and the plot orientation. 862 * 863 * @return The edge (never <code>null</code>). 864 */ 865 public RectangleEdge getDomainAxisEdge() { 866 return getDomainAxisEdge(0); 867 } 868 869 /** 870 * Returns the edge for a domain axis. 871 * 872 * @param index the axis index. 873 * 874 * @return The edge (never <code>null</code>). 875 */ 876 public RectangleEdge getDomainAxisEdge(int index) { 877 RectangleEdge result = null; 878 AxisLocation location = getDomainAxisLocation(index); 879 if (location != null) { 880 result = Plot.resolveDomainAxisLocation(location, this.orientation); 881 } 882 else { 883 result = RectangleEdge.opposite(getDomainAxisEdge(0)); 884 } 885 return result; 886 } 887 888 /** 889 * Returns the number of domain axes. 890 * 891 * @return The axis count. 892 */ 893 public int getDomainAxisCount() { 894 return this.domainAxes.size(); 895 } 896 897 /** 898 * Clears the domain axes from the plot and sends a {@link PlotChangeEvent} 899 * to all registered listeners. 900 */ 901 public void clearDomainAxes() { 902 for (int i = 0; i < this.domainAxes.size(); i++) { 903 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i); 904 if (axis != null) { 905 axis.removeChangeListener(this); 906 } 907 } 908 this.domainAxes.clear(); 909 fireChangeEvent(); 910 } 911 912 /** 913 * Configures the domain axes. 914 */ 915 public void configureDomainAxes() { 916 for (int i = 0; i < this.domainAxes.size(); i++) { 917 CategoryAxis axis = (CategoryAxis) this.domainAxes.get(i); 918 if (axis != null) { 919 axis.configure(); 920 } 921 } 922 } 923 924 /** 925 * Returns the range axis for the plot. If the range axis for this plot is 926 * null, then the method will return the parent plot's range axis (if there 927 * is a parent plot). 928 * 929 * @return The range axis (possibly <code>null</code>). 930 */ 931 public ValueAxis getRangeAxis() { 932 return getRangeAxis(0); 933 } 934 935 /** 936 * Returns a range axis. 937 * 938 * @param index the axis index. 939 * 940 * @return The axis (<code>null</code> possible). 941 */ 942 public ValueAxis getRangeAxis(int index) { 943 ValueAxis result = null; 944 if (index < this.rangeAxes.size()) { 945 result = (ValueAxis) this.rangeAxes.get(index); 946 } 947 if (result == null) { 948 Plot parent = getParent(); 949 if (parent instanceof CategoryPlot) { 950 CategoryPlot cp = (CategoryPlot) parent; 951 result = cp.getRangeAxis(index); 952 } 953 } 954 return result; 955 } 956 957 /** 958 * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to 959 * all registered listeners. 960 * 961 * @param axis the axis (<code>null</code> permitted). 962 */ 963 public void setRangeAxis(ValueAxis axis) { 964 setRangeAxis(0, axis); 965 } 966 967 /** 968 * Sets a range axis and sends a {@link PlotChangeEvent} to all registered 969 * listeners. 970 * 971 * @param index the axis index. 972 * @param axis the axis. 973 */ 974 public void setRangeAxis(int index, ValueAxis axis) { 975 setRangeAxis(index, axis, true); 976 } 977 978 /** 979 * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to 980 * all registered listeners. 981 * 982 * @param index the axis index. 983 * @param axis the axis. 984 * @param notify notify listeners? 985 */ 986 public void setRangeAxis(int index, ValueAxis axis, boolean notify) { 987 ValueAxis existing = (ValueAxis) this.rangeAxes.get(index); 988 if (existing != null) { 989 existing.removeChangeListener(this); 990 } 991 if (axis != null) { 992 axis.setPlot(this); 993 } 994 this.rangeAxes.set(index, axis); 995 if (axis != null) { 996 axis.configure(); 997 axis.addChangeListener(this); 998 } 999 if (notify) { 1000 fireChangeEvent(); 1001 } 1002 } 1003 1004 /** 1005 * Sets the range axes for this plot and sends a {@link PlotChangeEvent} 1006 * to all registered listeners. 1007 * 1008 * @param axes the axes (<code>null</code> not permitted). 1009 * 1010 * @see #setDomainAxes(CategoryAxis[]) 1011 */ 1012 public void setRangeAxes(ValueAxis[] axes) { 1013 for (int i = 0; i < axes.length; i++) { 1014 setRangeAxis(i, axes[i], false); 1015 } 1016 fireChangeEvent(); 1017 } 1018 1019 /** 1020 * Returns the index of the specified axis, or <code>-1</code> if the axis 1021 * is not assigned to the plot. 1022 * 1023 * @param axis the axis (<code>null</code> not permitted). 1024 * 1025 * @return The axis index. 1026 * 1027 * @see #getRangeAxis(int) 1028 * @see #getDomainAxisIndex(CategoryAxis) 1029 * 1030 * @since 1.0.7 1031 */ 1032 public int getRangeAxisIndex(ValueAxis axis) { 1033 if (axis == null) { 1034 throw new IllegalArgumentException("Null 'axis' argument."); 1035 } 1036 int result = this.rangeAxes.indexOf(axis); 1037 if (result < 0) { // try the parent plot 1038 Plot parent = getParent(); 1039 if (parent instanceof CategoryPlot) { 1040 CategoryPlot p = (CategoryPlot) parent; 1041 result = p.getRangeAxisIndex(axis); 1042 } 1043 } 1044 return result; 1045 } 1046 1047 /** 1048 * Returns the range axis location. 1049 * 1050 * @return The location (never <code>null</code>). 1051 */ 1052 public AxisLocation getRangeAxisLocation() { 1053 return getRangeAxisLocation(0); 1054 } 1055 1056 /** 1057 * Returns the location for a range axis. 1058 * 1059 * @param index the axis index. 1060 * 1061 * @return The location. 1062 * 1063 * @see #setRangeAxisLocation(int, AxisLocation) 1064 */ 1065 public AxisLocation getRangeAxisLocation(int index) { 1066 AxisLocation result = null; 1067 if (index < this.rangeAxisLocations.size()) { 1068 result = (AxisLocation) this.rangeAxisLocations.get(index); 1069 } 1070 if (result == null) { 1071 result = AxisLocation.getOpposite(getRangeAxisLocation(0)); 1072 } 1073 return result; 1074 } 1075 1076 /** 1077 * Sets the location of the range axis and sends a {@link PlotChangeEvent} 1078 * to all registered listeners. 1079 * 1080 * @param location the location (<code>null</code> not permitted). 1081 * 1082 * @see #setRangeAxisLocation(AxisLocation, boolean) 1083 * @see #setDomainAxisLocation(AxisLocation) 1084 */ 1085 public void setRangeAxisLocation(AxisLocation location) { 1086 // defer argument checking... 1087 setRangeAxisLocation(location, true); 1088 } 1089 1090 /** 1091 * Sets the location of the range axis and, if requested, sends a 1092 * {@link PlotChangeEvent} to all registered listeners. 1093 * 1094 * @param location the location (<code>null</code> not permitted). 1095 * @param notify notify listeners? 1096 * 1097 * @see #setDomainAxisLocation(AxisLocation, boolean) 1098 */ 1099 public void setRangeAxisLocation(AxisLocation location, boolean notify) { 1100 setRangeAxisLocation(0, location, notify); 1101 } 1102 1103 /** 1104 * Sets the location for a range axis and sends a {@link PlotChangeEvent} 1105 * to all registered listeners. 1106 * 1107 * @param index the axis index. 1108 * @param location the location. 1109 * 1110 * @see #getRangeAxisLocation(int) 1111 * @see #setRangeAxisLocation(int, AxisLocation, boolean) 1112 */ 1113 public void setRangeAxisLocation(int index, AxisLocation location) { 1114 setRangeAxisLocation(index, location, true); 1115 } 1116 1117 /** 1118 * Sets the location for a range axis and sends a {@link PlotChangeEvent} 1119 * to all registered listeners. 1120 * 1121 * @param index the axis index. 1122 * @param location the location. 1123 * @param notify notify listeners? 1124 * 1125 * @see #getRangeAxisLocation(int) 1126 * @see #setDomainAxisLocation(int, AxisLocation, boolean) 1127 */ 1128 public void setRangeAxisLocation(int index, AxisLocation location, 1129 boolean notify) { 1130 if (index == 0 && location == null) { 1131 throw new IllegalArgumentException( 1132 "Null 'location' for index 0 not permitted."); 1133 } 1134 this.rangeAxisLocations.set(index, location); 1135 if (notify) { 1136 fireChangeEvent(); 1137 } 1138 } 1139 1140 /** 1141 * Returns the edge where the primary range axis is located. 1142 * 1143 * @return The edge (never <code>null</code>). 1144 */ 1145 public RectangleEdge getRangeAxisEdge() { 1146 return getRangeAxisEdge(0); 1147 } 1148 1149 /** 1150 * Returns the edge for a range axis. 1151 * 1152 * @param index the axis index. 1153 * 1154 * @return The edge. 1155 */ 1156 public RectangleEdge getRangeAxisEdge(int index) { 1157 AxisLocation location = getRangeAxisLocation(index); 1158 RectangleEdge result = Plot.resolveRangeAxisLocation(location, 1159 this.orientation); 1160 if (result == null) { 1161 result = RectangleEdge.opposite(getRangeAxisEdge(0)); 1162 } 1163 return result; 1164 } 1165 1166 /** 1167 * Returns the number of range axes. 1168 * 1169 * @return The axis count. 1170 */ 1171 public int getRangeAxisCount() { 1172 return this.rangeAxes.size(); 1173 } 1174 1175 /** 1176 * Clears the range axes from the plot and sends a {@link PlotChangeEvent} 1177 * to all registered listeners. 1178 */ 1179 public void clearRangeAxes() { 1180 for (int i = 0; i < this.rangeAxes.size(); i++) { 1181 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i); 1182 if (axis != null) { 1183 axis.removeChangeListener(this); 1184 } 1185 } 1186 this.rangeAxes.clear(); 1187 fireChangeEvent(); 1188 } 1189 1190 /** 1191 * Configures the range axes. 1192 */ 1193 public void configureRangeAxes() { 1194 for (int i = 0; i < this.rangeAxes.size(); i++) { 1195 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i); 1196 if (axis != null) { 1197 axis.configure(); 1198 } 1199 } 1200 } 1201 1202 /** 1203 * Returns the primary dataset for the plot. 1204 * 1205 * @return The primary dataset (possibly <code>null</code>). 1206 * 1207 * @see #setDataset(CategoryDataset) 1208 */ 1209 public CategoryDataset getDataset() { 1210 return getDataset(0); 1211 } 1212 1213 /** 1214 * Returns the dataset at the given index. 1215 * 1216 * @param index the dataset index. 1217 * 1218 * @return The dataset (possibly <code>null</code>). 1219 * 1220 * @see #setDataset(int, CategoryDataset) 1221 */ 1222 public CategoryDataset getDataset(int index) { 1223 CategoryDataset result = null; 1224 if (this.datasets.size() > index) { 1225 result = (CategoryDataset) this.datasets.get(index); 1226 } 1227 return result; 1228 } 1229 1230 /** 1231 * Sets the dataset for the plot, replacing the existing dataset, if there 1232 * is one. This method also calls the 1233 * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the 1234 * axis ranges if necessary and sends a {@link PlotChangeEvent} to all 1235 * registered listeners. 1236 * 1237 * @param dataset the dataset (<code>null</code> permitted). 1238 * 1239 * @see #getDataset() 1240 */ 1241 public void setDataset(CategoryDataset dataset) { 1242 setDataset(0, dataset); 1243 } 1244 1245 /** 1246 * Sets a dataset for the plot. 1247 * 1248 * @param index the dataset index. 1249 * @param dataset the dataset (<code>null</code> permitted). 1250 * 1251 * @see #getDataset(int) 1252 */ 1253 public void setDataset(int index, CategoryDataset dataset) { 1254 1255 CategoryDataset existing = (CategoryDataset) this.datasets.get(index); 1256 if (existing != null) { 1257 existing.removeChangeListener(this); 1258 } 1259 this.datasets.set(index, dataset); 1260 if (dataset != null) { 1261 dataset.addChangeListener(this); 1262 } 1263 1264 // send a dataset change event to self... 1265 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); 1266 datasetChanged(event); 1267 1268 } 1269 1270 /** 1271 * Returns the number of datasets. 1272 * 1273 * @return The number of datasets. 1274 * 1275 * @since 1.0.2 1276 */ 1277 public int getDatasetCount() { 1278 return this.datasets.size(); 1279 } 1280 1281 /** 1282 * Returns the index of the specified dataset, or <code>-1</code> if the 1283 * dataset does not belong to the plot. 1284 * 1285 * @param dataset the dataset (<code>null</code> not permitted). 1286 * 1287 * @return The index. 1288 * 1289 * @since 1.0.11 1290 */ 1291 public int indexOf(CategoryDataset dataset) { 1292 int result = -1; 1293 for (int i = 0; i < this.datasets.size(); i++) { 1294 if (dataset == this.datasets.get(i)) { 1295 result = i; 1296 break; 1297 } 1298 } 1299 return result; 1300 } 1301 1302 /** 1303 * Maps a dataset to a particular domain axis. 1304 * 1305 * @param index the dataset index (zero-based). 1306 * @param axisIndex the axis index (zero-based). 1307 * 1308 * @see #getDomainAxisForDataset(int) 1309 */ 1310 public void mapDatasetToDomainAxis(int index, int axisIndex) { 1311 this.datasetToDomainAxisMap.set(index, new Integer(axisIndex)); 1312 // fake a dataset change event to update axes... 1313 datasetChanged(new DatasetChangeEvent(this, getDataset(index))); 1314 } 1315 1316 /** 1317 * Returns the domain axis for a dataset. You can change the axis for a 1318 * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method. 1319 * 1320 * @param index the dataset index. 1321 * 1322 * @return The domain axis. 1323 * 1324 * @see #mapDatasetToDomainAxis(int, int) 1325 */ 1326 public CategoryAxis getDomainAxisForDataset(int index) { 1327 CategoryAxis result = getDomainAxis(); 1328 Integer axisIndex = (Integer) this.datasetToDomainAxisMap.get(index); 1329 if (axisIndex != null) { 1330 result = getDomainAxis(axisIndex.intValue()); 1331 } 1332 return result; 1333 } 1334 1335 /** 1336 * Maps a dataset to a particular range axis. 1337 * 1338 * @param index the dataset index (zero-based). 1339 * @param axisIndex the axis index (zero-based). 1340 * 1341 * @see #getRangeAxisForDataset(int) 1342 */ 1343 public void mapDatasetToRangeAxis(int index, int axisIndex) { 1344 this.datasetToRangeAxisMap.set(index, new Integer(axisIndex)); 1345 // fake a dataset change event to update axes... 1346 datasetChanged(new DatasetChangeEvent(this, getDataset(index))); 1347 } 1348 1349 /** 1350 * Returns the range axis for a dataset. You can change the axis for a 1351 * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method. 1352 * 1353 * @param index the dataset index. 1354 * 1355 * @return The range axis. 1356 * 1357 * @see #mapDatasetToRangeAxis(int, int) 1358 */ 1359 public ValueAxis getRangeAxisForDataset(int index) { 1360 ValueAxis result = getRangeAxis(); 1361 Integer axisIndex = (Integer) this.datasetToRangeAxisMap.get(index); 1362 if (axisIndex != null) { 1363 result = getRangeAxis(axisIndex.intValue()); 1364 } 1365 return result; 1366 } 1367 1368 /** 1369 * Returns the number of renderer slots for this plot. 1370 * 1371 * @return The number of renderer slots. 1372 * 1373 * @since 1.0.11 1374 */ 1375 public int getRendererCount() { 1376 return this.renderers.size(); 1377 } 1378 1379 /** 1380 * Returns a reference to the renderer for the plot. 1381 * 1382 * @return The renderer. 1383 * 1384 * @see #setRenderer(CategoryItemRenderer) 1385 */ 1386 public CategoryItemRenderer getRenderer() { 1387 return getRenderer(0); 1388 } 1389 1390 /** 1391 * Returns the renderer at the given index. 1392 * 1393 * @param index the renderer index. 1394 * 1395 * @return The renderer (possibly <code>null</code>). 1396 * 1397 * @see #setRenderer(int, CategoryItemRenderer) 1398 */ 1399 public CategoryItemRenderer getRenderer(int index) { 1400 CategoryItemRenderer result = null; 1401 if (this.renderers.size() > index) { 1402 result = (CategoryItemRenderer) this.renderers.get(index); 1403 } 1404 return result; 1405 } 1406 1407 /** 1408 * Sets the renderer at index 0 (sometimes referred to as the "primary" 1409 * renderer) and sends a {@link PlotChangeEvent} to all registered 1410 * listeners. 1411 * 1412 * @param renderer the renderer (<code>null</code> permitted. 1413 * 1414 * @see #getRenderer() 1415 */ 1416 public void setRenderer(CategoryItemRenderer renderer) { 1417 setRenderer(0, renderer, true); 1418 } 1419 1420 /** 1421 * Sets the renderer at index 0 (sometimes referred to as the "primary" 1422 * renderer) and, if requested, sends a {@link PlotChangeEvent} to all 1423 * registered listeners. 1424 * <p> 1425 * You can set the renderer to <code>null</code>, but this is not 1426 * recommended because: 1427 * <ul> 1428 * <li>no data will be displayed;</li> 1429 * <li>the plot background will not be painted;</li> 1430 * </ul> 1431 * 1432 * @param renderer the renderer (<code>null</code> permitted). 1433 * @param notify notify listeners? 1434 * 1435 * @see #getRenderer() 1436 */ 1437 public void setRenderer(CategoryItemRenderer renderer, boolean notify) { 1438 setRenderer(0, renderer, notify); 1439 } 1440 1441 /** 1442 * Sets the renderer at the specified index and sends a 1443 * {@link PlotChangeEvent} to all registered listeners. 1444 * 1445 * @param index the index. 1446 * @param renderer the renderer (<code>null</code> permitted). 1447 * 1448 * @see #getRenderer(int) 1449 * @see #setRenderer(int, CategoryItemRenderer, boolean) 1450 */ 1451 public void setRenderer(int index, CategoryItemRenderer renderer) { 1452 setRenderer(index, renderer, true); 1453 } 1454 1455 /** 1456 * Sets a renderer. A {@link PlotChangeEvent} is sent to all registered 1457 * listeners. 1458 * 1459 * @param index the index. 1460 * @param renderer the renderer (<code>null</code> permitted). 1461 * @param notify notify listeners? 1462 * 1463 * @see #getRenderer(int) 1464 */ 1465 public void setRenderer(int index, CategoryItemRenderer renderer, 1466 boolean notify) { 1467 1468 // stop listening to the existing renderer... 1469 CategoryItemRenderer existing 1470 = (CategoryItemRenderer) this.renderers.get(index); 1471 if (existing != null) { 1472 existing.removeChangeListener(this); 1473 } 1474 1475 // register the new renderer... 1476 this.renderers.set(index, renderer); 1477 if (renderer != null) { 1478 renderer.setPlot(this); 1479 renderer.addChangeListener(this); 1480 } 1481 1482 configureDomainAxes(); 1483 configureRangeAxes(); 1484 1485 if (notify) { 1486 fireChangeEvent(); 1487 } 1488 } 1489 1490 /** 1491 * Sets the renderers for this plot and sends a {@link PlotChangeEvent} 1492 * to all registered listeners. 1493 * 1494 * @param renderers the renderers. 1495 */ 1496 public void setRenderers(CategoryItemRenderer[] renderers) { 1497 for (int i = 0; i < renderers.length; i++) { 1498 setRenderer(i, renderers[i], false); 1499 } 1500 fireChangeEvent(); 1501 } 1502 1503 /** 1504 * Returns the renderer for the specified dataset. If the dataset doesn't 1505 * belong to the plot, this method will return <code>null</code>. 1506 * 1507 * @param dataset the dataset (<code>null</code> permitted). 1508 * 1509 * @return The renderer (possibly <code>null</code>). 1510 */ 1511 public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) { 1512 CategoryItemRenderer result = null; 1513 for (int i = 0; i < this.datasets.size(); i++) { 1514 if (this.datasets.get(i) == dataset) { 1515 result = (CategoryItemRenderer) this.renderers.get(i); 1516 break; 1517 } 1518 } 1519 return result; 1520 } 1521 1522 /** 1523 * Returns the index of the specified renderer, or <code>-1</code> if the 1524 * renderer is not assigned to this plot. 1525 * 1526 * @param renderer the renderer (<code>null</code> permitted). 1527 * 1528 * @return The renderer index. 1529 */ 1530 public int getIndexOf(CategoryItemRenderer renderer) { 1531 return this.renderers.indexOf(renderer); 1532 } 1533 1534 /** 1535 * Returns the dataset rendering order. 1536 * 1537 * @return The order (never <code>null</code>). 1538 * 1539 * @see #setDatasetRenderingOrder(DatasetRenderingOrder) 1540 */ 1541 public DatasetRenderingOrder getDatasetRenderingOrder() { 1542 return this.renderingOrder; 1543 } 1544 1545 /** 1546 * Sets the rendering order and sends a {@link PlotChangeEvent} to all 1547 * registered listeners. By default, the plot renders the primary dataset 1548 * last (so that the primary dataset overlays the secondary datasets). You 1549 * can reverse this if you want to. 1550 * 1551 * @param order the rendering order (<code>null</code> not permitted). 1552 * 1553 * @see #getDatasetRenderingOrder() 1554 */ 1555 public void setDatasetRenderingOrder(DatasetRenderingOrder order) { 1556 if (order == null) { 1557 throw new IllegalArgumentException("Null 'order' argument."); 1558 } 1559 this.renderingOrder = order; 1560 fireChangeEvent(); 1561 } 1562 1563 /** 1564 * Returns the order in which the columns are rendered. The default value 1565 * is <code>SortOrder.ASCENDING</code>. 1566 * 1567 * @return The column rendering order (never <code>null</code). 1568 * 1569 * @see #setColumnRenderingOrder(SortOrder) 1570 */ 1571 public SortOrder getColumnRenderingOrder() { 1572 return this.columnRenderingOrder; 1573 } 1574 1575 /** 1576 * Sets the column order in which the items in each dataset should be 1577 * rendered and sends a {@link PlotChangeEvent} to all registered 1578 * listeners. Note that this affects the order in which items are drawn, 1579 * NOT their position in the chart. 1580 * 1581 * @param order the order (<code>null</code> not permitted). 1582 * 1583 * @see #getColumnRenderingOrder() 1584 * @see #setRowRenderingOrder(SortOrder) 1585 */ 1586 public void setColumnRenderingOrder(SortOrder order) { 1587 if (order == null) { 1588 throw new IllegalArgumentException("Null 'order' argument."); 1589 } 1590 this.columnRenderingOrder = order; 1591 fireChangeEvent(); 1592 } 1593 1594 /** 1595 * Returns the order in which the rows should be rendered. The default 1596 * value is <code>SortOrder.ASCENDING</code>. 1597 * 1598 * @return The order (never <code>null</code>). 1599 * 1600 * @see #setRowRenderingOrder(SortOrder) 1601 */ 1602 public SortOrder getRowRenderingOrder() { 1603 return this.rowRenderingOrder; 1604 } 1605 1606 /** 1607 * Sets the row order in which the items in each dataset should be 1608 * rendered and sends a {@link PlotChangeEvent} to all registered 1609 * listeners. Note that this affects the order in which items are drawn, 1610 * NOT their position in the chart. 1611 * 1612 * @param order the order (<code>null</code> not permitted). 1613 * 1614 * @see #getRowRenderingOrder() 1615 * @see #setColumnRenderingOrder(SortOrder) 1616 */ 1617 public void setRowRenderingOrder(SortOrder order) { 1618 if (order == null) { 1619 throw new IllegalArgumentException("Null 'order' argument."); 1620 } 1621 this.rowRenderingOrder = order; 1622 fireChangeEvent(); 1623 } 1624 1625 /** 1626 * Returns the flag that controls whether the domain grid-lines are visible. 1627 * 1628 * @return The <code>true</code> or <code>false</code>. 1629 * 1630 * @see #setDomainGridlinesVisible(boolean) 1631 */ 1632 public boolean isDomainGridlinesVisible() { 1633 return this.domainGridlinesVisible; 1634 } 1635 1636 /** 1637 * Sets the flag that controls whether or not grid-lines are drawn against 1638 * the domain axis. 1639 * <p> 1640 * If the flag value changes, a {@link PlotChangeEvent} is sent to all 1641 * registered listeners. 1642 * 1643 * @param visible the new value of the flag. 1644 * 1645 * @see #isDomainGridlinesVisible() 1646 */ 1647 public void setDomainGridlinesVisible(boolean visible) { 1648 if (this.domainGridlinesVisible != visible) { 1649 this.domainGridlinesVisible = visible; 1650 fireChangeEvent(); 1651 } 1652 } 1653 1654 /** 1655 * Returns the position used for the domain gridlines. 1656 * 1657 * @return The gridline position (never <code>null</code>). 1658 * 1659 * @see #setDomainGridlinePosition(CategoryAnchor) 1660 */ 1661 public CategoryAnchor getDomainGridlinePosition() { 1662 return this.domainGridlinePosition; 1663 } 1664 1665 /** 1666 * Sets the position used for the domain gridlines and sends a 1667 * {@link PlotChangeEvent} to all registered listeners. 1668 * 1669 * @param position the position (<code>null</code> not permitted). 1670 * 1671 * @see #getDomainGridlinePosition() 1672 */ 1673 public void setDomainGridlinePosition(CategoryAnchor position) { 1674 if (position == null) { 1675 throw new IllegalArgumentException("Null 'position' argument."); 1676 } 1677 this.domainGridlinePosition = position; 1678 fireChangeEvent(); 1679 } 1680 1681 /** 1682 * Returns the stroke used to draw grid-lines against the domain axis. 1683 * 1684 * @return The stroke (never <code>null</code>). 1685 * 1686 * @see #setDomainGridlineStroke(Stroke) 1687 */ 1688 public Stroke getDomainGridlineStroke() { 1689 return this.domainGridlineStroke; 1690 } 1691 1692 /** 1693 * Sets the stroke used to draw grid-lines against the domain axis and 1694 * sends a {@link PlotChangeEvent} to all registered listeners. 1695 * 1696 * @param stroke the stroke (<code>null</code> not permitted). 1697 * 1698 * @see #getDomainGridlineStroke() 1699 */ 1700 public void setDomainGridlineStroke(Stroke stroke) { 1701 if (stroke == null) { 1702 throw new IllegalArgumentException("Null 'stroke' not permitted."); 1703 } 1704 this.domainGridlineStroke = stroke; 1705 fireChangeEvent(); 1706 } 1707 1708 /** 1709 * Returns the paint used to draw grid-lines against the domain axis. 1710 * 1711 * @return The paint (never <code>null</code>). 1712 * 1713 * @see #setDomainGridlinePaint(Paint) 1714 */ 1715 public Paint getDomainGridlinePaint() { 1716 return this.domainGridlinePaint; 1717 } 1718 1719 /** 1720 * Sets the paint used to draw the grid-lines (if any) against the domain 1721 * axis and sends a {@link PlotChangeEvent} to all registered listeners. 1722 * 1723 * @param paint the paint (<code>null</code> not permitted). 1724 * 1725 * @see #getDomainGridlinePaint() 1726 */ 1727 public void setDomainGridlinePaint(Paint paint) { 1728 if (paint == null) { 1729 throw new IllegalArgumentException("Null 'paint' argument."); 1730 } 1731 this.domainGridlinePaint = paint; 1732 fireChangeEvent(); 1733 } 1734 1735 /** 1736 * Returns the flag that controls whether the range grid-lines are visible. 1737 * 1738 * @return The flag. 1739 * 1740 * @see #setRangeGridlinesVisible(boolean) 1741 */ 1742 public boolean isRangeGridlinesVisible() { 1743 return this.rangeGridlinesVisible; 1744 } 1745 1746 /** 1747 * Sets the flag that controls whether or not grid-lines are drawn against 1748 * the range axis. If the flag changes value, a {@link PlotChangeEvent} is 1749 * sent to all registered listeners. 1750 * 1751 * @param visible the new value of the flag. 1752 * 1753 * @see #isRangeGridlinesVisible() 1754 */ 1755 public void setRangeGridlinesVisible(boolean visible) { 1756 if (this.rangeGridlinesVisible != visible) { 1757 this.rangeGridlinesVisible = visible; 1758 fireChangeEvent(); 1759 } 1760 } 1761 1762 /** 1763 * Returns the stroke used to draw the grid-lines against the range axis. 1764 * 1765 * @return The stroke (never <code>null</code>). 1766 * 1767 * @see #setRangeGridlineStroke(Stroke) 1768 */ 1769 public Stroke getRangeGridlineStroke() { 1770 return this.rangeGridlineStroke; 1771 } 1772 1773 /** 1774 * Sets the stroke used to draw the grid-lines against the range axis and 1775 * sends a {@link PlotChangeEvent} to all registered listeners. 1776 * 1777 * @param stroke the stroke (<code>null</code> not permitted). 1778 * 1779 * @see #getRangeGridlineStroke() 1780 */ 1781 public void setRangeGridlineStroke(Stroke stroke) { 1782 if (stroke == null) { 1783 throw new IllegalArgumentException("Null 'stroke' argument."); 1784 } 1785 this.rangeGridlineStroke = stroke; 1786 fireChangeEvent(); 1787 } 1788 1789 /** 1790 * Returns the paint used to draw the grid-lines against the range axis. 1791 * 1792 * @return The paint (never <code>null</code>). 1793 * 1794 * @see #setRangeGridlinePaint(Paint) 1795 */ 1796 public Paint getRangeGridlinePaint() { 1797 return this.rangeGridlinePaint; 1798 } 1799 1800 /** 1801 * Sets the paint used to draw the grid lines against the range axis and 1802 * sends a {@link PlotChangeEvent} to all registered listeners. 1803 * 1804 * @param paint the paint (<code>null</code> not permitted). 1805 * 1806 * @see #getRangeGridlinePaint() 1807 */ 1808 public void setRangeGridlinePaint(Paint paint) { 1809 if (paint == null) { 1810 throw new IllegalArgumentException("Null 'paint' argument."); 1811 } 1812 this.rangeGridlinePaint = paint; 1813 fireChangeEvent(); 1814 } 1815 1816 /** 1817 * Returns the fixed legend items, if any. 1818 * 1819 * @return The legend items (possibly <code>null</code>). 1820 * 1821 * @see #setFixedLegendItems(LegendItemCollection) 1822 */ 1823 public LegendItemCollection getFixedLegendItems() { 1824 return this.fixedLegendItems; 1825 } 1826 1827 /** 1828 * Sets the fixed legend items for the plot. Leave this set to 1829 * <code>null</code> if you prefer the legend items to be created 1830 * automatically. 1831 * 1832 * @param items the legend items (<code>null</code> permitted). 1833 * 1834 * @see #getFixedLegendItems() 1835 */ 1836 public void setFixedLegendItems(LegendItemCollection items) { 1837 this.fixedLegendItems = items; 1838 fireChangeEvent(); 1839 } 1840 1841 /** 1842 * Returns the legend items for the plot. By default, this method creates 1843 * a legend item for each series in each of the datasets. You can change 1844 * this behaviour by overriding this method. 1845 * 1846 * @return The legend items. 1847 */ 1848 public LegendItemCollection getLegendItems() { 1849 LegendItemCollection result = this.fixedLegendItems; 1850 if (result == null) { 1851 result = new LegendItemCollection(); 1852 // get the legend items for the datasets... 1853 int count = this.datasets.size(); 1854 for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) { 1855 CategoryDataset dataset = getDataset(datasetIndex); 1856 if (dataset != null) { 1857 CategoryItemRenderer renderer = getRenderer(datasetIndex); 1858 if (renderer != null) { 1859 int seriesCount = dataset.getRowCount(); 1860 for (int i = 0; i < seriesCount; i++) { 1861 LegendItem item = renderer.getLegendItem( 1862 datasetIndex, i); 1863 if (item != null) { 1864 result.add(item); 1865 } 1866 } 1867 } 1868 } 1869 } 1870 } 1871 return result; 1872 } 1873 1874 /** 1875 * Handles a 'click' on the plot by updating the anchor value. 1876 * 1877 * @param x x-coordinate of the click (in Java2D space). 1878 * @param y y-coordinate of the click (in Java2D space). 1879 * @param info information about the plot's dimensions. 1880 * 1881 */ 1882 public void handleClick(int x, int y, PlotRenderingInfo info) { 1883 1884 Rectangle2D dataArea = info.getDataArea(); 1885 if (dataArea.contains(x, y)) { 1886 // set the anchor value for the range axis... 1887 double java2D = 0.0; 1888 if (this.orientation == PlotOrientation.HORIZONTAL) { 1889 java2D = x; 1890 } 1891 else if (this.orientation == PlotOrientation.VERTICAL) { 1892 java2D = y; 1893 } 1894 RectangleEdge edge = Plot.resolveRangeAxisLocation( 1895 getRangeAxisLocation(), this.orientation); 1896 double value = getRangeAxis().java2DToValue( 1897 java2D, info.getDataArea(), edge); 1898 setAnchorValue(value); 1899 setRangeCrosshairValue(value); 1900 } 1901 1902 } 1903 1904 /** 1905 * Zooms (in or out) on the plot's value axis. 1906 * <p> 1907 * If the value 0.0 is passed in as the zoom percent, the auto-range 1908 * calculation for the axis is restored (which sets the range to include 1909 * the minimum and maximum data values, thus displaying all the data). 1910 * 1911 * @param percent the zoom amount. 1912 */ 1913 public void zoom(double percent) { 1914 1915 if (percent > 0.0) { 1916 double range = getRangeAxis().getRange().getLength(); 1917 double scaledRange = range * percent; 1918 getRangeAxis().setRange(this.anchorValue - scaledRange / 2.0, 1919 this.anchorValue + scaledRange / 2.0); 1920 } 1921 else { 1922 getRangeAxis().setAutoRange(true); 1923 } 1924 1925 } 1926 1927 /** 1928 * Receives notification of a change to the plot's dataset. 1929 * <P> 1930 * The range axis bounds will be recalculated if necessary. 1931 * 1932 * @param event information about the event (not used here). 1933 */ 1934 public void datasetChanged(DatasetChangeEvent event) { 1935 1936 int count = this.rangeAxes.size(); 1937 for (int axisIndex = 0; axisIndex < count; axisIndex++) { 1938 ValueAxis yAxis = getRangeAxis(axisIndex); 1939 if (yAxis != null) { 1940 yAxis.configure(); 1941 } 1942 } 1943 if (getParent() != null) { 1944 getParent().datasetChanged(event); 1945 } 1946 else { 1947 PlotChangeEvent e = new PlotChangeEvent(this); 1948 e.setType(ChartChangeEventType.DATASET_UPDATED); 1949 notifyListeners(e); 1950 } 1951 1952 } 1953 1954 /** 1955 * Receives notification of a renderer change event. 1956 * 1957 * @param event the event. 1958 */ 1959 public void rendererChanged(RendererChangeEvent event) { 1960 Plot parent = getParent(); 1961 if (parent != null) { 1962 if (parent instanceof RendererChangeListener) { 1963 RendererChangeListener rcl = (RendererChangeListener) parent; 1964 rcl.rendererChanged(event); 1965 } 1966 else { 1967 // this should never happen with the existing code, but throw 1968 // an exception in case future changes make it possible... 1969 throw new RuntimeException( 1970 "The renderer has changed and I don't know what to do!"); 1971 } 1972 } 1973 else { 1974 configureRangeAxes(); 1975 PlotChangeEvent e = new PlotChangeEvent(this); 1976 notifyListeners(e); 1977 } 1978 } 1979 1980 /** 1981 * Adds a marker for display (in the foreground) against the domain axis and 1982 * sends a {@link PlotChangeEvent} to all registered listeners. Typically a 1983 * marker will be drawn by the renderer as a line perpendicular to the 1984 * domain axis, however this is entirely up to the renderer. 1985 * 1986 * @param marker the marker (<code>null</code> not permitted). 1987 * 1988 * @see #removeDomainMarker(Marker) 1989 */ 1990 public void addDomainMarker(CategoryMarker marker) { 1991 addDomainMarker(marker, Layer.FOREGROUND); 1992 } 1993 1994 /** 1995 * Adds a marker for display against the domain axis and sends a 1996 * {@link PlotChangeEvent} to all registered listeners. Typically a marker 1997 * will be drawn by the renderer as a line perpendicular to the domain 1998 * axis, however this is entirely up to the renderer. 1999 * 2000 * @param marker the marker (<code>null</code> not permitted). 2001 * @param layer the layer (foreground or background) (<code>null</code> 2002 * not permitted). 2003 * 2004 * @see #removeDomainMarker(Marker, Layer) 2005 */ 2006 public void addDomainMarker(CategoryMarker marker, Layer layer) { 2007 addDomainMarker(0, marker, layer); 2008 } 2009 2010 /** 2011 * Adds a marker for display by a particular renderer and sends a 2012 * {@link PlotChangeEvent} to all registered listeners. 2013 * <P> 2014 * Typically a marker will be drawn by the renderer as a line perpendicular 2015 * to a domain axis, however this is entirely up to the renderer. 2016 * 2017 * @param index the renderer index. 2018 * @param marker the marker (<code>null</code> not permitted). 2019 * @param layer the layer (<code>null</code> not permitted). 2020 * 2021 * @see #removeDomainMarker(int, Marker, Layer) 2022 */ 2023 public void addDomainMarker(int index, CategoryMarker marker, Layer layer) { 2024 addDomainMarker(index, marker, layer, true); 2025 } 2026 2027 /** 2028 * Adds a marker for display by a particular renderer and, if requested, 2029 * sends a {@link PlotChangeEvent} to all registered listeners. 2030 * <P> 2031 * Typically a marker will be drawn by the renderer as a line perpendicular 2032 * to a domain axis, however this is entirely up to the renderer. 2033 * 2034 * @param index the renderer index. 2035 * @param marker the marker (<code>null</code> not permitted). 2036 * @param layer the layer (<code>null</code> not permitted). 2037 * @param notify notify listeners? 2038 * 2039 * @since 1.0.10 2040 * 2041 * @see #removeDomainMarker(int, Marker, Layer, boolean) 2042 */ 2043 public void addDomainMarker(int index, CategoryMarker marker, Layer layer, 2044 boolean notify) { 2045 if (marker == null) { 2046 throw new IllegalArgumentException("Null 'marker' not permitted."); 2047 } 2048 if (layer == null) { 2049 throw new IllegalArgumentException("Null 'layer' not permitted."); 2050 } 2051 Collection markers; 2052 if (layer == Layer.FOREGROUND) { 2053 markers = (Collection) this.foregroundDomainMarkers.get( 2054 new Integer(index)); 2055 if (markers == null) { 2056 markers = new java.util.ArrayList(); 2057 this.foregroundDomainMarkers.put(new Integer(index), markers); 2058 } 2059 markers.add(marker); 2060 } 2061 else if (layer == Layer.BACKGROUND) { 2062 markers = (Collection) this.backgroundDomainMarkers.get( 2063 new Integer(index)); 2064 if (markers == null) { 2065 markers = new java.util.ArrayList(); 2066 this.backgroundDomainMarkers.put(new Integer(index), markers); 2067 } 2068 markers.add(marker); 2069 } 2070 marker.addChangeListener(this); 2071 if (notify) { 2072 fireChangeEvent(); 2073 } 2074 } 2075 2076 /** 2077 * Clears all the domain markers for the plot and sends a 2078 * {@link PlotChangeEvent} to all registered listeners. 2079 * 2080 * @see #clearRangeMarkers() 2081 */ 2082 public void clearDomainMarkers() { 2083 if (this.backgroundDomainMarkers != null) { 2084 Set keys = this.backgroundDomainMarkers.keySet(); 2085 Iterator iterator = keys.iterator(); 2086 while (iterator.hasNext()) { 2087 Integer key = (Integer) iterator.next(); 2088 clearDomainMarkers(key.intValue()); 2089 } 2090 this.backgroundDomainMarkers.clear(); 2091 } 2092 if (this.foregroundDomainMarkers != null) { 2093 Set keys = this.foregroundDomainMarkers.keySet(); 2094 Iterator iterator = keys.iterator(); 2095 while (iterator.hasNext()) { 2096 Integer key = (Integer) iterator.next(); 2097 clearDomainMarkers(key.intValue()); 2098 } 2099 this.foregroundDomainMarkers.clear(); 2100 } 2101 fireChangeEvent(); 2102 } 2103 2104 /** 2105 * Returns the list of domain markers (read only) for the specified layer. 2106 * 2107 * @param layer the layer (foreground or background). 2108 * 2109 * @return The list of domain markers. 2110 */ 2111 public Collection getDomainMarkers(Layer layer) { 2112 return getDomainMarkers(0, layer); 2113 } 2114 2115 /** 2116 * Returns a collection of domain markers for a particular renderer and 2117 * layer. 2118 * 2119 * @param index the renderer index. 2120 * @param layer the layer. 2121 * 2122 * @return A collection of markers (possibly <code>null</code>). 2123 */ 2124 public Collection getDomainMarkers(int index, Layer layer) { 2125 Collection result = null; 2126 Integer key = new Integer(index); 2127 if (layer == Layer.FOREGROUND) { 2128 result = (Collection) this.foregroundDomainMarkers.get(key); 2129 } 2130 else if (layer == Layer.BACKGROUND) { 2131 result = (Collection) this.backgroundDomainMarkers.get(key); 2132 } 2133 if (result != null) { 2134 result = Collections.unmodifiableCollection(result); 2135 } 2136 return result; 2137 } 2138 2139 /** 2140 * Clears all the domain markers for the specified renderer. 2141 * 2142 * @param index the renderer index. 2143 * 2144 * @see #clearRangeMarkers(int) 2145 */ 2146 public void clearDomainMarkers(int index) { 2147 Integer key = new Integer(index); 2148 if (this.backgroundDomainMarkers != null) { 2149 Collection markers 2150 = (Collection) this.backgroundDomainMarkers.get(key); 2151 if (markers != null) { 2152 Iterator iterator = markers.iterator(); 2153 while (iterator.hasNext()) { 2154 Marker m = (Marker) iterator.next(); 2155 m.removeChangeListener(this); 2156 } 2157 markers.clear(); 2158 } 2159 } 2160 if (this.foregroundDomainMarkers != null) { 2161 Collection markers 2162 = (Collection) this.foregroundDomainMarkers.get(key); 2163 if (markers != null) { 2164 Iterator iterator = markers.iterator(); 2165 while (iterator.hasNext()) { 2166 Marker m = (Marker) iterator.next(); 2167 m.removeChangeListener(this); 2168 } 2169 markers.clear(); 2170 } 2171 } 2172 fireChangeEvent(); 2173 } 2174 2175 /** 2176 * Removes a marker for the domain axis and sends a {@link PlotChangeEvent} 2177 * to all registered listeners. 2178 * 2179 * @param marker the marker. 2180 * 2181 * @return A boolean indicating whether or not the marker was actually 2182 * removed. 2183 * 2184 * @since 1.0.7 2185 */ 2186 public boolean removeDomainMarker(Marker marker) { 2187 return removeDomainMarker(marker, Layer.FOREGROUND); 2188 } 2189 2190 /** 2191 * Removes a marker for the domain axis in the specified layer and sends a 2192 * {@link PlotChangeEvent} to all registered listeners. 2193 * 2194 * @param marker the marker (<code>null</code> not permitted). 2195 * @param layer the layer (foreground or background). 2196 * 2197 * @return A boolean indicating whether or not the marker was actually 2198 * removed. 2199 * 2200 * @since 1.0.7 2201 */ 2202 public boolean removeDomainMarker(Marker marker, Layer layer) { 2203 return removeDomainMarker(0, marker, layer); 2204 } 2205 2206 /** 2207 * Removes a marker for a specific dataset/renderer and sends a 2208 * {@link PlotChangeEvent} to all registered listeners. 2209 * 2210 * @param index the dataset/renderer index. 2211 * @param marker the marker. 2212 * @param layer the layer (foreground or background). 2213 * 2214 * @return A boolean indicating whether or not the marker was actually 2215 * removed. 2216 * 2217 * @since 1.0.7 2218 */ 2219 public boolean removeDomainMarker(int index, Marker marker, Layer layer) { 2220 return removeDomainMarker(index, marker, layer, true); 2221 } 2222 2223 /** 2224 * Removes a marker for a specific dataset/renderer and, if requested, 2225 * sends a {@link PlotChangeEvent} to all registered listeners. 2226 * 2227 * @param index the dataset/renderer index. 2228 * @param marker the marker. 2229 * @param layer the layer (foreground or background). 2230 * @param notify notify listeners? 2231 * 2232 * @return A boolean indicating whether or not the marker was actually 2233 * removed. 2234 * 2235 * @since 1.0.10 2236 */ 2237 public boolean removeDomainMarker(int index, Marker marker, Layer layer, 2238 boolean notify) { 2239 ArrayList markers; 2240 if (layer == Layer.FOREGROUND) { 2241 markers = (ArrayList) this.foregroundDomainMarkers.get(new Integer( 2242 index)); 2243 } 2244 else { 2245 markers = (ArrayList) this.backgroundDomainMarkers.get(new Integer( 2246 index)); 2247 } 2248 if (markers == null) { 2249 return false; 2250 } 2251 boolean removed = markers.remove(marker); 2252 if (removed && notify) { 2253 fireChangeEvent(); 2254 } 2255 return removed; 2256 } 2257 2258 /** 2259 * Adds a marker for display (in the foreground) against the range axis and 2260 * sends a {@link PlotChangeEvent} to all registered listeners. Typically a 2261 * marker will be drawn by the renderer as a line perpendicular to the 2262 * range axis, however this is entirely up to the renderer. 2263 * 2264 * @param marker the marker (<code>null</code> not permitted). 2265 * 2266 * @see #removeRangeMarker(Marker) 2267 */ 2268 public void addRangeMarker(Marker marker) { 2269 addRangeMarker(marker, Layer.FOREGROUND); 2270 } 2271 2272 /** 2273 * Adds a marker for display against the range axis and sends a 2274 * {@link PlotChangeEvent} to all registered listeners. Typically a marker 2275 * will be drawn by the renderer as a line perpendicular to the range axis, 2276 * however this is entirely up to the renderer. 2277 * 2278 * @param marker the marker (<code>null</code> not permitted). 2279 * @param layer the layer (foreground or background) (<code>null</code> 2280 * not permitted). 2281 * 2282 * @see #removeRangeMarker(Marker, Layer) 2283 */ 2284 public void addRangeMarker(Marker marker, Layer layer) { 2285 addRangeMarker(0, marker, layer); 2286 } 2287 2288 /** 2289 * Adds a marker for display by a particular renderer and sends a 2290 * {@link PlotChangeEvent} to all registered listeners. 2291 * <P> 2292 * Typically a marker will be drawn by the renderer as a line perpendicular 2293 * to a range axis, however this is entirely up to the renderer. 2294 * 2295 * @param index the renderer index. 2296 * @param marker the marker. 2297 * @param layer the layer. 2298 * 2299 * @see #removeRangeMarker(int, Marker, Layer) 2300 */ 2301 public void addRangeMarker(int index, Marker marker, Layer layer) { 2302 addRangeMarker(index, marker, layer, true); 2303 } 2304 2305 /** 2306 * Adds a marker for display by a particular renderer and sends a 2307 * {@link PlotChangeEvent} to all registered listeners. 2308 * <P> 2309 * Typically a marker will be drawn by the renderer as a line perpendicular 2310 * to a range axis, however this is entirely up to the renderer. 2311 * 2312 * @param index the renderer index. 2313 * @param marker the marker. 2314 * @param layer the layer. 2315 * @param notify notify listeners? 2316 * 2317 * @since 1.0.10 2318 * 2319 * @see #removeRangeMarker(int, Marker, Layer, boolean) 2320 */ 2321 public void addRangeMarker(int index, Marker marker, Layer layer, 2322 boolean notify) { 2323 Collection markers; 2324 if (layer == Layer.FOREGROUND) { 2325 markers = (Collection) this.foregroundRangeMarkers.get( 2326 new Integer(index)); 2327 if (markers == null) { 2328 markers = new java.util.ArrayList(); 2329 this.foregroundRangeMarkers.put(new Integer(index), markers); 2330 } 2331 markers.add(marker); 2332 } 2333 else if (layer == Layer.BACKGROUND) { 2334 markers = (Collection) this.backgroundRangeMarkers.get( 2335 new Integer(index)); 2336 if (markers == null) { 2337 markers = new java.util.ArrayList(); 2338 this.backgroundRangeMarkers.put(new Integer(index), markers); 2339 } 2340 markers.add(marker); 2341 } 2342 marker.addChangeListener(this); 2343 if (notify) { 2344 fireChangeEvent(); 2345 } 2346 } 2347 2348 /** 2349 * Clears all the range markers for the plot and sends a 2350 * {@link PlotChangeEvent} to all registered listeners. 2351 * 2352 * @see #clearDomainMarkers() 2353 */ 2354 public void clearRangeMarkers() { 2355 if (this.backgroundRangeMarkers != null) { 2356 Set keys = this.backgroundRangeMarkers.keySet(); 2357 Iterator iterator = keys.iterator(); 2358 while (iterator.hasNext()) { 2359 Integer key = (Integer) iterator.next(); 2360 clearRangeMarkers(key.intValue()); 2361 } 2362 this.backgroundRangeMarkers.clear(); 2363 } 2364 if (this.foregroundRangeMarkers != null) { 2365 Set keys = this.foregroundRangeMarkers.keySet(); 2366 Iterator iterator = keys.iterator(); 2367 while (iterator.hasNext()) { 2368 Integer key = (Integer) iterator.next(); 2369 clearRangeMarkers(key.intValue()); 2370 } 2371 this.foregroundRangeMarkers.clear(); 2372 } 2373 fireChangeEvent(); 2374 } 2375 2376 /** 2377 * Returns the list of range markers (read only) for the specified layer. 2378 * 2379 * @param layer the layer (foreground or background). 2380 * 2381 * @return The list of range markers. 2382 * 2383 * @see #getRangeMarkers(int, Layer) 2384 */ 2385 public Collection getRangeMarkers(Layer layer) { 2386 return getRangeMarkers(0, layer); 2387 } 2388 2389 /** 2390 * Returns a collection of range markers for a particular renderer and 2391 * layer. 2392 * 2393 * @param index the renderer index. 2394 * @param layer the layer. 2395 * 2396 * @return A collection of markers (possibly <code>null</code>). 2397 */ 2398 public Collection getRangeMarkers(int index, Layer layer) { 2399 Collection result = null; 2400 Integer key = new Integer(index); 2401 if (layer == Layer.FOREGROUND) { 2402 result = (Collection) this.foregroundRangeMarkers.get(key); 2403 } 2404 else if (layer == Layer.BACKGROUND) { 2405 result = (Collection) this.backgroundRangeMarkers.get(key); 2406 } 2407 if (result != null) { 2408 result = Collections.unmodifiableCollection(result); 2409 } 2410 return result; 2411 } 2412 2413 /** 2414 * Clears all the range markers for the specified renderer. 2415 * 2416 * @param index the renderer index. 2417 * 2418 * @see #clearDomainMarkers(int) 2419 */ 2420 public void clearRangeMarkers(int index) { 2421 Integer key = new Integer(index); 2422 if (this.backgroundRangeMarkers != null) { 2423 Collection markers 2424 = (Collection) this.backgroundRangeMarkers.get(key); 2425 if (markers != null) { 2426 Iterator iterator = markers.iterator(); 2427 while (iterator.hasNext()) { 2428 Marker m = (Marker) iterator.next(); 2429 m.removeChangeListener(this); 2430 } 2431 markers.clear(); 2432 } 2433 } 2434 if (this.foregroundRangeMarkers != null) { 2435 Collection markers 2436 = (Collection) this.foregroundRangeMarkers.get(key); 2437 if (markers != null) { 2438 Iterator iterator = markers.iterator(); 2439 while (iterator.hasNext()) { 2440 Marker m = (Marker) iterator.next(); 2441 m.removeChangeListener(this); 2442 } 2443 markers.clear(); 2444 } 2445 } 2446 fireChangeEvent(); 2447 } 2448 2449 /** 2450 * Removes a marker for the range axis and sends a {@link PlotChangeEvent} 2451 * to all registered listeners. 2452 * 2453 * @param marker the marker. 2454 * 2455 * @return A boolean indicating whether or not the marker was actually 2456 * removed. 2457 * 2458 * @since 1.0.7 2459 * 2460 * @see #addRangeMarker(Marker) 2461 */ 2462 public boolean removeRangeMarker(Marker marker) { 2463 return removeRangeMarker(marker, Layer.FOREGROUND); 2464 } 2465 2466 /** 2467 * Removes a marker for the range axis in the specified layer and sends a 2468 * {@link PlotChangeEvent} to all registered listeners. 2469 * 2470 * @param marker the marker (<code>null</code> not permitted). 2471 * @param layer the layer (foreground or background). 2472 * 2473 * @return A boolean indicating whether or not the marker was actually 2474 * removed. 2475 * 2476 * @since 1.0.7 2477 * 2478 * @see #addRangeMarker(Marker, Layer) 2479 */ 2480 public boolean removeRangeMarker(Marker marker, Layer layer) { 2481 return removeRangeMarker(0, marker, layer); 2482 } 2483 2484 /** 2485 * Removes a marker for a specific dataset/renderer and sends a 2486 * {@link PlotChangeEvent} to all registered listeners. 2487 * 2488 * @param index the dataset/renderer index. 2489 * @param marker the marker. 2490 * @param layer the layer (foreground or background). 2491 * 2492 * @return A boolean indicating whether or not the marker was actually 2493 * removed. 2494 * 2495 * @since 1.0.7 2496 * 2497 * @see #addRangeMarker(int, Marker, Layer) 2498 */ 2499 public boolean removeRangeMarker(int index, Marker marker, Layer layer) { 2500 return removeRangeMarker(index, marker, layer, true); 2501 } 2502 2503 /** 2504 * Removes a marker for a specific dataset/renderer and sends a 2505 * {@link PlotChangeEvent} to all registered listeners. 2506 * 2507 * @param index the dataset/renderer index. 2508 * @param marker the marker. 2509 * @param layer the layer (foreground or background). 2510 * @param notify notify listeners. 2511 * 2512 * @return A boolean indicating whether or not the marker was actually 2513 * removed. 2514 * 2515 * @since 1.0.10 2516 * 2517 * @see #addRangeMarker(int, Marker, Layer, boolean) 2518 */ 2519 public boolean removeRangeMarker(int index, Marker marker, Layer layer, 2520 boolean notify) { 2521 if (marker == null) { 2522 throw new IllegalArgumentException("Null 'marker' argument."); 2523 } 2524 ArrayList markers; 2525 if (layer == Layer.FOREGROUND) { 2526 markers = (ArrayList) this.foregroundRangeMarkers.get(new Integer( 2527 index)); 2528 } 2529 else { 2530 markers = (ArrayList) this.backgroundRangeMarkers.get(new Integer( 2531 index)); 2532 } 2533 if (markers == null) { 2534 return false; 2535 } 2536 boolean removed = markers.remove(marker); 2537 if (removed && notify) { 2538 fireChangeEvent(); 2539 } 2540 return removed; 2541 } 2542 2543 /** 2544 * Returns the flag that controls whether or not the domain crosshair is 2545 * displayed by the plot. 2546 * 2547 * @return A boolean. 2548 * 2549 * @since 1.0.11 2550 * 2551 * @see #setDomainCrosshairVisible(boolean) 2552 */ 2553 public boolean isDomainCrosshairVisible() { 2554 return this.domainCrosshairVisible; 2555 } 2556 2557 /** 2558 * Sets the flag that controls whether or not the domain crosshair is 2559 * displayed by the plot, and sends a {@link PlotChangeEvent} to all 2560 * registered listeners. 2561 * 2562 * @param flag the new flag value. 2563 * 2564 * @since 1.0.11 2565 * 2566 * @see #isDomainCrosshairVisible() 2567 * @see #setRangeCrosshairVisible(boolean) 2568 */ 2569 public void setDomainCrosshairVisible(boolean flag) { 2570 if (this.domainCrosshairVisible != flag) { 2571 this.domainCrosshairVisible = flag; 2572 fireChangeEvent(); 2573 } 2574 } 2575 2576 /** 2577 * Returns the row key for the domain crosshair. 2578 * 2579 * @return The row key. 2580 * 2581 * @since 1.0.11 2582 */ 2583 public Comparable getDomainCrosshairRowKey() { 2584 return this.domainCrosshairRowKey; 2585 } 2586 2587 /** 2588 * Sets the row key for the domain crosshair and sends a 2589 * {PlotChangeEvent} to all registered listeners. 2590 * 2591 * @param key the key. 2592 * 2593 * @since 1.0.11 2594 */ 2595 public void setDomainCrosshairRowKey(Comparable key) { 2596 setDomainCrosshairRowKey(key, true); 2597 } 2598 2599 /** 2600 * Sets the row key for the domain crosshair and, if requested, sends a 2601 * {PlotChangeEvent} to all registered listeners. 2602 * 2603 * @param key the key. 2604 * @param notify notify listeners? 2605 * 2606 * @since 1.0.11 2607 */ 2608 public void setDomainCrosshairRowKey(Comparable key, boolean notify) { 2609 this.domainCrosshairRowKey = key; 2610 if (notify) { 2611 fireChangeEvent(); 2612 } 2613 } 2614 2615 /** 2616 * Returns the column key for the domain crosshair. 2617 * 2618 * @return The column key. 2619 * 2620 * @since 1.0.11 2621 */ 2622 public Comparable getDomainCrosshairColumnKey() { 2623 return this.domainCrosshairColumnKey; 2624 } 2625 2626 /** 2627 * Sets the column key for the domain crosshair and sends 2628 * a {@link PlotChangeEvent} to all registered listeners. 2629 * 2630 * @param key the key. 2631 * 2632 * @since 1.0.11 2633 */ 2634 public void setDomainCrosshairColumnKey(Comparable key) { 2635 setDomainCrosshairColumnKey(key, true); 2636 } 2637 2638 /** 2639 * Sets the column key for the domain crosshair and, if requested, sends 2640 * a {@link PlotChangeEvent} to all registered listeners. 2641 * 2642 * @param key the key. 2643 * @param notify notify listeners? 2644 * 2645 * @since 1.0.11 2646 */ 2647 public void setDomainCrosshairColumnKey(Comparable key, boolean notify) { 2648 this.domainCrosshairColumnKey = key; 2649 if (notify) { 2650 fireChangeEvent(); 2651 } 2652 } 2653 2654 /** 2655 * Returns the dataset index for the crosshair. 2656 * 2657 * @return The dataset index. 2658 * 2659 * @since 1.0.11 2660 */ 2661 public int getCrosshairDatasetIndex() { 2662 return this.crosshairDatasetIndex; 2663 } 2664 2665 /** 2666 * Sets the dataset index for the crosshair and sends a 2667 * {@link PlotChangeEvent} to all registered listeners. 2668 * 2669 * @param index the index. 2670 * 2671 * @since 1.0.11 2672 */ 2673 public void setCrosshairDatasetIndex(int index) { 2674 setCrosshairDatasetIndex(index, true); 2675 } 2676 2677 /** 2678 * Sets the dataset index for the crosshair and, if requested, sends a 2679 * {@link PlotChangeEvent} to all registered listeners. 2680 * 2681 * @param index the index. 2682 * @param notify notify listeners? 2683 * 2684 * @since 1.0.11 2685 */ 2686 public void setCrosshairDatasetIndex(int index, boolean notify) { 2687 this.crosshairDatasetIndex = index; 2688 if (notify) { 2689 fireChangeEvent(); 2690 } 2691 } 2692 2693 /** 2694 * Returns the paint used to draw the domain crosshair. 2695 * 2696 * @return The paint (never <code>null</code>). 2697 * 2698 * @since 1.0.11 2699 * 2700 * @see #setDomainCrosshairPaint(Paint) 2701 * @see #getDomainCrosshairStroke() 2702 */ 2703 public Paint getDomainCrosshairPaint() { 2704 return this.domainCrosshairPaint; 2705 } 2706 2707 /** 2708 * Sets the paint used to draw the domain crosshair. 2709 * 2710 * @param paint the paint (<code>null</code> not permitted). 2711 * 2712 * @since 1.0.11 2713 * 2714 * @see #getDomainCrosshairPaint() 2715 */ 2716 public void setDomainCrosshairPaint(Paint paint) { 2717 if (paint == null) { 2718 throw new IllegalArgumentException("Null 'paint' argument."); 2719 } 2720 this.domainCrosshairPaint = paint; 2721 fireChangeEvent(); 2722 } 2723 2724 /** 2725 * Returns the stroke used to draw the domain crosshair. 2726 * 2727 * @return The stroke (never <code>null</code>). 2728 * 2729 * @since 1.0.11 2730 * 2731 * @see #setDomainCrosshairStroke(Stroke) 2732 * @see #getDomainCrosshairPaint() 2733 */ 2734 public Stroke getDomainCrosshairStroke() { 2735 return this.domainCrosshairStroke; 2736 } 2737 2738 /** 2739 * Sets the stroke used to draw the domain crosshair, and sends a 2740 * {@link PlotChangeEvent} to all registered listeners. 2741 * 2742 * @param stroke the stroke (<code>null</code> not permitted). 2743 * 2744 * @since 1.0.11 2745 * 2746 * @see #getDomainCrosshairStroke() 2747 */ 2748 public void setDomainCrosshairStroke(Stroke stroke) { 2749 if (stroke == null) { 2750 throw new IllegalArgumentException("Null 'stroke' argument."); 2751 } 2752 this.domainCrosshairStroke = stroke; 2753 } 2754 2755 /** 2756 * Returns a flag indicating whether or not the range crosshair is visible. 2757 * 2758 * @return The flag. 2759 * 2760 * @see #setRangeCrosshairVisible(boolean) 2761 */ 2762 public boolean isRangeCrosshairVisible() { 2763 return this.rangeCrosshairVisible; 2764 } 2765 2766 /** 2767 * Sets the flag indicating whether or not the range crosshair is visible. 2768 * 2769 * @param flag the new value of the flag. 2770 * 2771 * @see #isRangeCrosshairVisible() 2772 */ 2773 public void setRangeCrosshairVisible(boolean flag) { 2774 if (this.rangeCrosshairVisible != flag) { 2775 this.rangeCrosshairVisible = flag; 2776 fireChangeEvent(); 2777 } 2778 } 2779 2780 /** 2781 * Returns a flag indicating whether or not the crosshair should "lock-on" 2782 * to actual data values. 2783 * 2784 * @return The flag. 2785 * 2786 * @see #setRangeCrosshairLockedOnData(boolean) 2787 */ 2788 public boolean isRangeCrosshairLockedOnData() { 2789 return this.rangeCrosshairLockedOnData; 2790 } 2791 2792 /** 2793 * Sets the flag indicating whether or not the range crosshair should 2794 * "lock-on" to actual data values, and sends a {@link PlotChangeEvent} 2795 * to all registered listeners. 2796 * 2797 * @param flag the flag. 2798 * 2799 * @see #isRangeCrosshairLockedOnData() 2800 */ 2801 public void setRangeCrosshairLockedOnData(boolean flag) { 2802 if (this.rangeCrosshairLockedOnData != flag) { 2803 this.rangeCrosshairLockedOnData = flag; 2804 fireChangeEvent(); 2805 } 2806 } 2807 2808 /** 2809 * Returns the range crosshair value. 2810 * 2811 * @return The value. 2812 * 2813 * @see #setRangeCrosshairValue(double) 2814 */ 2815 public double getRangeCrosshairValue() { 2816 return this.rangeCrosshairValue; 2817 } 2818 2819 /** 2820 * Sets the range crosshair value and, if the crosshair is visible, sends 2821 * a {@link PlotChangeEvent} to all registered listeners. 2822 * 2823 * @param value the new value. 2824 * 2825 * @see #getRangeCrosshairValue() 2826 */ 2827 public void setRangeCrosshairValue(double value) { 2828 setRangeCrosshairValue(value, true); 2829 } 2830 2831 /** 2832 * Sets the range crosshair value and, if requested, sends a 2833 * {@link PlotChangeEvent} to all registered listeners (but only if the 2834 * crosshair is visible). 2835 * 2836 * @param value the new value. 2837 * @param notify a flag that controls whether or not listeners are 2838 * notified. 2839 * 2840 * @see #getRangeCrosshairValue() 2841 */ 2842 public void setRangeCrosshairValue(double value, boolean notify) { 2843 this.rangeCrosshairValue = value; 2844 if (isRangeCrosshairVisible() && notify) { 2845 fireChangeEvent(); 2846 } 2847 } 2848 2849 /** 2850 * Returns the pen-style (<code>Stroke</code>) used to draw the crosshair 2851 * (if visible). 2852 * 2853 * @return The crosshair stroke (never <code>null</code>). 2854 * 2855 * @see #setRangeCrosshairStroke(Stroke) 2856 * @see #isRangeCrosshairVisible() 2857 * @see #getRangeCrosshairPaint() 2858 */ 2859 public Stroke getRangeCrosshairStroke() { 2860 return this.rangeCrosshairStroke; 2861 } 2862 2863 /** 2864 * Sets the pen-style (<code>Stroke</code>) used to draw the range 2865 * crosshair (if visible), and sends a {@link PlotChangeEvent} to all 2866 * registered listeners. 2867 * 2868 * @param stroke the new crosshair stroke (<code>null</code> not 2869 * permitted). 2870 * 2871 * @see #getRangeCrosshairStroke() 2872 */ 2873 public void setRangeCrosshairStroke(Stroke stroke) { 2874 if (stroke == null) { 2875 throw new IllegalArgumentException("Null 'stroke' argument."); 2876 } 2877 this.rangeCrosshairStroke = stroke; 2878 fireChangeEvent(); 2879 } 2880 2881 /** 2882 * Returns the paint used to draw the range crosshair. 2883 * 2884 * @return The paint (never <code>null</code>). 2885 * 2886 * @see #setRangeCrosshairPaint(Paint) 2887 * @see #isRangeCrosshairVisible() 2888 * @see #getRangeCrosshairStroke() 2889 */ 2890 public Paint getRangeCrosshairPaint() { 2891 return this.rangeCrosshairPaint; 2892 } 2893 2894 /** 2895 * Sets the paint used to draw the range crosshair (if visible) and 2896 * sends a {@link PlotChangeEvent} to all registered listeners. 2897 * 2898 * @param paint the paint (<code>null</code> not permitted). 2899 * 2900 * @see #getRangeCrosshairPaint() 2901 */ 2902 public void setRangeCrosshairPaint(Paint paint) { 2903 if (paint == null) { 2904 throw new IllegalArgumentException("Null 'paint' argument."); 2905 } 2906 this.rangeCrosshairPaint = paint; 2907 fireChangeEvent(); 2908 } 2909 2910 /** 2911 * Returns the list of annotations. 2912 * 2913 * @return The list of annotations (never <code>null</code>). 2914 * 2915 * @see #addAnnotation(CategoryAnnotation) 2916 * @see #clearAnnotations() 2917 */ 2918 public List getAnnotations() { 2919 return this.annotations; 2920 } 2921 2922 /** 2923 * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all 2924 * registered listeners. 2925 * 2926 * @param annotation the annotation (<code>null</code> not permitted). 2927 * 2928 * @see #removeAnnotation(CategoryAnnotation) 2929 */ 2930 public void addAnnotation(CategoryAnnotation annotation) { 2931 addAnnotation(annotation, true); 2932 } 2933 2934 /** 2935 * Adds an annotation to the plot and, if requested, sends a 2936 * {@link PlotChangeEvent} to all registered listeners. 2937 * 2938 * @param annotation the annotation (<code>null</code> not permitted). 2939 * @param notify notify listeners? 2940 * 2941 * @since 1.0.10 2942 */ 2943 public void addAnnotation(CategoryAnnotation annotation, boolean notify) { 2944 if (annotation == null) { 2945 throw new IllegalArgumentException("Null 'annotation' argument."); 2946 } 2947 this.annotations.add(annotation); 2948 if (notify) { 2949 fireChangeEvent(); 2950 } 2951 } 2952 2953 /** 2954 * Removes an annotation from the plot and sends a {@link PlotChangeEvent} 2955 * to all registered listeners. 2956 * 2957 * @param annotation the annotation (<code>null</code> not permitted). 2958 * 2959 * @return A boolean (indicates whether or not the annotation was removed). 2960 * 2961 * @see #addAnnotation(CategoryAnnotation) 2962 */ 2963 public boolean removeAnnotation(CategoryAnnotation annotation) { 2964 return removeAnnotation(annotation, true); 2965 } 2966 2967 /** 2968 * Removes an annotation from the plot and, if requested, sends a 2969 * {@link PlotChangeEvent} to all registered listeners. 2970 * 2971 * @param annotation the annotation (<code>null</code> not permitted). 2972 * @param notify notify listeners? 2973 * 2974 * @return A boolean (indicates whether or not the annotation was removed). 2975 * 2976 * @since 1.0.10 2977 */ 2978 public boolean removeAnnotation(CategoryAnnotation annotation, 2979 boolean notify) { 2980 if (annotation == null) { 2981 throw new IllegalArgumentException("Null 'annotation' argument."); 2982 } 2983 boolean removed = this.annotations.remove(annotation); 2984 if (removed && notify) { 2985 fireChangeEvent(); 2986 } 2987 return removed; 2988 } 2989 2990 /** 2991 * Clears all the annotations and sends a {@link PlotChangeEvent} to all 2992 * registered listeners. 2993 */ 2994 public void clearAnnotations() { 2995 this.annotations.clear(); 2996 fireChangeEvent(); 2997 } 2998 2999 /** 3000 * Calculates the space required for the domain axis/axes. 3001 * 3002 * @param g2 the graphics device. 3003 * @param plotArea the plot area. 3004 * @param space a carrier for the result (<code>null</code> permitted). 3005 * 3006 * @return The required space. 3007 */ 3008 protected AxisSpace calculateDomainAxisSpace(Graphics2D g2, 3009 Rectangle2D plotArea, 3010 AxisSpace space) { 3011 3012 if (space == null) { 3013 space = new AxisSpace(); 3014 } 3015 3016 // reserve some space for the domain axis... 3017 if (this.fixedDomainAxisSpace != null) { 3018 if (this.orientation == PlotOrientation.HORIZONTAL) { 3019 space.ensureAtLeast( 3020 this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT); 3021 space.ensureAtLeast(this.fixedDomainAxisSpace.getRight(), 3022 RectangleEdge.RIGHT); 3023 } 3024 else if (this.orientation == PlotOrientation.VERTICAL) { 3025 space.ensureAtLeast(this.fixedDomainAxisSpace.getTop(), 3026 RectangleEdge.TOP); 3027 space.ensureAtLeast(this.fixedDomainAxisSpace.getBottom(), 3028 RectangleEdge.BOTTOM); 3029 } 3030 } 3031 else { 3032 // reserve space for the primary domain axis... 3033 RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( 3034 getDomainAxisLocation(), this.orientation); 3035 if (this.drawSharedDomainAxis) { 3036 space = getDomainAxis().reserveSpace(g2, this, plotArea, 3037 domainEdge, space); 3038 } 3039 3040 // reserve space for any domain axes... 3041 for (int i = 0; i < this.domainAxes.size(); i++) { 3042 Axis xAxis = (Axis) this.domainAxes.get(i); 3043 if (xAxis != null) { 3044 RectangleEdge edge = getDomainAxisEdge(i); 3045 space = xAxis.reserveSpace(g2, this, plotArea, edge, space); 3046 } 3047 } 3048 } 3049 3050 return space; 3051 3052 } 3053 3054 /** 3055 * Calculates the space required for the range axis/axes. 3056 * 3057 * @param g2 the graphics device. 3058 * @param plotArea the plot area. 3059 * @param space a carrier for the result (<code>null</code> permitted). 3060 * 3061 * @return The required space. 3062 */ 3063 protected AxisSpace calculateRangeAxisSpace(Graphics2D g2, 3064 Rectangle2D plotArea, 3065 AxisSpace space) { 3066 3067 if (space == null) { 3068 space = new AxisSpace(); 3069 } 3070 3071 // reserve some space for the range axis... 3072 if (this.fixedRangeAxisSpace != null) { 3073 if (this.orientation == PlotOrientation.HORIZONTAL) { 3074 space.ensureAtLeast(this.fixedRangeAxisSpace.getTop(), 3075 RectangleEdge.TOP); 3076 space.ensureAtLeast(this.fixedRangeAxisSpace.getBottom(), 3077 RectangleEdge.BOTTOM); 3078 } 3079 else if (this.orientation == PlotOrientation.VERTICAL) { 3080 space.ensureAtLeast(this.fixedRangeAxisSpace.getLeft(), 3081 RectangleEdge.LEFT); 3082 space.ensureAtLeast(this.fixedRangeAxisSpace.getRight(), 3083 RectangleEdge.RIGHT); 3084 } 3085 } 3086 else { 3087 // reserve space for the range axes (if any)... 3088 for (int i = 0; i < this.rangeAxes.size(); i++) { 3089 Axis yAxis = (Axis) this.rangeAxes.get(i); 3090 if (yAxis != null) { 3091 RectangleEdge edge = getRangeAxisEdge(i); 3092 space = yAxis.reserveSpace(g2, this, plotArea, edge, space); 3093 } 3094 } 3095 } 3096 return space; 3097 3098 } 3099 3100 /** 3101 * Calculates the space required for the axes. 3102 * 3103 * @param g2 the graphics device. 3104 * @param plotArea the plot area. 3105 * 3106 * @return The space required for the axes. 3107 */ 3108 protected AxisSpace calculateAxisSpace(Graphics2D g2, 3109 Rectangle2D plotArea) { 3110 AxisSpace space = new AxisSpace(); 3111 space = calculateRangeAxisSpace(g2, plotArea, space); 3112 space = calculateDomainAxisSpace(g2, plotArea, space); 3113 return space; 3114 } 3115 3116 /** 3117 * Draws the plot on a Java 2D graphics device (such as the screen or a 3118 * printer). 3119 * <P> 3120 * At your option, you may supply an instance of {@link PlotRenderingInfo}. 3121 * If you do, it will be populated with information about the drawing, 3122 * including various plot dimensions and tooltip info. 3123 * 3124 * @param g2 the graphics device. 3125 * @param area the area within which the plot (including axes) should 3126 * be drawn. 3127 * @param anchor the anchor point (<code>null</code> permitted). 3128 * @param parentState the state from the parent plot, if there is one. 3129 * @param state collects info as the chart is drawn (possibly 3130 * <code>null</code>). 3131 */ 3132 public void draw(Graphics2D g2, Rectangle2D area, 3133 Point2D anchor, 3134 PlotState parentState, 3135 PlotRenderingInfo state) { 3136 3137 // if the plot area is too small, just return... 3138 boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); 3139 boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); 3140 if (b1 || b2) { 3141 return; 3142 } 3143 3144 // record the plot area... 3145 if (state == null) { 3146 // if the incoming state is null, no information will be passed 3147 // back to the caller - but we create a temporary state to record 3148 // the plot area, since that is used later by the axes 3149 state = new PlotRenderingInfo(null); 3150 } 3151 state.setPlotArea(area); 3152 3153 // adjust the drawing area for the plot insets (if any)... 3154 RectangleInsets insets = getInsets(); 3155 insets.trim(area); 3156 3157 // calculate the data area... 3158 AxisSpace space = calculateAxisSpace(g2, area); 3159 Rectangle2D dataArea = space.shrink(area, null); 3160 this.axisOffset.trim(dataArea); 3161 3162 state.setDataArea(dataArea); 3163 3164 // if there is a renderer, it draws the background, otherwise use the 3165 // default background... 3166 if (getRenderer() != null) { 3167 getRenderer().drawBackground(g2, this, dataArea); 3168 } 3169 else { 3170 drawBackground(g2, dataArea); 3171 } 3172 3173 Map axisStateMap = drawAxes(g2, area, dataArea, state); 3174 3175 // the anchor point is typically the point where the mouse last 3176 // clicked - the crosshairs will be driven off this point... 3177 if (anchor != null && !dataArea.contains(anchor)) { 3178 anchor = ShapeUtilities.getPointInRectangle(anchor.getX(), 3179 anchor.getY(), dataArea); 3180 } 3181 CategoryCrosshairState crosshairState = new CategoryCrosshairState(); 3182 crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); 3183 crosshairState.setAnchor(anchor); 3184 3185 // specify the anchor X and Y coordinates in Java2D space, for the 3186 // cases where these are not updated during rendering (i.e. no lock 3187 // on data) 3188 crosshairState.setAnchorX(Double.NaN); 3189 crosshairState.setAnchorY(Double.NaN); 3190 if (anchor != null) { 3191 ValueAxis rangeAxis = getRangeAxis(); 3192 if (rangeAxis != null) { 3193 double y; 3194 if (getOrientation() == PlotOrientation.VERTICAL) { 3195 y = rangeAxis.java2DToValue(anchor.getY(), dataArea, 3196 getRangeAxisEdge()); 3197 } 3198 else { 3199 y = rangeAxis.java2DToValue(anchor.getX(), dataArea, 3200 getRangeAxisEdge()); 3201 } 3202 crosshairState.setAnchorY(y); 3203 } 3204 } 3205 crosshairState.setRowKey(getDomainCrosshairRowKey()); 3206 crosshairState.setColumnKey(getDomainCrosshairColumnKey()); 3207 crosshairState.setCrosshairY(getRangeCrosshairValue()); 3208 3209 // don't let anyone draw outside the data area 3210 Shape savedClip = g2.getClip(); 3211 g2.clip(dataArea); 3212 3213 drawDomainGridlines(g2, dataArea); 3214 3215 AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis()); 3216 if (rangeAxisState == null) { 3217 if (parentState != null) { 3218 rangeAxisState = (AxisState) parentState.getSharedAxisStates() 3219 .get(getRangeAxis()); 3220 } 3221 } 3222 if (rangeAxisState != null) { 3223 drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks()); 3224 } 3225 3226 // draw the markers... 3227 for (int i = 0; i < this.renderers.size(); i++) { 3228 drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND); 3229 } 3230 for (int i = 0; i < this.renderers.size(); i++) { 3231 drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND); 3232 } 3233 3234 // now render data items... 3235 boolean foundData = false; 3236 3237 // set up the alpha-transparency... 3238 Composite originalComposite = g2.getComposite(); 3239 g2.setComposite(AlphaComposite.getInstance( 3240 AlphaComposite.SRC_OVER, getForegroundAlpha())); 3241 3242 DatasetRenderingOrder order = getDatasetRenderingOrder(); 3243 if (order == DatasetRenderingOrder.FORWARD) { 3244 for (int i = 0; i < this.datasets.size(); i++) { 3245 foundData = render(g2, dataArea, i, state, crosshairState) 3246 || foundData; 3247 } 3248 } 3249 else { // DatasetRenderingOrder.REVERSE 3250 for (int i = this.datasets.size() - 1; i >= 0; i--) { 3251 foundData = render(g2, dataArea, i, state, crosshairState) 3252 || foundData; 3253 } 3254 } 3255 // draw the foreground markers... 3256 for (int i = 0; i < this.renderers.size(); i++) { 3257 drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND); 3258 } 3259 for (int i = 0; i < this.renderers.size(); i++) { 3260 drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND); 3261 } 3262 3263 // draw the annotations (if any)... 3264 drawAnnotations(g2, dataArea); 3265 3266 g2.setClip(savedClip); 3267 g2.setComposite(originalComposite); 3268 3269 if (!foundData) { 3270 drawNoDataMessage(g2, dataArea); 3271 } 3272 3273 int datasetIndex = crosshairState.getDatasetIndex(); 3274 setCrosshairDatasetIndex(datasetIndex, false); 3275 3276 // draw domain crosshair if required... 3277 Comparable rowKey = crosshairState.getRowKey(); 3278 Comparable columnKey = crosshairState.getColumnKey(); 3279 setDomainCrosshairRowKey(rowKey, false); 3280 setDomainCrosshairColumnKey(columnKey, false); 3281 if (isDomainCrosshairVisible() && columnKey != null) { 3282 Paint paint = getDomainCrosshairPaint(); 3283 Stroke stroke = getDomainCrosshairStroke(); 3284 drawDomainCrosshair(g2, dataArea, this.orientation, 3285 datasetIndex, rowKey, columnKey, stroke, paint); 3286 } 3287 3288 // draw range crosshair if required... 3289 ValueAxis yAxis = getRangeAxisForDataset(datasetIndex); 3290 RectangleEdge yAxisEdge = getRangeAxisEdge(); 3291 if (!this.rangeCrosshairLockedOnData && anchor != null) { 3292 double yy; 3293 if (getOrientation() == PlotOrientation.VERTICAL) { 3294 yy = yAxis.java2DToValue(anchor.getY(), dataArea, yAxisEdge); 3295 } 3296 else { 3297 yy = yAxis.java2DToValue(anchor.getX(), dataArea, yAxisEdge); 3298 } 3299 crosshairState.setCrosshairY(yy); 3300 } 3301 setRangeCrosshairValue(crosshairState.getCrosshairY(), false); 3302 if (isRangeCrosshairVisible()) { 3303 double y = getRangeCrosshairValue(); 3304 Paint paint = getRangeCrosshairPaint(); 3305 Stroke stroke = getRangeCrosshairStroke(); 3306 drawRangeCrosshair(g2, dataArea, getOrientation(), y, yAxis, stroke, paint); 3307 } 3308 3309 // draw an outline around the plot area... 3310 if (isOutlineVisible()) { 3311 if (getRenderer() != null) { 3312 getRenderer().drawOutline(g2, this, dataArea); 3313 } 3314 else { 3315 drawOutline(g2, dataArea); 3316 } 3317 } 3318 3319 } 3320 3321 /** 3322 * Draws the plot background (the background color and/or image). 3323 * <P> 3324 * This method will be called during the chart drawing process and is 3325 * declared public so that it can be accessed by the renderers used by 3326 * certain subclasses. You shouldn't need to call this method directly. 3327 * 3328 * @param g2 the graphics device. 3329 * @param area the area within which the plot should be drawn. 3330 */ 3331 public void drawBackground(Graphics2D g2, Rectangle2D area) { 3332 fillBackground(g2, area, this.orientation); 3333 drawBackgroundImage(g2, area); 3334 } 3335 3336 /** 3337 * A utility method for drawing the plot's axes. 3338 * 3339 * @param g2 the graphics device. 3340 * @param plotArea the plot area. 3341 * @param dataArea the data area. 3342 * @param plotState collects information about the plot (<code>null</code> 3343 * permitted). 3344 * 3345 * @return A map containing the axis states. 3346 */ 3347 protected Map drawAxes(Graphics2D g2, 3348 Rectangle2D plotArea, 3349 Rectangle2D dataArea, 3350 PlotRenderingInfo plotState) { 3351 3352 AxisCollection axisCollection = new AxisCollection(); 3353 3354 // add domain axes to lists... 3355 for (int index = 0; index < this.domainAxes.size(); index++) { 3356 CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(index); 3357 if (xAxis != null) { 3358 axisCollection.add(xAxis, getDomainAxisEdge(index)); 3359 } 3360 } 3361 3362 // add range axes to lists... 3363 for (int index = 0; index < this.rangeAxes.size(); index++) { 3364 ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(index); 3365 if (yAxis != null) { 3366 axisCollection.add(yAxis, getRangeAxisEdge(index)); 3367 } 3368 } 3369 3370 Map axisStateMap = new HashMap(); 3371 3372 // draw the top axes 3373 double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset( 3374 dataArea.getHeight()); 3375 Iterator iterator = axisCollection.getAxesAtTop().iterator(); 3376 while (iterator.hasNext()) { 3377 Axis axis = (Axis) iterator.next(); 3378 if (axis != null) { 3379 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, 3380 RectangleEdge.TOP, plotState); 3381 cursor = axisState.getCursor(); 3382 axisStateMap.put(axis, axisState); 3383 } 3384 } 3385 3386 // draw the bottom axes 3387 cursor = dataArea.getMaxY() 3388 + this.axisOffset.calculateBottomOutset(dataArea.getHeight()); 3389 iterator = axisCollection.getAxesAtBottom().iterator(); 3390 while (iterator.hasNext()) { 3391 Axis axis = (Axis) iterator.next(); 3392 if (axis != null) { 3393 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, 3394 RectangleEdge.BOTTOM, plotState); 3395 cursor = axisState.getCursor(); 3396 axisStateMap.put(axis, axisState); 3397 } 3398 } 3399 3400 // draw the left axes 3401 cursor = dataArea.getMinX() 3402 - this.axisOffset.calculateLeftOutset(dataArea.getWidth()); 3403 iterator = axisCollection.getAxesAtLeft().iterator(); 3404 while (iterator.hasNext()) { 3405 Axis axis = (Axis) iterator.next(); 3406 if (axis != null) { 3407 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, 3408 RectangleEdge.LEFT, plotState); 3409 cursor = axisState.getCursor(); 3410 axisStateMap.put(axis, axisState); 3411 } 3412 } 3413 3414 // draw the right axes 3415 cursor = dataArea.getMaxX() 3416 + this.axisOffset.calculateRightOutset(dataArea.getWidth()); 3417 iterator = axisCollection.getAxesAtRight().iterator(); 3418 while (iterator.hasNext()) { 3419 Axis axis = (Axis) iterator.next(); 3420 if (axis != null) { 3421 AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, 3422 RectangleEdge.RIGHT, plotState); 3423 cursor = axisState.getCursor(); 3424 axisStateMap.put(axis, axisState); 3425 } 3426 } 3427 3428 return axisStateMap; 3429 3430 } 3431 3432 /** 3433 * Draws a representation of a dataset within the dataArea region using the 3434 * appropriate renderer. 3435 * 3436 * @param g2 the graphics device. 3437 * @param dataArea the region in which the data is to be drawn. 3438 * @param index the dataset and renderer index. 3439 * @param info an optional object for collection dimension information. 3440 * @param crosshairState a state object for tracking crosshair info 3441 * (<code>null</code> permitted). 3442 * 3443 * @return A boolean that indicates whether or not real data was found. 3444 * 3445 * @since 1.0.11 3446 */ 3447 public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, 3448 PlotRenderingInfo info, CategoryCrosshairState crosshairState) { 3449 3450 boolean foundData = false; 3451 CategoryDataset currentDataset = getDataset(index); 3452 CategoryItemRenderer renderer = getRenderer(index); 3453 CategoryAxis domainAxis = getDomainAxisForDataset(index); 3454 ValueAxis rangeAxis = getRangeAxisForDataset(index); 3455 boolean hasData = !DatasetUtilities.isEmptyOrNull(currentDataset); 3456 if (hasData && renderer != null) { 3457 3458 foundData = true; 3459 CategoryItemRendererState state = renderer.initialise(g2, dataArea, 3460 this, index, info); 3461 state.setCrosshairState(crosshairState); 3462 int columnCount = currentDataset.getColumnCount(); 3463 int rowCount = currentDataset.getRowCount(); 3464 int passCount = renderer.getPassCount(); 3465 for (int pass = 0; pass < passCount; pass++) { 3466 if (this.columnRenderingOrder == SortOrder.ASCENDING) { 3467 for (int column = 0; column < columnCount; column++) { 3468 if (this.rowRenderingOrder == SortOrder.ASCENDING) { 3469 for (int row = 0; row < rowCount; row++) { 3470 renderer.drawItem(g2, state, dataArea, this, 3471 domainAxis, rangeAxis, currentDataset, 3472 row, column, pass); 3473 } 3474 } 3475 else { 3476 for (int row = rowCount - 1; row >= 0; row--) { 3477 renderer.drawItem(g2, state, dataArea, this, 3478 domainAxis, rangeAxis, currentDataset, 3479 row, column, pass); 3480 } 3481 } 3482 } 3483 } 3484 else { 3485 for (int column = columnCount - 1; column >= 0; column--) { 3486 if (this.rowRenderingOrder == SortOrder.ASCENDING) { 3487 for (int row = 0; row < rowCount; row++) { 3488 renderer.drawItem(g2, state, dataArea, this, 3489 domainAxis, rangeAxis, currentDataset, 3490 row, column, pass); 3491 } 3492 } 3493 else { 3494 for (int row = rowCount - 1; row >= 0; row--) { 3495 renderer.drawItem(g2, state, dataArea, this, 3496 domainAxis, rangeAxis, currentDataset, 3497 row, column, pass); 3498 } 3499 } 3500 } 3501 } 3502 } 3503 } 3504 return foundData; 3505 3506 } 3507 3508 /** 3509 * Draws the gridlines for the plot. 3510 * 3511 * @param g2 the graphics device. 3512 * @param dataArea the area inside the axes. 3513 * 3514 * @see #drawRangeGridlines(Graphics2D, Rectangle2D, List) 3515 */ 3516 protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea) { 3517 3518 // draw the domain grid lines, if any... 3519 if (isDomainGridlinesVisible()) { 3520 CategoryAnchor anchor = getDomainGridlinePosition(); 3521 RectangleEdge domainAxisEdge = getDomainAxisEdge(); 3522 Stroke gridStroke = getDomainGridlineStroke(); 3523 Paint gridPaint = getDomainGridlinePaint(); 3524 if ((gridStroke != null) && (gridPaint != null)) { 3525 // iterate over the categories 3526 CategoryDataset data = getDataset(); 3527 if (data != null) { 3528 CategoryAxis axis = getDomainAxis(); 3529 if (axis != null) { 3530 int columnCount = data.getColumnCount(); 3531 for (int c = 0; c < columnCount; c++) { 3532 double xx = axis.getCategoryJava2DCoordinate( 3533 anchor, c, columnCount, dataArea, 3534 domainAxisEdge); 3535 CategoryItemRenderer renderer1 = getRenderer(); 3536 if (renderer1 != null) { 3537 renderer1.drawDomainGridline(g2, this, 3538 dataArea, xx); 3539 } 3540 } 3541 } 3542 } 3543 } 3544 } 3545 } 3546 3547 /** 3548 * Draws the gridlines for the plot. 3549 * 3550 * @param g2 the graphics device. 3551 * @param dataArea the area inside the axes. 3552 * @param ticks the ticks. 3553 * 3554 * @see #drawDomainGridlines(Graphics2D, Rectangle2D) 3555 */ 3556 protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea, 3557 List ticks) { 3558 // draw the range grid lines, if any... 3559 if (isRangeGridlinesVisible()) { 3560 Stroke gridStroke = getRangeGridlineStroke(); 3561 Paint gridPaint = getRangeGridlinePaint(); 3562 if ((gridStroke != null) && (gridPaint != null)) { 3563 ValueAxis axis = getRangeAxis(); 3564 if (axis != null) { 3565 Iterator iterator = ticks.iterator(); 3566 while (iterator.hasNext()) { 3567 ValueTick tick = (ValueTick) iterator.next(); 3568 CategoryItemRenderer renderer1 = getRenderer(); 3569 if (renderer1 != null) { 3570 renderer1.drawRangeGridline(g2, this, 3571 getRangeAxis(), dataArea, tick.getValue()); 3572 } 3573 } 3574 } 3575 } 3576 } 3577 } 3578 3579 /** 3580 * Draws the annotations. 3581 * 3582 * @param g2 the graphics device. 3583 * @param dataArea the data area. 3584 */ 3585 protected void drawAnnotations(Graphics2D g2, Rectangle2D dataArea) { 3586 3587 if (getAnnotations() != null) { 3588 Iterator iterator = getAnnotations().iterator(); 3589 while (iterator.hasNext()) { 3590 CategoryAnnotation annotation 3591 = (CategoryAnnotation) iterator.next(); 3592 annotation.draw(g2, this, dataArea, getDomainAxis(), 3593 getRangeAxis()); 3594 } 3595 } 3596 3597 } 3598 3599 /** 3600 * Draws the domain markers (if any) for an axis and layer. This method is 3601 * typically called from within the draw() method. 3602 * 3603 * @param g2 the graphics device. 3604 * @param dataArea the data area. 3605 * @param index the renderer index. 3606 * @param layer the layer (foreground or background). 3607 * 3608 * @see #drawRangeMarkers(Graphics2D, Rectangle2D, int, Layer) 3609 */ 3610 protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea, 3611 int index, Layer layer) { 3612 3613 CategoryItemRenderer r = getRenderer(index); 3614 if (r == null) { 3615 return; 3616 } 3617 3618 Collection markers = getDomainMarkers(index, layer); 3619 CategoryAxis axis = getDomainAxisForDataset(index); 3620 if (markers != null && axis != null) { 3621 Iterator iterator = markers.iterator(); 3622 while (iterator.hasNext()) { 3623 CategoryMarker marker = (CategoryMarker) iterator.next(); 3624 r.drawDomainMarker(g2, this, axis, marker, dataArea); 3625 } 3626 } 3627 3628 } 3629 3630 /** 3631 * Draws the range markers (if any) for an axis and layer. This method is 3632 * typically called from within the draw() method. 3633 * 3634 * @param g2 the graphics device. 3635 * @param dataArea the data area. 3636 * @param index the renderer index. 3637 * @param layer the layer (foreground or background). 3638 * 3639 * @see #drawDomainMarkers(Graphics2D, Rectangle2D, int, Layer) 3640 */ 3641 protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea, 3642 int index, Layer layer) { 3643 3644 CategoryItemRenderer r = getRenderer(index); 3645 if (r == null) { 3646 return; 3647 } 3648 3649 Collection markers = getRangeMarkers(index, layer); 3650 ValueAxis axis = getRangeAxisForDataset(index); 3651 if (markers != null && axis != null) { 3652 Iterator iterator = markers.iterator(); 3653 while (iterator.hasNext()) { 3654 Marker marker = (Marker) iterator.next(); 3655 r.drawRangeMarker(g2, this, axis, marker, dataArea); 3656 } 3657 } 3658 3659 } 3660 3661 /** 3662 * Utility method for drawing a line perpendicular to the range axis (used 3663 * for crosshairs). 3664 * 3665 * @param g2 the graphics device. 3666 * @param dataArea the area defined by the axes. 3667 * @param value the data value. 3668 * @param stroke the line stroke (<code>null</code> not permitted). 3669 * @param paint the line paint (<code>null</code> not permitted). 3670 */ 3671 protected void drawRangeLine(Graphics2D g2, Rectangle2D dataArea, 3672 double value, Stroke stroke, Paint paint) { 3673 3674 double java2D = getRangeAxis().valueToJava2D(value, dataArea, 3675 getRangeAxisEdge()); 3676 Line2D line = null; 3677 if (this.orientation == PlotOrientation.HORIZONTAL) { 3678 line = new Line2D.Double(java2D, dataArea.getMinY(), java2D, 3679 dataArea.getMaxY()); 3680 } 3681 else if (this.orientation == PlotOrientation.VERTICAL) { 3682 line = new Line2D.Double(dataArea.getMinX(), java2D, 3683 dataArea.getMaxX(), java2D); 3684 } 3685 g2.setStroke(stroke); 3686 g2.setPaint(paint); 3687 g2.draw(line); 3688 3689 } 3690 3691 /** 3692 * Draws a domain crosshair. 3693 * 3694 * @param g2 the graphics target. 3695 * @param dataArea the data area. 3696 * @param orientation the plot orientation. 3697 * @param datasetIndex the dataset index. 3698 * @param rowKey the row key. 3699 * @param columnKey the column key. 3700 * @param stroke the stroke used to draw the crosshair line. 3701 * @param paint the paint used to draw the crosshair line. 3702 * 3703 * @see #drawRangeCrosshair(Graphics2D, Rectangle2D, PlotOrientation, 3704 * double, ValueAxis, Stroke, Paint) 3705 * 3706 * @since 1.0.11 3707 */ 3708 protected void drawDomainCrosshair(Graphics2D g2, Rectangle2D dataArea, 3709 PlotOrientation orientation, int datasetIndex, 3710 Comparable rowKey, Comparable columnKey, Stroke stroke, 3711 Paint paint) { 3712 3713 CategoryDataset dataset = getDataset(datasetIndex); 3714 CategoryAxis axis = getDomainAxisForDataset(datasetIndex); 3715 CategoryItemRenderer renderer = getRenderer(datasetIndex); 3716 Line2D line = null; 3717 if (orientation == PlotOrientation.VERTICAL) { 3718 double xx = renderer.getItemMiddle(rowKey, columnKey, dataset, axis, 3719 dataArea, RectangleEdge.BOTTOM); 3720 line = new Line2D.Double(xx, dataArea.getMinY(), xx, 3721 dataArea.getMaxY()); 3722 } 3723 else { 3724 double yy = renderer.getItemMiddle(rowKey, columnKey, dataset, axis, 3725 dataArea, RectangleEdge.LEFT); 3726 line = new Line2D.Double(dataArea.getMinX(), yy, 3727 dataArea.getMaxX(), yy); 3728 } 3729 g2.setStroke(stroke); 3730 g2.setPaint(paint); 3731 g2.draw(line); 3732 3733 } 3734 3735 /** 3736 * Draws a range crosshair. 3737 * 3738 * @param g2 the graphics target. 3739 * @param dataArea the data area. 3740 * @param orientation the plot orientation. 3741 * @param value the crosshair value. 3742 * @param axis the axis against which the value is measured. 3743 * @param stroke the stroke used to draw the crosshair line. 3744 * @param paint the paint used to draw the crosshair line. 3745 * 3746 * @see #drawDomainCrosshair(Graphics2D, Rectangle2D, PlotOrientation, int, 3747 * Comparable, Comparable, Stroke, Paint) 3748 * 3749 * @since 1.0.5 3750 */ 3751 protected void drawRangeCrosshair(Graphics2D g2, Rectangle2D dataArea, 3752 PlotOrientation orientation, double value, ValueAxis axis, 3753 Stroke stroke, Paint paint) { 3754 3755 if (!axis.getRange().contains(value)) { 3756 return; 3757 } 3758 Line2D line = null; 3759 if (orientation == PlotOrientation.HORIZONTAL) { 3760 double xx = axis.valueToJava2D(value, dataArea, 3761 RectangleEdge.BOTTOM); 3762 line = new Line2D.Double(xx, dataArea.getMinY(), xx, 3763 dataArea.getMaxY()); 3764 } 3765 else { 3766 double yy = axis.valueToJava2D(value, dataArea, 3767 RectangleEdge.LEFT); 3768 line = new Line2D.Double(dataArea.getMinX(), yy, 3769 dataArea.getMaxX(), yy); 3770 } 3771 g2.setStroke(stroke); 3772 g2.setPaint(paint); 3773 g2.draw(line); 3774 3775 } 3776 3777 /** 3778 * Returns the range of data values that will be plotted against the range 3779 * axis. If the dataset is <code>null</code>, this method returns 3780 * <code>null</code>. 3781 * 3782 * @param axis the axis. 3783 * 3784 * @return The data range. 3785 */ 3786 public Range getDataRange(ValueAxis axis) { 3787 3788 Range result = null; 3789 List mappedDatasets = new ArrayList(); 3790 3791 int rangeIndex = this.rangeAxes.indexOf(axis); 3792 if (rangeIndex >= 0) { 3793 mappedDatasets.addAll(datasetsMappedToRangeAxis(rangeIndex)); 3794 } 3795 else if (axis == getRangeAxis()) { 3796 mappedDatasets.addAll(datasetsMappedToRangeAxis(0)); 3797 } 3798 3799 // iterate through the datasets that map to the axis and get the union 3800 // of the ranges. 3801 Iterator iterator = mappedDatasets.iterator(); 3802 while (iterator.hasNext()) { 3803 CategoryDataset d = (CategoryDataset) iterator.next(); 3804 CategoryItemRenderer r = getRendererForDataset(d); 3805 if (r != null) { 3806 result = Range.combine(result, r.findRangeBounds(d)); 3807 } 3808 } 3809 return result; 3810 3811 } 3812 3813 /** 3814 * Returns a list of the datasets that are mapped to the axis with the 3815 * specified index. 3816 * 3817 * @param axisIndex the axis index. 3818 * 3819 * @return The list (possibly empty, but never <code>null</code>). 3820 * 3821 * @since 1.0.3 3822 */ 3823 private List datasetsMappedToDomainAxis(int axisIndex) { 3824 List result = new ArrayList(); 3825 for (int datasetIndex = 0; datasetIndex < this.datasets.size(); 3826 datasetIndex++) { 3827 Object dataset = this.datasets.get(datasetIndex); 3828 if (dataset != null) { 3829 Integer m = (Integer) this.datasetToDomainAxisMap.get( 3830 datasetIndex); 3831 if (m == null) { // a dataset with no mapping is assigned to 3832 // axis 0 3833 if (axisIndex == 0) { 3834 result.add(dataset); 3835 } 3836 } 3837 else { 3838 if (m.intValue() == axisIndex) { 3839 result.add(dataset); 3840 } 3841 } 3842 } 3843 } 3844 return result; 3845 } 3846 3847 /** 3848 * A utility method that returns a list of datasets that are mapped to a 3849 * given range axis. 3850 * 3851 * @param index the axis index. 3852 * 3853 * @return A list of datasets. 3854 */ 3855 private List datasetsMappedToRangeAxis(int index) { 3856 List result = new ArrayList(); 3857 for (int i = 0; i < this.datasets.size(); i++) { 3858 Object dataset = this.datasets.get(i); 3859 if (dataset != null) { 3860 Integer m = (Integer) this.datasetToRangeAxisMap.get(i); 3861 if (m == null) { // a dataset with no mapping is assigned to 3862 // axis 0 3863 if (index == 0) { 3864 result.add(dataset); 3865 } 3866 } 3867 else { 3868 if (m.intValue() == index) { 3869 result.add(dataset); 3870 } 3871 } 3872 } 3873 } 3874 return result; 3875 } 3876 3877 /** 3878 * Returns the weight for this plot when it is used as a subplot within a 3879 * combined plot. 3880 * 3881 * @return The weight. 3882 * 3883 * @see #setWeight(int) 3884 */ 3885 public int getWeight() { 3886 return this.weight; 3887 } 3888 3889 /** 3890 * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all 3891 * registered listeners. 3892 * 3893 * @param weight the weight. 3894 * 3895 * @see #getWeight() 3896 */ 3897 public void setWeight(int weight) { 3898 this.weight = weight; 3899 fireChangeEvent(); 3900 } 3901 3902 /** 3903 * Returns the fixed domain axis space. 3904 * 3905 * @return The fixed domain axis space (possibly <code>null</code>). 3906 * 3907 * @see #setFixedDomainAxisSpace(AxisSpace) 3908 */ 3909 public AxisSpace getFixedDomainAxisSpace() { 3910 return this.fixedDomainAxisSpace; 3911 } 3912 3913 /** 3914 * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to 3915 * all registered listeners. 3916 * 3917 * @param space the space (<code>null</code> permitted). 3918 * 3919 * @see #getFixedDomainAxisSpace() 3920 */ 3921 public void setFixedDomainAxisSpace(AxisSpace space) { 3922 setFixedDomainAxisSpace(space, true); 3923 } 3924 3925 /** 3926 * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to 3927 * all registered listeners. 3928 * 3929 * @param space the space (<code>null</code> permitted). 3930 * @param notify notify listeners? 3931 * 3932 * @see #getFixedDomainAxisSpace() 3933 * 3934 * @since 1.0.7 3935 */ 3936 public void setFixedDomainAxisSpace(AxisSpace space, boolean notify) { 3937 this.fixedDomainAxisSpace = space; 3938 if (notify) { 3939 fireChangeEvent(); 3940 } 3941 } 3942 3943 /** 3944 * Returns the fixed range axis space. 3945 * 3946 * @return The fixed range axis space (possibly <code>null</code>). 3947 * 3948 * @see #setFixedRangeAxisSpace(AxisSpace) 3949 */ 3950 public AxisSpace getFixedRangeAxisSpace() { 3951 return this.fixedRangeAxisSpace; 3952 } 3953 3954 /** 3955 * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to 3956 * all registered listeners. 3957 * 3958 * @param space the space (<code>null</code> permitted). 3959 * 3960 * @see #getFixedRangeAxisSpace() 3961 */ 3962 public void setFixedRangeAxisSpace(AxisSpace space) { 3963 setFixedRangeAxisSpace(space, true); 3964 } 3965 3966 /** 3967 * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to 3968 * all registered listeners. 3969 * 3970 * @param space the space (<code>null</code> permitted). 3971 * @param notify notify listeners? 3972 * 3973 * @see #getFixedRangeAxisSpace() 3974 * 3975 * @since 1.0.7 3976 */ 3977 public void setFixedRangeAxisSpace(AxisSpace space, boolean notify) { 3978 this.fixedRangeAxisSpace = space; 3979 if (notify) { 3980 fireChangeEvent(); 3981 } 3982 } 3983 3984 /** 3985 * Returns a list of the categories in the plot's primary dataset. 3986 * 3987 * @return A list of the categories in the plot's primary dataset. 3988 * 3989 * @see #getCategoriesForAxis(CategoryAxis) 3990 */ 3991 public List getCategories() { 3992 List result = null; 3993 if (getDataset() != null) { 3994 result = Collections.unmodifiableList(getDataset().getColumnKeys()); 3995 } 3996 return result; 3997 } 3998 3999 /** 4000 * Returns a list of the categories that should be displayed for the 4001 * specified axis. 4002 * 4003 * @param axis the axis (<code>null</code> not permitted) 4004 * 4005 * @return The categories. 4006 * 4007 * @since 1.0.3 4008 */ 4009 public List getCategoriesForAxis(CategoryAxis axis) { 4010 List result = new ArrayList(); 4011 int axisIndex = this.domainAxes.indexOf(axis); 4012 List datasets = datasetsMappedToDomainAxis(axisIndex); 4013 Iterator iterator = datasets.iterator(); 4014 while (iterator.hasNext()) { 4015 CategoryDataset dataset = (CategoryDataset) iterator.next(); 4016 // add the unique categories from this dataset 4017 for (int i = 0; i < dataset.getColumnCount(); i++) { 4018 Comparable category = dataset.getColumnKey(i); 4019 if (!result.contains(category)) { 4020 result.add(category); 4021 } 4022 } 4023 } 4024 return result; 4025 } 4026 4027 /** 4028 * Returns the flag that controls whether or not the shared domain axis is 4029 * drawn for each subplot. 4030 * 4031 * @return A boolean. 4032 * 4033 * @see #setDrawSharedDomainAxis(boolean) 4034 */ 4035 public boolean getDrawSharedDomainAxis() { 4036 return this.drawSharedDomainAxis; 4037 } 4038 4039 /** 4040 * Sets the flag that controls whether the shared domain axis is drawn when 4041 * this plot is being used as a subplot. 4042 * 4043 * @param draw a boolean. 4044 * 4045 * @see #getDrawSharedDomainAxis() 4046 */ 4047 public void setDrawSharedDomainAxis(boolean draw) { 4048 this.drawSharedDomainAxis = draw; 4049 fireChangeEvent(); 4050 } 4051 4052 /** 4053 * Returns <code>false</code> to indicate that the domain axes are not 4054 * zoomable. 4055 * 4056 * @return A boolean. 4057 * 4058 * @see #isRangeZoomable() 4059 */ 4060 public boolean isDomainZoomable() { 4061 return false; 4062 } 4063 4064 /** 4065 * Returns <code>true</code> to indicate that the range axes are zoomable. 4066 * 4067 * @return A boolean. 4068 * 4069 * @see #isDomainZoomable() 4070 */ 4071 public boolean isRangeZoomable() { 4072 return true; 4073 } 4074 4075 /** 4076 * This method does nothing, because <code>CategoryPlot</code> doesn't 4077 * support zooming on the domain. 4078 * 4079 * @param factor the zoom factor. 4080 * @param state the plot state. 4081 * @param source the source point (in Java2D space) for the zoom. 4082 */ 4083 public void zoomDomainAxes(double factor, PlotRenderingInfo state, 4084 Point2D source) { 4085 // can't zoom domain axis 4086 } 4087 4088 /** 4089 * This method does nothing, because <code>CategoryPlot</code> doesn't 4090 * support zooming on the domain. 4091 * 4092 * @param lowerPercent the lower bound. 4093 * @param upperPercent the upper bound. 4094 * @param state the plot state. 4095 * @param source the source point (in Java2D space) for the zoom. 4096 */ 4097 public void zoomDomainAxes(double lowerPercent, double upperPercent, 4098 PlotRenderingInfo state, Point2D source) { 4099 // can't zoom domain axis 4100 } 4101 4102 /** 4103 * This method does nothing, because <code>CategoryPlot</code> doesn't 4104 * support zooming on the domain. 4105 * 4106 * @param factor the zoom factor. 4107 * @param info the plot rendering info. 4108 * @param source the source point (in Java2D space). 4109 * @param useAnchor use source point as zoom anchor? 4110 * 4111 * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean) 4112 * 4113 * @since 1.0.7 4114 */ 4115 public void zoomDomainAxes(double factor, PlotRenderingInfo info, 4116 Point2D source, boolean useAnchor) { 4117 // can't zoom domain axis 4118 } 4119 4120 /** 4121 * Multiplies the range on the range axis/axes by the specified factor. 4122 * 4123 * @param factor the zoom factor. 4124 * @param state the plot state. 4125 * @param source the source point (in Java2D space) for the zoom. 4126 */ 4127 public void zoomRangeAxes(double factor, PlotRenderingInfo state, 4128 Point2D source) { 4129 // delegate to other method 4130 zoomRangeAxes(factor, state, source, false); 4131 } 4132 4133 /** 4134 * Multiplies the range on the range axis/axes by the specified factor. 4135 * 4136 * @param factor the zoom factor. 4137 * @param info the plot rendering info. 4138 * @param source the source point. 4139 * @param useAnchor a flag that controls whether or not the source point 4140 * is used for the zoom anchor. 4141 * 4142 * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) 4143 * 4144 * @since 1.0.7 4145 */ 4146 public void zoomRangeAxes(double factor, PlotRenderingInfo info, 4147 Point2D source, boolean useAnchor) { 4148 4149 // perform the zoom on each range axis 4150 for (int i = 0; i < this.rangeAxes.size(); i++) { 4151 ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i); 4152 if (rangeAxis != null) { 4153 if (useAnchor) { 4154 // get the relevant source coordinate given the plot 4155 // orientation 4156 double sourceY = source.getY(); 4157 if (this.orientation == PlotOrientation.HORIZONTAL) { 4158 sourceY = source.getX(); 4159 } 4160 double anchorY = rangeAxis.java2DToValue(sourceY, 4161 info.getDataArea(), getRangeAxisEdge()); 4162 rangeAxis.resizeRange(factor, anchorY); 4163 } 4164 else { 4165 rangeAxis.resizeRange(factor); 4166 } 4167 } 4168 } 4169 } 4170 4171 /** 4172 * Zooms in on the range axes. 4173 * 4174 * @param lowerPercent the lower bound. 4175 * @param upperPercent the upper bound. 4176 * @param state the plot state. 4177 * @param source the source point (in Java2D space) for the zoom. 4178 */ 4179 public void zoomRangeAxes(double lowerPercent, double upperPercent, 4180 PlotRenderingInfo state, Point2D source) { 4181 for (int i = 0; i < this.rangeAxes.size(); i++) { 4182 ValueAxis rangeAxis = (ValueAxis) this.rangeAxes.get(i); 4183 if (rangeAxis != null) { 4184 rangeAxis.zoomRange(lowerPercent, upperPercent); 4185 } 4186 } 4187 } 4188 4189 /** 4190 * Returns the anchor value. 4191 * 4192 * @return The anchor value. 4193 * 4194 * @see #setAnchorValue(double) 4195 */ 4196 public double getAnchorValue() { 4197 return this.anchorValue; 4198 } 4199 4200 /** 4201 * Sets the anchor value and sends a {@link PlotChangeEvent} to all 4202 * registered listeners. 4203 * 4204 * @param value the anchor value. 4205 * 4206 * @see #getAnchorValue() 4207 */ 4208 public void setAnchorValue(double value) { 4209 setAnchorValue(value, true); 4210 } 4211 4212 /** 4213 * Sets the anchor value and, if requested, sends a {@link PlotChangeEvent} 4214 * to all registered listeners. 4215 * 4216 * @param value the value. 4217 * @param notify notify listeners? 4218 * 4219 * @see #getAnchorValue() 4220 */ 4221 public void setAnchorValue(double value, boolean notify) { 4222 this.anchorValue = value; 4223 if (notify) { 4224 fireChangeEvent(); 4225 } 4226 } 4227 4228 /** 4229 * Tests the plot for equality with an arbitrary object. 4230 * 4231 * @param obj the object to test against (<code>null</code> permitted). 4232 * 4233 * @return A boolean. 4234 */ 4235 public boolean equals(Object obj) { 4236 4237 if (obj == this) { 4238 return true; 4239 } 4240 if (!(obj instanceof CategoryPlot)) { 4241 return false; 4242 } 4243 CategoryPlot that = (CategoryPlot) obj; 4244 if (this.orientation != that.orientation) { 4245 return false; 4246 } 4247 if (!ObjectUtilities.equal(this.axisOffset, that.axisOffset)) { 4248 return false; 4249 } 4250 if (!this.domainAxes.equals(that.domainAxes)) { 4251 return false; 4252 } 4253 if (!this.domainAxisLocations.equals(that.domainAxisLocations)) { 4254 return false; 4255 } 4256 if (this.drawSharedDomainAxis != that.drawSharedDomainAxis) { 4257 return false; 4258 } 4259 if (!this.rangeAxes.equals(that.rangeAxes)) { 4260 return false; 4261 } 4262 if (!this.rangeAxisLocations.equals(that.rangeAxisLocations)) { 4263 return false; 4264 } 4265 if (!ObjectUtilities.equal(this.datasetToDomainAxisMap, 4266 that.datasetToDomainAxisMap)) { 4267 return false; 4268 } 4269 if (!ObjectUtilities.equal(this.datasetToRangeAxisMap, 4270 that.datasetToRangeAxisMap)) { 4271 return false; 4272 } 4273 if (!ObjectUtilities.equal(this.renderers, that.renderers)) { 4274 return false; 4275 } 4276 if (this.renderingOrder != that.renderingOrder) { 4277 return false; 4278 } 4279 if (this.columnRenderingOrder != that.columnRenderingOrder) { 4280 return false; 4281 } 4282 if (this.rowRenderingOrder != that.rowRenderingOrder) { 4283 return false; 4284 } 4285 if (this.domainGridlinesVisible != that.domainGridlinesVisible) { 4286 return false; 4287 } 4288 if (this.domainGridlinePosition != that.domainGridlinePosition) { 4289 return false; 4290 } 4291 if (!ObjectUtilities.equal(this.domainGridlineStroke, 4292 that.domainGridlineStroke)) { 4293 return false; 4294 } 4295 if (!PaintUtilities.equal(this.domainGridlinePaint, 4296 that.domainGridlinePaint)) { 4297 return false; 4298 } 4299 if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) { 4300 return false; 4301 } 4302 if (!ObjectUtilities.equal(this.rangeGridlineStroke, 4303 that.rangeGridlineStroke)) { 4304 return false; 4305 } 4306 if (!PaintUtilities.equal(this.rangeGridlinePaint, 4307 that.rangeGridlinePaint)) { 4308 return false; 4309 } 4310 if (this.anchorValue != that.anchorValue) { 4311 return false; 4312 } 4313 if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) { 4314 return false; 4315 } 4316 if (this.rangeCrosshairValue != that.rangeCrosshairValue) { 4317 return false; 4318 } 4319 if (!ObjectUtilities.equal(this.rangeCrosshairStroke, 4320 that.rangeCrosshairStroke)) { 4321 return false; 4322 } 4323 if (!PaintUtilities.equal(this.rangeCrosshairPaint, 4324 that.rangeCrosshairPaint)) { 4325 return false; 4326 } 4327 if (this.rangeCrosshairLockedOnData 4328 != that.rangeCrosshairLockedOnData) { 4329 return false; 4330 } 4331 if (!ObjectUtilities.equal(this.foregroundDomainMarkers, 4332 that.foregroundDomainMarkers)) { 4333 return false; 4334 } 4335 if (!ObjectUtilities.equal(this.backgroundDomainMarkers, 4336 that.backgroundDomainMarkers)) { 4337 return false; 4338 } 4339 if (!ObjectUtilities.equal(this.foregroundRangeMarkers, 4340 that.foregroundRangeMarkers)) { 4341 return false; 4342 } 4343 if (!ObjectUtilities.equal(this.backgroundRangeMarkers, 4344 that.backgroundRangeMarkers)) { 4345 return false; 4346 } 4347 if (!ObjectUtilities.equal(this.annotations, that.annotations)) { 4348 return false; 4349 } 4350 if (this.weight != that.weight) { 4351 return false; 4352 } 4353 if (!ObjectUtilities.equal(this.fixedDomainAxisSpace, 4354 that.fixedDomainAxisSpace)) { 4355 return false; 4356 } 4357 if (!ObjectUtilities.equal(this.fixedRangeAxisSpace, 4358 that.fixedRangeAxisSpace)) { 4359 return false; 4360 } 4361 if (!ObjectUtilities.equal(this.fixedLegendItems, 4362 that.fixedLegendItems)) { 4363 return false; 4364 } 4365 if (this.domainCrosshairVisible != that.domainCrosshairVisible) { 4366 return false; 4367 } 4368 if (this.crosshairDatasetIndex != that.crosshairDatasetIndex) { 4369 return false; 4370 } 4371 if (!ObjectUtilities.equal(this.domainCrosshairColumnKey, 4372 that.domainCrosshairColumnKey)) { 4373 return false; 4374 } 4375 if (!ObjectUtilities.equal(this.domainCrosshairRowKey, 4376 that.domainCrosshairRowKey)) { 4377 return false; 4378 } 4379 if (!PaintUtilities.equal(this.domainCrosshairPaint, 4380 that.domainCrosshairPaint)) { 4381 return false; 4382 } 4383 if (!ObjectUtilities.equal(this.domainCrosshairStroke, 4384 that.domainCrosshairStroke)) { 4385 return false; 4386 } 4387 return super.equals(obj); 4388 4389 } 4390 4391 /** 4392 * Returns a clone of the plot. 4393 * 4394 * @return A clone. 4395 * 4396 * @throws CloneNotSupportedException if the cloning is not supported. 4397 */ 4398 public Object clone() throws CloneNotSupportedException { 4399 4400 CategoryPlot clone = (CategoryPlot) super.clone(); 4401 4402 clone.domainAxes = new ObjectList(); 4403 for (int i = 0; i < this.domainAxes.size(); i++) { 4404 CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(i); 4405 if (xAxis != null) { 4406 CategoryAxis clonedAxis = (CategoryAxis) xAxis.clone(); 4407 clone.setDomainAxis(i, clonedAxis); 4408 } 4409 } 4410 clone.domainAxisLocations 4411 = (ObjectList) this.domainAxisLocations.clone(); 4412 4413 clone.rangeAxes = new ObjectList(); 4414 for (int i = 0; i < this.rangeAxes.size(); i++) { 4415 ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(i); 4416 if (yAxis != null) { 4417 ValueAxis clonedAxis = (ValueAxis) yAxis.clone(); 4418 clone.setRangeAxis(i, clonedAxis); 4419 } 4420 } 4421 clone.rangeAxisLocations = (ObjectList) this.rangeAxisLocations.clone(); 4422 4423 clone.datasets = (ObjectList) this.datasets.clone(); 4424 for (int i = 0; i < clone.datasets.size(); i++) { 4425 CategoryDataset dataset = clone.getDataset(i); 4426 if (dataset != null) { 4427 dataset.addChangeListener(clone); 4428 } 4429 } 4430 clone.datasetToDomainAxisMap 4431 = (ObjectList) this.datasetToDomainAxisMap.clone(); 4432 clone.datasetToRangeAxisMap 4433 = (ObjectList) this.datasetToRangeAxisMap.clone(); 4434 clone.renderers = (ObjectList) this.renderers.clone(); 4435 if (this.fixedDomainAxisSpace != null) { 4436 clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities.clone( 4437 this.fixedDomainAxisSpace); 4438 } 4439 if (this.fixedRangeAxisSpace != null) { 4440 clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities.clone( 4441 this.fixedRangeAxisSpace); 4442 } 4443 4444 clone.annotations = (List) ObjectUtilities.deepClone(this.annotations); 4445 clone.foregroundDomainMarkers = cloneMarkerMap( 4446 this.foregroundDomainMarkers); 4447 clone.backgroundDomainMarkers = cloneMarkerMap( 4448 this.backgroundDomainMarkers); 4449 clone.foregroundRangeMarkers = cloneMarkerMap( 4450 this.foregroundRangeMarkers); 4451 clone.backgroundRangeMarkers = cloneMarkerMap( 4452 this.backgroundRangeMarkers); 4453 if (this.fixedLegendItems != null) { 4454 clone.fixedLegendItems 4455 = (LegendItemCollection) this.fixedLegendItems.clone(); 4456 } 4457 return clone; 4458 4459 } 4460 4461 /** 4462 * A utility method to clone the marker maps. 4463 * 4464 * @param map the map to clone. 4465 * 4466 * @return A clone of the map. 4467 * 4468 * @throws CloneNotSupportedException if there is some problem cloning the 4469 * map. 4470 */ 4471 private Map cloneMarkerMap(Map map) throws CloneNotSupportedException { 4472 Map clone = new HashMap(); 4473 Set keys = map.keySet(); 4474 Iterator iterator = keys.iterator(); 4475 while (iterator.hasNext()) { 4476 Object key = iterator.next(); 4477 List entry = (List) map.get(key); 4478 Object toAdd = ObjectUtilities.deepClone(entry); 4479 clone.put(key, toAdd); 4480 } 4481 return clone; 4482 } 4483 4484 /** 4485 * Provides serialization support. 4486 * 4487 * @param stream the output stream. 4488 * 4489 * @throws IOException if there is an I/O error. 4490 */ 4491 private void writeObject(ObjectOutputStream stream) throws IOException { 4492 stream.defaultWriteObject(); 4493 SerialUtilities.writeStroke(this.domainGridlineStroke, stream); 4494 SerialUtilities.writePaint(this.domainGridlinePaint, stream); 4495 SerialUtilities.writeStroke(this.rangeGridlineStroke, stream); 4496 SerialUtilities.writePaint(this.rangeGridlinePaint, stream); 4497 SerialUtilities.writeStroke(this.rangeCrosshairStroke, stream); 4498 SerialUtilities.writePaint(this.rangeCrosshairPaint, stream); 4499 SerialUtilities.writeStroke(this.domainCrosshairStroke, stream); 4500 SerialUtilities.writePaint(this.domainCrosshairPaint, stream); 4501 } 4502 4503 /** 4504 * Provides serialization support. 4505 * 4506 * @param stream the input stream. 4507 * 4508 * @throws IOException if there is an I/O error. 4509 * @throws ClassNotFoundException if there is a classpath problem. 4510 */ 4511 private void readObject(ObjectInputStream stream) 4512 throws IOException, ClassNotFoundException { 4513 4514 stream.defaultReadObject(); 4515 this.domainGridlineStroke = SerialUtilities.readStroke(stream); 4516 this.domainGridlinePaint = SerialUtilities.readPaint(stream); 4517 this.rangeGridlineStroke = SerialUtilities.readStroke(stream); 4518 this.rangeGridlinePaint = SerialUtilities.readPaint(stream); 4519 this.rangeCrosshairStroke = SerialUtilities.readStroke(stream); 4520 this.rangeCrosshairPaint = SerialUtilities.readPaint(stream); 4521 this.domainCrosshairStroke = SerialUtilities.readStroke(stream); 4522 this.domainCrosshairPaint = SerialUtilities.readPaint(stream); 4523 4524 for (int i = 0; i < this.domainAxes.size(); i++) { 4525 CategoryAxis xAxis = (CategoryAxis) this.domainAxes.get(i); 4526 if (xAxis != null) { 4527 xAxis.setPlot(this); 4528 xAxis.addChangeListener(this); 4529 } 4530 } 4531 for (int i = 0; i < this.rangeAxes.size(); i++) { 4532 ValueAxis yAxis = (ValueAxis) this.rangeAxes.get(i); 4533 if (yAxis != null) { 4534 yAxis.setPlot(this); 4535 yAxis.addChangeListener(this); 4536 } 4537 } 4538 int datasetCount = this.datasets.size(); 4539 for (int i = 0; i < datasetCount; i++) { 4540 Dataset dataset = (Dataset) this.datasets.get(i); 4541 if (dataset != null) { 4542 dataset.addChangeListener(this); 4543 } 4544 } 4545 int rendererCount = this.renderers.size(); 4546 for (int i = 0; i < rendererCount; i++) { 4547 CategoryItemRenderer renderer 4548 = (CategoryItemRenderer) this.renderers.get(i); 4549 if (renderer != null) { 4550 renderer.addChangeListener(this); 4551 } 4552 } 4553 4554 } 4555 4556 }