001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2007, 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 * AbstractXYItemRenderer.java 029 * --------------------------- 030 * (C) Copyright 2002-2007, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Richard Atkinson; 034 * Focus Computer Services Limited; 035 * Tim Bardzil; 036 * Sergei Ivanov; 037 * 038 * $Id: AbstractXYItemRenderer.java,v 1.26.2.10 2007/02/06 16:29:11 mungady Exp $ 039 * 040 * Changes: 041 * -------- 042 * 15-Mar-2002 : Version 1 (DG); 043 * 09-Apr-2002 : Added a getToolTipGenerator() method reflecting the change in 044 * the XYItemRenderer interface (DG); 045 * 05-Aug-2002 : Added a urlGenerator member variable to support HTML image 046 * maps (RA); 047 * 20-Aug-2002 : Added property change events for the tooltip and URL 048 * generators (DG); 049 * 22-Aug-2002 : Moved property change support into AbstractRenderer class (DG); 050 * 23-Sep-2002 : Fixed errors reported by Checkstyle tool (DG); 051 * 18-Nov-2002 : Added methods for drawing grid lines (DG); 052 * 17-Jan-2003 : Moved plot classes into a separate package (DG); 053 * 25-Mar-2003 : Implemented Serializable (DG); 054 * 01-May-2003 : Modified initialise() return type and drawItem() method 055 * signature (DG); 056 * 15-May-2003 : Modified to take into account the plot orientation (DG); 057 * 21-May-2003 : Added labels to markers (DG); 058 * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer 059 * Services Ltd) (DG); 060 * 27-Jul-2003 : Added getRangeType() to support stacked XY area charts (RA); 061 * 31-Jul-2003 : Deprecated all but the default constructor (DG); 062 * 13-Aug-2003 : Implemented Cloneable (DG); 063 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 064 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 065 * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG); 066 * 11-Feb-2004 : Updated labelling for markers (DG); 067 * 25-Feb-2004 : Added updateCrosshairValues() method. Moved deprecated code 068 * to bottom of source file (DG); 069 * 16-Apr-2004 : Added support for IntervalMarker in drawRangeMarker() method 070 * - thanks to Tim Bardzil (DG); 071 * 05-May-2004 : Fixed bug (948310) where interval markers extend beyond axis 072 * range (DG); 073 * 03-Jun-2004 : Fixed more bugs in drawing interval markers (DG); 074 * 26-Aug-2004 : Added the addEntity() method (DG); 075 * 29-Sep-2004 : Added annotation support (with layers) (DG); 076 * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities --> 077 * TextUtilities (DG); 078 * 06-Oct-2004 : Added findDomainBounds() method and renamed 079 * getRangeExtent() --> findRangeBounds() (DG); 080 * 07-Jan-2005 : Removed deprecated code (DG); 081 * 27-Jan-2005 : Modified getLegendItem() to omit hidden series (DG); 082 * 24-Feb-2005 : Added getLegendItems() method (DG); 083 * 08-Mar-2005 : Fixed positioning of marker labels (DG); 084 * 20-Apr-2005 : Renamed XYLabelGenerator --> XYItemLabelGenerator and 085 * added generators for legend labels, tooltips and URLs (DG); 086 * 01-Jun-2005 : Handle one dimension of the marker label adjustment 087 * automatically (DG); 088 * ------------- JFREECHART 1.0.x --------------------------------------------- 089 * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG); 090 * 24-Oct-2006 : Respect alpha setting in markers (see patch 1567843 by Sergei 091 * Ivanov) (DG); 092 * 24-Oct-2006 : Added code to draw outlines for interval markers (DG); 093 * 24-Nov-2006 : Fixed cloning for legend item generators (DG); 094 * 06-Feb-2007 : Added new updateCrosshairValues() method that takes into 095 * account multiple axis plots (see bug 1086307) (DG); 096 * 097 */ 098 099 package org.jfree.chart.renderer.xy; 100 101 import java.awt.AlphaComposite; 102 import java.awt.Composite; 103 import java.awt.Font; 104 import java.awt.GradientPaint; 105 import java.awt.Graphics2D; 106 import java.awt.Paint; 107 import java.awt.Shape; 108 import java.awt.Stroke; 109 import java.awt.geom.Ellipse2D; 110 import java.awt.geom.Line2D; 111 import java.awt.geom.Point2D; 112 import java.awt.geom.Rectangle2D; 113 import java.io.Serializable; 114 import java.util.Iterator; 115 import java.util.List; 116 117 import org.jfree.chart.LegendItem; 118 import org.jfree.chart.LegendItemCollection; 119 import org.jfree.chart.annotations.XYAnnotation; 120 import org.jfree.chart.axis.ValueAxis; 121 import org.jfree.chart.entity.EntityCollection; 122 import org.jfree.chart.entity.XYItemEntity; 123 import org.jfree.chart.event.RendererChangeEvent; 124 import org.jfree.chart.labels.ItemLabelPosition; 125 import org.jfree.chart.labels.StandardXYSeriesLabelGenerator; 126 import org.jfree.chart.labels.XYItemLabelGenerator; 127 import org.jfree.chart.labels.XYSeriesLabelGenerator; 128 import org.jfree.chart.labels.XYToolTipGenerator; 129 import org.jfree.chart.plot.CrosshairState; 130 import org.jfree.chart.plot.DrawingSupplier; 131 import org.jfree.chart.plot.IntervalMarker; 132 import org.jfree.chart.plot.Marker; 133 import org.jfree.chart.plot.Plot; 134 import org.jfree.chart.plot.PlotOrientation; 135 import org.jfree.chart.plot.PlotRenderingInfo; 136 import org.jfree.chart.plot.ValueMarker; 137 import org.jfree.chart.plot.XYPlot; 138 import org.jfree.chart.renderer.AbstractRenderer; 139 import org.jfree.chart.urls.XYURLGenerator; 140 import org.jfree.data.Range; 141 import org.jfree.data.general.DatasetUtilities; 142 import org.jfree.data.xy.XYDataset; 143 import org.jfree.text.TextUtilities; 144 import org.jfree.ui.GradientPaintTransformer; 145 import org.jfree.ui.Layer; 146 import org.jfree.ui.LengthAdjustmentType; 147 import org.jfree.ui.RectangleAnchor; 148 import org.jfree.ui.RectangleInsets; 149 import org.jfree.util.ObjectList; 150 import org.jfree.util.ObjectUtilities; 151 import org.jfree.util.PublicCloneable; 152 153 /** 154 * A base class that can be used to create new {@link XYItemRenderer} 155 * implementations. 156 */ 157 public abstract class AbstractXYItemRenderer extends AbstractRenderer 158 implements XYItemRenderer, 159 Cloneable, 160 Serializable { 161 162 /** For serialization. */ 163 private static final long serialVersionUID = 8019124836026607990L; 164 165 /** The plot. */ 166 private XYPlot plot; 167 168 /** The item label generator for ALL series. */ 169 private XYItemLabelGenerator itemLabelGenerator; 170 171 /** A list of item label generators (one per series). */ 172 private ObjectList itemLabelGeneratorList; 173 174 /** The base item label generator. */ 175 private XYItemLabelGenerator baseItemLabelGenerator; 176 177 /** The tool tip generator for ALL series. */ 178 private XYToolTipGenerator toolTipGenerator; 179 180 /** A list of tool tip generators (one per series). */ 181 private ObjectList toolTipGeneratorList; 182 183 /** The base tool tip generator. */ 184 private XYToolTipGenerator baseToolTipGenerator; 185 186 /** The URL text generator. */ 187 private XYURLGenerator urlGenerator; 188 189 /** 190 * Annotations to be drawn in the background layer ('underneath' the data 191 * items). 192 */ 193 private List backgroundAnnotations; 194 195 /** 196 * Annotations to be drawn in the foreground layer ('on top' of the data 197 * items). 198 */ 199 private List foregroundAnnotations; 200 201 private int defaultEntityRadius; 202 203 private XYSeriesLabelGenerator legendItemLabelGenerator; 204 205 private XYSeriesLabelGenerator legendItemToolTipGenerator; 206 207 private XYSeriesLabelGenerator legendItemURLGenerator; 208 209 /** 210 * Creates a renderer where the tooltip generator and the URL generator are 211 * both <code>null</code>. 212 */ 213 protected AbstractXYItemRenderer() { 214 this.itemLabelGenerator = null; 215 this.itemLabelGeneratorList = new ObjectList(); 216 this.toolTipGenerator = null; 217 this.toolTipGeneratorList = new ObjectList(); 218 this.urlGenerator = null; 219 this.backgroundAnnotations = new java.util.ArrayList(); 220 this.foregroundAnnotations = new java.util.ArrayList(); 221 this.defaultEntityRadius = 3; 222 this.legendItemLabelGenerator 223 = new StandardXYSeriesLabelGenerator("{0}"); 224 } 225 226 /** 227 * Returns the number of passes through the data that the renderer requires 228 * in order to draw the chart. Most charts will require a single pass, but 229 * some require two passes. 230 * 231 * @return The pass count. 232 */ 233 public int getPassCount() { 234 return 1; 235 } 236 237 /** 238 * Returns the plot that the renderer is assigned to. 239 * 240 * @return The plot. 241 */ 242 public XYPlot getPlot() { 243 return this.plot; 244 } 245 246 /** 247 * Sets the plot that the renderer is assigned to. 248 * 249 * @param plot the plot. 250 */ 251 public void setPlot(XYPlot plot) { 252 this.plot = plot; 253 } 254 255 /** 256 * Initialises the renderer and returns a state object that should be 257 * passed to all subsequent calls to the drawItem() method. 258 * <P> 259 * This method will be called before the first item is rendered, giving the 260 * renderer an opportunity to initialise any state information it wants to 261 * maintain. The renderer can do nothing if it chooses. 262 * 263 * @param g2 the graphics device. 264 * @param dataArea the area inside the axes. 265 * @param plot the plot. 266 * @param data the data. 267 * @param info an optional info collection object to return data back to 268 * the caller. 269 * 270 * @return The renderer state (never <code>null</code>). 271 */ 272 public XYItemRendererState initialise(Graphics2D g2, 273 Rectangle2D dataArea, 274 XYPlot plot, 275 XYDataset data, 276 PlotRenderingInfo info) { 277 278 XYItemRendererState state = new XYItemRendererState(info); 279 return state; 280 281 } 282 283 // ITEM LABEL GENERATOR 284 285 /** 286 * Returns the label generator for a data item. This implementation simply 287 * passes control to the {@link #getSeriesItemLabelGenerator(int)} method. 288 * If, for some reason, you want a different generator for individual 289 * items, you can override this method. 290 * 291 * @param row the row index (zero based). 292 * @param column the column index (zero based). 293 * 294 * @return The generator (possibly <code>null</code>). 295 */ 296 public XYItemLabelGenerator getItemLabelGenerator(int row, int column) { 297 return getSeriesItemLabelGenerator(row); 298 } 299 300 /** 301 * Returns the item label generator for a series. 302 * 303 * @param series the series index (zero based). 304 * 305 * @return The generator (possibly <code>null</code>). 306 */ 307 public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) { 308 309 // return the generator for ALL series, if there is one... 310 if (this.itemLabelGenerator != null) { 311 return this.itemLabelGenerator; 312 } 313 314 // otherwise look up the generator table 315 XYItemLabelGenerator generator 316 = (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series); 317 if (generator == null) { 318 generator = this.baseItemLabelGenerator; 319 } 320 return generator; 321 322 } 323 324 /** 325 * Sets the item label generator for ALL series and sends a 326 * {@link RendererChangeEvent} to all registered listeners. 327 * 328 * @param generator the generator (<code>null</code> permitted). 329 */ 330 public void setItemLabelGenerator(XYItemLabelGenerator generator) { 331 this.itemLabelGenerator = generator; 332 notifyListeners(new RendererChangeEvent(this)); 333 } 334 335 /** 336 * Sets the item label generator for a series and sends a 337 * {@link RendererChangeEvent} to all registered listeners. 338 * 339 * @param series the series index (zero based). 340 * @param generator the generator (<code>null</code> permitted). 341 */ 342 public void setSeriesItemLabelGenerator(int series, 343 XYItemLabelGenerator generator) { 344 this.itemLabelGeneratorList.set(series, generator); 345 notifyListeners(new RendererChangeEvent(this)); 346 } 347 348 /** 349 * Returns the base item label generator. 350 * 351 * @return The generator (possibly <code>null</code>). 352 */ 353 public XYItemLabelGenerator getBaseItemLabelGenerator() { 354 return this.baseItemLabelGenerator; 355 } 356 357 /** 358 * Sets the base item label generator and sends a 359 * {@link RendererChangeEvent} to all registered listeners. 360 * 361 * @param generator the generator (<code>null</code> permitted). 362 */ 363 public void setBaseItemLabelGenerator(XYItemLabelGenerator generator) { 364 this.baseItemLabelGenerator = generator; 365 notifyListeners(new RendererChangeEvent(this)); 366 } 367 368 // TOOL TIP GENERATOR 369 370 /** 371 * Returns the tool tip generator for a data item. This implementation 372 * simply passes control to the getSeriesToolTipGenerator() method. If, 373 * for some reason, you want a different generator for individual items, 374 * you can override this method. 375 * 376 * @param row the row index (zero based). 377 * @param column the column index (zero based). 378 * 379 * @return The generator (possibly <code>null</code>). 380 */ 381 public XYToolTipGenerator getToolTipGenerator(int row, int column) { 382 return getSeriesToolTipGenerator(row); 383 } 384 385 /** 386 * Returns the tool tip generator for a series. 387 * 388 * @param series the series index (zero based). 389 * 390 * @return The generator (possibly <code>null</code>). 391 */ 392 public XYToolTipGenerator getSeriesToolTipGenerator(int series) { 393 394 // return the generator for ALL series, if there is one... 395 if (this.toolTipGenerator != null) { 396 return this.toolTipGenerator; 397 } 398 399 // otherwise look up the generator table 400 XYToolTipGenerator generator 401 = (XYToolTipGenerator) this.toolTipGeneratorList.get(series); 402 if (generator == null) { 403 generator = this.baseToolTipGenerator; 404 } 405 return generator; 406 407 } 408 409 /** 410 * Sets the tool tip generator for ALL series and sends a 411 * {@link RendererChangeEvent} to all registered listeners. 412 * 413 * @param generator the generator (<code>null</code> permitted). 414 */ 415 public void setToolTipGenerator(XYToolTipGenerator generator) { 416 this.toolTipGenerator = generator; 417 notifyListeners(new RendererChangeEvent(this)); 418 } 419 420 /** 421 * Sets the tool tip generator for a series and sends a 422 * {@link RendererChangeEvent} to all registered listeners. 423 * 424 * @param series the series index (zero based). 425 * @param generator the generator (<code>null</code> permitted). 426 */ 427 public void setSeriesToolTipGenerator(int series, 428 XYToolTipGenerator generator) { 429 this.toolTipGeneratorList.set(series, generator); 430 notifyListeners(new RendererChangeEvent(this)); 431 } 432 433 /** 434 * Returns the base tool tip generator. 435 * 436 * @return The generator (possibly <code>null</code>). 437 */ 438 public XYToolTipGenerator getBaseToolTipGenerator() { 439 return this.baseToolTipGenerator; 440 } 441 442 /** 443 * Sets the base tool tip generator and sends a {@link RendererChangeEvent} 444 * to all registered listeners. 445 * 446 * @param generator the generator (<code>null</code> permitted). 447 */ 448 public void setBaseToolTipGenerator(XYToolTipGenerator generator) { 449 this.baseToolTipGenerator = generator; 450 notifyListeners(new RendererChangeEvent(this)); 451 } 452 453 // URL GENERATOR 454 455 /** 456 * Returns the URL generator for HTML image maps. 457 * 458 * @return The URL generator (possibly <code>null</code>). 459 */ 460 public XYURLGenerator getURLGenerator() { 461 return this.urlGenerator; 462 } 463 464 /** 465 * Sets the URL generator for HTML image maps. 466 * 467 * @param urlGenerator the URL generator (<code>null</code> permitted). 468 */ 469 public void setURLGenerator(XYURLGenerator urlGenerator) { 470 this.urlGenerator = urlGenerator; 471 notifyListeners(new RendererChangeEvent(this)); 472 } 473 474 /** 475 * Adds an annotation and sends a {@link RendererChangeEvent} to all 476 * registered listeners. The annotation is added to the foreground 477 * layer. 478 * 479 * @param annotation the annotation (<code>null</code> not permitted). 480 */ 481 public void addAnnotation(XYAnnotation annotation) { 482 // defer argument checking 483 addAnnotation(annotation, Layer.FOREGROUND); 484 } 485 486 /** 487 * Adds an annotation to the specified layer. 488 * 489 * @param annotation the annotation (<code>null</code> not permitted). 490 * @param layer the layer (<code>null</code> not permitted). 491 */ 492 public void addAnnotation(XYAnnotation annotation, Layer layer) { 493 if (annotation == null) { 494 throw new IllegalArgumentException("Null 'annotation' argument."); 495 } 496 if (layer.equals(Layer.FOREGROUND)) { 497 this.foregroundAnnotations.add(annotation); 498 notifyListeners(new RendererChangeEvent(this)); 499 } 500 else if (layer.equals(Layer.BACKGROUND)) { 501 this.backgroundAnnotations.add(annotation); 502 notifyListeners(new RendererChangeEvent(this)); 503 } 504 else { 505 // should never get here 506 throw new RuntimeException("Unknown layer."); 507 } 508 } 509 /** 510 * Removes the specified annotation and sends a {@link RendererChangeEvent} 511 * to all registered listeners. 512 * 513 * @param annotation the annotation to remove (<code>null</code> not 514 * permitted). 515 * 516 * @return A boolean to indicate whether or not the annotation was 517 * successfully removed. 518 */ 519 public boolean removeAnnotation(XYAnnotation annotation) { 520 boolean removed = this.foregroundAnnotations.remove(annotation); 521 removed = removed & this.backgroundAnnotations.remove(annotation); 522 notifyListeners(new RendererChangeEvent(this)); 523 return removed; 524 } 525 526 /** 527 * Removes all annotations and sends a {@link RendererChangeEvent} 528 * to all registered listeners. 529 */ 530 public void removeAnnotations() { 531 this.foregroundAnnotations.clear(); 532 this.backgroundAnnotations.clear(); 533 notifyListeners(new RendererChangeEvent(this)); 534 } 535 536 /** 537 * Returns the radius of the circle used for the default entity area 538 * when no area is specified. 539 * 540 * @return A radius. 541 */ 542 public int getDefaultEntityRadius() { 543 return this.defaultEntityRadius; 544 } 545 546 /** 547 * Sets the radius of the circle used for the default entity area 548 * when no area is specified. 549 * 550 * @param radius the radius. 551 */ 552 public void setDefaultEntityRadius(int radius) { 553 this.defaultEntityRadius = radius; 554 } 555 556 /** 557 * Returns the legend item label generator. 558 * 559 * @return The label generator (never <code>null</code>). 560 * 561 * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator) 562 */ 563 public XYSeriesLabelGenerator getLegendItemLabelGenerator() { 564 return this.legendItemLabelGenerator; 565 } 566 567 /** 568 * Sets the legend item label generator and sends a 569 * {@link RendererChangeEvent} to all registered listeners. 570 * 571 * @param generator the generator (<code>null</code> not permitted). 572 * 573 * @see #getLegendItemLabelGenerator() 574 */ 575 public void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator) { 576 if (generator == null) { 577 throw new IllegalArgumentException("Null 'generator' argument."); 578 } 579 this.legendItemLabelGenerator = generator; 580 notifyListeners(new RendererChangeEvent(this)); 581 } 582 583 /** 584 * Returns the legend item tool tip generator. 585 * 586 * @return The tool tip generator (possibly <code>null</code>). 587 * 588 * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator) 589 */ 590 public XYSeriesLabelGenerator getLegendItemToolTipGenerator() { 591 return this.legendItemToolTipGenerator; 592 } 593 594 /** 595 * Sets the legend item tool tip generator and sends a 596 * {@link RendererChangeEvent} to all registered listeners. 597 * 598 * @param generator the generator (<code>null</code> permitted). 599 * 600 * @see #getLegendItemToolTipGenerator() 601 */ 602 public void setLegendItemToolTipGenerator( 603 XYSeriesLabelGenerator generator) { 604 this.legendItemToolTipGenerator = generator; 605 notifyListeners(new RendererChangeEvent(this)); 606 } 607 608 /** 609 * Returns the legend item URL generator. 610 * 611 * @return The URL generator (possibly <code>null</code>). 612 * 613 * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator) 614 */ 615 public XYSeriesLabelGenerator getLegendItemURLGenerator() { 616 return this.legendItemURLGenerator; 617 } 618 619 /** 620 * Sets the legend item URL generator and sends a 621 * {@link RendererChangeEvent} to all registered listeners. 622 * 623 * @param generator the generator (<code>null</code> permitted). 624 * 625 * @see #getLegendItemURLGenerator() 626 */ 627 public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) { 628 this.legendItemURLGenerator = generator; 629 notifyListeners(new RendererChangeEvent(this)); 630 } 631 632 /** 633 * Returns the lower and upper bounds (range) of the x-values in the 634 * specified dataset. 635 * 636 * @param dataset the dataset (<code>null</code> permitted). 637 * 638 * @return The range (<code>null</code> if the dataset is <code>null</code> 639 * or empty). 640 */ 641 public Range findDomainBounds(XYDataset dataset) { 642 if (dataset != null) { 643 return DatasetUtilities.findDomainBounds(dataset, false); 644 } 645 else { 646 return null; 647 } 648 } 649 650 /** 651 * Returns the range of values the renderer requires to display all the 652 * items from the specified dataset. 653 * 654 * @param dataset the dataset (<code>null</code> permitted). 655 * 656 * @return The range (<code>null</code> if the dataset is <code>null</code> 657 * or empty). 658 */ 659 public Range findRangeBounds(XYDataset dataset) { 660 if (dataset != null) { 661 return DatasetUtilities.findRangeBounds(dataset, false); 662 } 663 else { 664 return null; 665 } 666 } 667 668 /** 669 * Returns a (possibly empty) collection of legend items for the series 670 * that this renderer is responsible for drawing. 671 * 672 * @return The legend item collection (never <code>null</code>). 673 */ 674 public LegendItemCollection getLegendItems() { 675 if (this.plot == null) { 676 return new LegendItemCollection(); 677 } 678 LegendItemCollection result = new LegendItemCollection(); 679 int index = this.plot.getIndexOf(this); 680 XYDataset dataset = this.plot.getDataset(index); 681 if (dataset != null) { 682 int seriesCount = dataset.getSeriesCount(); 683 for (int i = 0; i < seriesCount; i++) { 684 if (isSeriesVisibleInLegend(i)) { 685 LegendItem item = getLegendItem(index, i); 686 if (item != null) { 687 result.add(item); 688 } 689 } 690 } 691 692 } 693 return result; 694 } 695 696 /** 697 * Returns a default legend item for the specified series. Subclasses 698 * should override this method to generate customised items. 699 * 700 * @param datasetIndex the dataset index (zero-based). 701 * @param series the series index (zero-based). 702 * 703 * @return A legend item for the series. 704 */ 705 public LegendItem getLegendItem(int datasetIndex, int series) { 706 LegendItem result = null; 707 XYPlot xyplot = getPlot(); 708 if (xyplot != null) { 709 XYDataset dataset = xyplot.getDataset(datasetIndex); 710 if (dataset != null) { 711 String label = this.legendItemLabelGenerator.generateLabel( 712 dataset, series); 713 String description = label; 714 String toolTipText = null; 715 if (getLegendItemToolTipGenerator() != null) { 716 toolTipText = getLegendItemToolTipGenerator().generateLabel( 717 dataset, series); 718 } 719 String urlText = null; 720 if (getLegendItemURLGenerator() != null) { 721 urlText = getLegendItemURLGenerator().generateLabel( 722 dataset, series); 723 } 724 Shape shape = getSeriesShape(series); 725 Paint paint = getSeriesPaint(series); 726 Paint outlinePaint = getSeriesOutlinePaint(series); 727 Stroke outlineStroke = getSeriesOutlineStroke(series); 728 result = new LegendItem(label, description, toolTipText, 729 urlText, shape, paint, outlineStroke, outlinePaint); 730 result.setSeriesIndex(series); 731 result.setDatasetIndex(datasetIndex); 732 } 733 } 734 return result; 735 } 736 737 /** 738 * Fills a band between two values on the axis. This can be used to color 739 * bands between the grid lines. 740 * 741 * @param g2 the graphics device. 742 * @param plot the plot. 743 * @param axis the domain axis. 744 * @param dataArea the data area. 745 * @param start the start value. 746 * @param end the end value. 747 */ 748 public void fillDomainGridBand(Graphics2D g2, 749 XYPlot plot, 750 ValueAxis axis, 751 Rectangle2D dataArea, 752 double start, double end) { 753 754 double x1 = axis.valueToJava2D(start, dataArea, 755 plot.getDomainAxisEdge()); 756 double x2 = axis.valueToJava2D(end, dataArea, 757 plot.getDomainAxisEdge()); 758 // TODO: need to change the next line to take account of plot 759 // orientation... 760 Rectangle2D band = new Rectangle2D.Double(x1, dataArea.getMinY(), 761 x2 - x1, dataArea.getMaxY() - dataArea.getMinY()); 762 Paint paint = plot.getDomainTickBandPaint(); 763 764 if (paint != null) { 765 g2.setPaint(paint); 766 g2.fill(band); 767 } 768 769 } 770 771 /** 772 * Fills a band between two values on the range axis. This can be used to 773 * color bands between the grid lines. 774 * 775 * @param g2 the graphics device. 776 * @param plot the plot. 777 * @param axis the range axis. 778 * @param dataArea the data area. 779 * @param start the start value. 780 * @param end the end value. 781 */ 782 public void fillRangeGridBand(Graphics2D g2, 783 XYPlot plot, 784 ValueAxis axis, 785 Rectangle2D dataArea, 786 double start, double end) { 787 788 double y1 = axis.valueToJava2D(start, dataArea, 789 plot.getRangeAxisEdge()); 790 double y2 = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge()); 791 // TODO: need to change the next line to take account of the plot 792 // orientation 793 Rectangle2D band = new Rectangle2D.Double(dataArea.getMinX(), y2, 794 dataArea.getWidth(), y1 - y2); 795 Paint paint = plot.getRangeTickBandPaint(); 796 797 if (paint != null) { 798 g2.setPaint(paint); 799 g2.fill(band); 800 } 801 802 } 803 804 /** 805 * Draws a grid line against the range axis. 806 * 807 * @param g2 the graphics device. 808 * @param plot the plot. 809 * @param axis the value axis. 810 * @param dataArea the area for plotting data (not yet adjusted for any 811 * 3D effect). 812 * @param value the value at which the grid line should be drawn. 813 */ 814 public void drawDomainGridLine(Graphics2D g2, 815 XYPlot plot, 816 ValueAxis axis, 817 Rectangle2D dataArea, 818 double value) { 819 820 Range range = axis.getRange(); 821 if (!range.contains(value)) { 822 return; 823 } 824 825 PlotOrientation orientation = plot.getOrientation(); 826 double v = axis.valueToJava2D(value, dataArea, 827 plot.getDomainAxisEdge()); 828 Line2D line = null; 829 if (orientation == PlotOrientation.HORIZONTAL) { 830 line = new Line2D.Double(dataArea.getMinX(), v, 831 dataArea.getMaxX(), v); 832 } 833 else if (orientation == PlotOrientation.VERTICAL) { 834 line = new Line2D.Double(v, dataArea.getMinY(), v, 835 dataArea.getMaxY()); 836 } 837 838 Paint paint = plot.getDomainGridlinePaint(); 839 Stroke stroke = plot.getDomainGridlineStroke(); 840 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT); 841 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE); 842 g2.draw(line); 843 844 } 845 846 /** 847 * Draws a line perpendicular to the range axis. 848 * 849 * @param g2 the graphics device. 850 * @param plot the plot. 851 * @param axis the value axis. 852 * @param dataArea the area for plotting data (not yet adjusted for any 3D 853 * effect). 854 * @param value the value at which the grid line should be drawn. 855 * @param paint the paint. 856 * @param stroke the stroke. 857 */ 858 public void drawRangeLine(Graphics2D g2, 859 XYPlot plot, 860 ValueAxis axis, 861 Rectangle2D dataArea, 862 double value, 863 Paint paint, 864 Stroke stroke) { 865 866 Range range = axis.getRange(); 867 if (!range.contains(value)) { 868 return; 869 } 870 871 PlotOrientation orientation = plot.getOrientation(); 872 Line2D line = null; 873 double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); 874 if (orientation == PlotOrientation.HORIZONTAL) { 875 line = new Line2D.Double(v, dataArea.getMinY(), v, 876 dataArea.getMaxY()); 877 } 878 else if (orientation == PlotOrientation.VERTICAL) { 879 line = new Line2D.Double(dataArea.getMinX(), v, 880 dataArea.getMaxX(), v); 881 } 882 883 g2.setPaint(paint); 884 g2.setStroke(stroke); 885 g2.draw(line); 886 887 } 888 889 /** 890 * Draws a vertical line on the chart to represent a 'range marker'. 891 * 892 * @param g2 the graphics device. 893 * @param plot the plot. 894 * @param domainAxis the domain axis. 895 * @param marker the marker line. 896 * @param dataArea the axis data area. 897 */ 898 public void drawDomainMarker(Graphics2D g2, 899 XYPlot plot, 900 ValueAxis domainAxis, 901 Marker marker, 902 Rectangle2D dataArea) { 903 904 if (marker instanceof ValueMarker) { 905 ValueMarker vm = (ValueMarker) marker; 906 double value = vm.getValue(); 907 Range range = domainAxis.getRange(); 908 if (!range.contains(value)) { 909 return; 910 } 911 912 double v = domainAxis.valueToJava2D(value, dataArea, 913 plot.getDomainAxisEdge()); 914 915 PlotOrientation orientation = plot.getOrientation(); 916 Line2D line = null; 917 if (orientation == PlotOrientation.HORIZONTAL) { 918 line = new Line2D.Double(dataArea.getMinX(), v, 919 dataArea.getMaxX(), v); 920 } 921 else if (orientation == PlotOrientation.VERTICAL) { 922 line = new Line2D.Double(v, dataArea.getMinY(), v, 923 dataArea.getMaxY()); 924 } 925 926 final Composite originalComposite = g2.getComposite(); 927 g2.setComposite(AlphaComposite.getInstance( 928 AlphaComposite.SRC_OVER, marker.getAlpha())); 929 g2.setPaint(marker.getPaint()); 930 g2.setStroke(marker.getStroke()); 931 g2.draw(line); 932 933 String label = marker.getLabel(); 934 RectangleAnchor anchor = marker.getLabelAnchor(); 935 if (label != null) { 936 Font labelFont = marker.getLabelFont(); 937 g2.setFont(labelFont); 938 g2.setPaint(marker.getLabelPaint()); 939 Point2D coordinates = calculateDomainMarkerTextAnchorPoint( 940 g2, orientation, dataArea, line.getBounds2D(), 941 marker.getLabelOffset(), 942 LengthAdjustmentType.EXPAND, anchor); 943 TextUtilities.drawAlignedString(label, g2, 944 (float) coordinates.getX(), (float) coordinates.getY(), 945 marker.getLabelTextAnchor()); 946 } 947 g2.setComposite(originalComposite); 948 } 949 else if (marker instanceof IntervalMarker) { 950 IntervalMarker im = (IntervalMarker) marker; 951 double start = im.getStartValue(); 952 double end = im.getEndValue(); 953 Range range = domainAxis.getRange(); 954 if (!(range.intersects(start, end))) { 955 return; 956 } 957 958 double start2d = domainAxis.valueToJava2D(start, dataArea, 959 plot.getDomainAxisEdge()); 960 double end2d = domainAxis.valueToJava2D(end, dataArea, 961 plot.getDomainAxisEdge()); 962 963 PlotOrientation orientation = plot.getOrientation(); 964 Rectangle2D rect = null; 965 if (orientation == PlotOrientation.HORIZONTAL) { 966 rect = new Rectangle2D.Double(dataArea.getMinX(), 967 Math.min(start2d, end2d), dataArea.getWidth(), 968 Math.abs(end2d - start2d)); 969 } 970 else if (orientation == PlotOrientation.VERTICAL) { 971 rect = new Rectangle2D.Double(Math.min(start2d, end2d), 972 dataArea.getMinY(), Math.abs(end2d - start2d), 973 dataArea.getHeight()); 974 } 975 976 final Composite originalComposite = g2.getComposite(); 977 g2.setComposite(AlphaComposite.getInstance( 978 AlphaComposite.SRC_OVER, marker.getAlpha())); 979 Paint p = marker.getPaint(); 980 if (p instanceof GradientPaint) { 981 GradientPaint gp = (GradientPaint) p; 982 GradientPaintTransformer t = im.getGradientPaintTransformer(); 983 if (t != null) { 984 gp = t.transform(gp, rect); 985 } 986 g2.setPaint(gp); 987 } 988 else { 989 g2.setPaint(p); 990 } 991 g2.fill(rect); 992 993 // now draw the outlines, if visible... 994 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { 995 double x0 = rect.getMinX(); 996 double x1 = rect.getMaxX(); 997 double y0 = rect.getMinY(); 998 double y1 = rect.getMaxY(); 999 if (orientation == PlotOrientation.VERTICAL) { 1000 Line2D line = new Line2D.Double(x0, y0, x0, y1); 1001 g2.setPaint(im.getOutlinePaint()); 1002 g2.setStroke(im.getOutlineStroke()); 1003 g2.draw(line); 1004 line.setLine(x1, y0, x1, y1); 1005 g2.draw(line); 1006 } 1007 else { // PlotOrientation.HORIZONTAL 1008 Line2D line = new Line2D.Double(x0, y0, x1, y0); 1009 g2.setPaint(im.getOutlinePaint()); 1010 g2.setStroke(im.getOutlineStroke()); 1011 g2.draw(line); 1012 line.setLine(x0, y1, x1, y1); 1013 g2.draw(line); 1014 } 1015 } 1016 1017 String label = marker.getLabel(); 1018 RectangleAnchor anchor = marker.getLabelAnchor(); 1019 if (label != null) { 1020 Font labelFont = marker.getLabelFont(); 1021 g2.setFont(labelFont); 1022 g2.setPaint(marker.getLabelPaint()); 1023 Point2D coordinates = calculateDomainMarkerTextAnchorPoint( 1024 g2, orientation, dataArea, rect, 1025 marker.getLabelOffset(), marker.getLabelOffsetType(), 1026 anchor); 1027 TextUtilities.drawAlignedString(label, g2, 1028 (float) coordinates.getX(), (float) coordinates.getY(), 1029 marker.getLabelTextAnchor()); 1030 } 1031 g2.setComposite(originalComposite); 1032 1033 } 1034 1035 } 1036 1037 /** 1038 * Calculates the (x, y) coordinates for drawing a marker label. 1039 * 1040 * @param g2 the graphics device. 1041 * @param orientation the plot orientation. 1042 * @param dataArea the data area. 1043 * @param markerArea the rectangle surrounding the marker area. 1044 * @param markerOffset the marker label offset. 1045 * @param labelOffsetType the label offset type. 1046 * @param anchor the label anchor. 1047 * 1048 * @return The coordinates for drawing the marker label. 1049 */ 1050 protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2, 1051 PlotOrientation orientation, 1052 Rectangle2D dataArea, 1053 Rectangle2D markerArea, 1054 RectangleInsets markerOffset, 1055 LengthAdjustmentType labelOffsetType, 1056 RectangleAnchor anchor) { 1057 1058 Rectangle2D anchorRect = null; 1059 if (orientation == PlotOrientation.HORIZONTAL) { 1060 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1061 LengthAdjustmentType.CONTRACT, labelOffsetType); 1062 } 1063 else if (orientation == PlotOrientation.VERTICAL) { 1064 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1065 labelOffsetType, LengthAdjustmentType.CONTRACT); 1066 } 1067 return RectangleAnchor.coordinates(anchorRect, anchor); 1068 1069 } 1070 1071 /** 1072 * Draws a horizontal line across the chart to represent a 'range marker'. 1073 * 1074 * @param g2 the graphics device. 1075 * @param plot the plot. 1076 * @param rangeAxis the range axis. 1077 * @param marker the marker line. 1078 * @param dataArea the axis data area. 1079 */ 1080 public void drawRangeMarker(Graphics2D g2, 1081 XYPlot plot, 1082 ValueAxis rangeAxis, 1083 Marker marker, 1084 Rectangle2D dataArea) { 1085 1086 if (marker instanceof ValueMarker) { 1087 ValueMarker vm = (ValueMarker) marker; 1088 double value = vm.getValue(); 1089 Range range = rangeAxis.getRange(); 1090 if (!range.contains(value)) { 1091 return; 1092 } 1093 1094 double v = rangeAxis.valueToJava2D(value, dataArea, 1095 plot.getRangeAxisEdge()); 1096 PlotOrientation orientation = plot.getOrientation(); 1097 Line2D line = null; 1098 if (orientation == PlotOrientation.HORIZONTAL) { 1099 line = new Line2D.Double(v, dataArea.getMinY(), v, 1100 dataArea.getMaxY()); 1101 } 1102 else if (orientation == PlotOrientation.VERTICAL) { 1103 line = new Line2D.Double(dataArea.getMinX(), v, 1104 dataArea.getMaxX(), v); 1105 } 1106 1107 final Composite originalComposite = g2.getComposite(); 1108 g2.setComposite(AlphaComposite.getInstance( 1109 AlphaComposite.SRC_OVER, marker.getAlpha())); 1110 g2.setPaint(marker.getPaint()); 1111 g2.setStroke(marker.getStroke()); 1112 g2.draw(line); 1113 1114 String label = marker.getLabel(); 1115 RectangleAnchor anchor = marker.getLabelAnchor(); 1116 if (label != null) { 1117 Font labelFont = marker.getLabelFont(); 1118 g2.setFont(labelFont); 1119 g2.setPaint(marker.getLabelPaint()); 1120 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 1121 g2, orientation, dataArea, line.getBounds2D(), 1122 marker.getLabelOffset(), 1123 LengthAdjustmentType.EXPAND, anchor); 1124 TextUtilities.drawAlignedString(label, g2, 1125 (float) coordinates.getX(), (float) coordinates.getY(), 1126 marker.getLabelTextAnchor()); 1127 } 1128 g2.setComposite(originalComposite); 1129 } 1130 else if (marker instanceof IntervalMarker) { 1131 1132 IntervalMarker im = (IntervalMarker) marker; 1133 double start = im.getStartValue(); 1134 double end = im.getEndValue(); 1135 Range range = rangeAxis.getRange(); 1136 if (!(range.intersects(start, end))) { 1137 return; 1138 } 1139 1140 double start2d = rangeAxis.valueToJava2D(start, dataArea, 1141 plot.getRangeAxisEdge()); 1142 double end2d = rangeAxis.valueToJava2D(end, dataArea, 1143 plot.getRangeAxisEdge()); 1144 1145 PlotOrientation orientation = plot.getOrientation(); 1146 Rectangle2D rect = null; 1147 if (orientation == PlotOrientation.HORIZONTAL) { 1148 rect = new Rectangle2D.Double(Math.min(start2d, end2d), 1149 dataArea.getMinY(), Math.abs(end2d - start2d), 1150 dataArea.getHeight()); 1151 } 1152 else if (orientation == PlotOrientation.VERTICAL) { 1153 rect = new Rectangle2D.Double(dataArea.getMinX(), 1154 Math.min(start2d, end2d), dataArea.getWidth(), 1155 Math.abs(end2d - start2d)); 1156 } 1157 1158 final Composite originalComposite = g2.getComposite(); 1159 g2.setComposite(AlphaComposite.getInstance( 1160 AlphaComposite.SRC_OVER, marker.getAlpha())); 1161 Paint p = marker.getPaint(); 1162 if (p instanceof GradientPaint) { 1163 GradientPaint gp = (GradientPaint) p; 1164 GradientPaintTransformer t = im.getGradientPaintTransformer(); 1165 if (t != null) { 1166 gp = t.transform(gp, rect); 1167 } 1168 g2.setPaint(gp); 1169 } 1170 else { 1171 g2.setPaint(p); 1172 } 1173 g2.fill(rect); 1174 1175 // now draw the outlines, if visible... 1176 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { 1177 double x0 = rect.getMinX(); 1178 double x1 = rect.getMaxX(); 1179 double y0 = rect.getMinY(); 1180 double y1 = rect.getMaxY(); 1181 if (orientation == PlotOrientation.VERTICAL) { 1182 Line2D line = new Line2D.Double(x0, y0, x0, y1); 1183 g2.setPaint(im.getOutlinePaint()); 1184 g2.setStroke(im.getOutlineStroke()); 1185 g2.draw(line); 1186 line.setLine(x1, y0, x1, y1); 1187 g2.draw(line); 1188 } 1189 else { // PlotOrientation.HORIZONTAL 1190 Line2D line = new Line2D.Double(x0, y0, x1, y0); 1191 g2.setPaint(im.getOutlinePaint()); 1192 g2.setStroke(im.getOutlineStroke()); 1193 g2.draw(line); 1194 line.setLine(x0, y1, x1, y1); 1195 g2.draw(line); 1196 } 1197 } 1198 1199 String label = marker.getLabel(); 1200 RectangleAnchor anchor = marker.getLabelAnchor(); 1201 if (label != null) { 1202 Font labelFont = marker.getLabelFont(); 1203 g2.setFont(labelFont); 1204 g2.setPaint(marker.getLabelPaint()); 1205 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 1206 g2, orientation, dataArea, rect, 1207 marker.getLabelOffset(), marker.getLabelOffsetType(), 1208 anchor); 1209 TextUtilities.drawAlignedString(label, g2, 1210 (float) coordinates.getX(), (float) coordinates.getY(), 1211 marker.getLabelTextAnchor()); 1212 } 1213 g2.setComposite(originalComposite); 1214 } 1215 } 1216 1217 /** 1218 * Calculates the (x, y) coordinates for drawing a marker label. 1219 * 1220 * @param g2 the graphics device. 1221 * @param orientation the plot orientation. 1222 * @param dataArea the data area. 1223 * @param markerArea the marker area. 1224 * @param markerOffset the marker offset. 1225 * @param anchor the label anchor. 1226 * 1227 * @return The coordinates for drawing the marker label. 1228 */ 1229 private Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2, 1230 PlotOrientation orientation, 1231 Rectangle2D dataArea, 1232 Rectangle2D markerArea, 1233 RectangleInsets markerOffset, 1234 LengthAdjustmentType labelOffsetForRange, 1235 RectangleAnchor anchor) { 1236 1237 Rectangle2D anchorRect = null; 1238 if (orientation == PlotOrientation.HORIZONTAL) { 1239 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1240 labelOffsetForRange, LengthAdjustmentType.CONTRACT); 1241 } 1242 else if (orientation == PlotOrientation.VERTICAL) { 1243 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1244 LengthAdjustmentType.CONTRACT, labelOffsetForRange); 1245 } 1246 return RectangleAnchor.coordinates(anchorRect, anchor); 1247 1248 } 1249 1250 /** 1251 * Returns a clone of the renderer. 1252 * 1253 * @return A clone. 1254 * 1255 * @throws CloneNotSupportedException if the renderer does not support 1256 * cloning. 1257 */ 1258 protected Object clone() throws CloneNotSupportedException { 1259 AbstractXYItemRenderer clone = (AbstractXYItemRenderer) super.clone(); 1260 // 'plot' : just retain reference, not a deep copy 1261 if (this.itemLabelGenerator != null 1262 && this.itemLabelGenerator instanceof PublicCloneable) { 1263 PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator; 1264 clone.itemLabelGenerator = (XYItemLabelGenerator) pc.clone(); 1265 } 1266 if (clone.legendItemLabelGenerator instanceof PublicCloneable) { 1267 clone.legendItemLabelGenerator = (XYSeriesLabelGenerator) 1268 ObjectUtilities.clone(this.legendItemLabelGenerator); 1269 } 1270 if (clone.legendItemToolTipGenerator instanceof PublicCloneable) { 1271 clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) 1272 ObjectUtilities.clone(this.legendItemToolTipGenerator); 1273 } 1274 if (clone.legendItemURLGenerator instanceof PublicCloneable) { 1275 clone.legendItemURLGenerator = (XYSeriesLabelGenerator) 1276 ObjectUtilities.clone(this.legendItemURLGenerator); 1277 } 1278 return clone; 1279 } 1280 1281 /** 1282 * Tests this renderer for equality with another object. 1283 * 1284 * @param obj the object. 1285 * 1286 * @return <code>true</code> or <code>false</code>. 1287 */ 1288 public boolean equals(Object obj) { 1289 1290 if (obj == null) { 1291 return false; 1292 } 1293 if (obj == this) { 1294 return true; 1295 } 1296 if (!(obj instanceof AbstractXYItemRenderer)) { 1297 return false; 1298 } 1299 AbstractXYItemRenderer renderer = (AbstractXYItemRenderer) obj; 1300 if (!super.equals(obj)) { 1301 return false; 1302 } 1303 if (!ObjectUtilities.equal(this.itemLabelGenerator, 1304 renderer.itemLabelGenerator)) { 1305 return false; 1306 } 1307 if (!ObjectUtilities.equal(this.urlGenerator, renderer.urlGenerator)) { 1308 return false; 1309 } 1310 return true; 1311 } 1312 1313 /** 1314 * Returns the drawing supplier from the plot. 1315 * 1316 * @return The drawing supplier (possibly <code>null</code>). 1317 */ 1318 public DrawingSupplier getDrawingSupplier() { 1319 DrawingSupplier result = null; 1320 XYPlot p = getPlot(); 1321 if (p != null) { 1322 result = p.getDrawingSupplier(); 1323 } 1324 return result; 1325 } 1326 1327 /** 1328 * Considers the current (x, y) coordinate and updates the crosshair point 1329 * if it meets the criteria (usually means the (x, y) coordinate is the 1330 * closest to the anchor point so far). 1331 * 1332 * @param crosshairState the crosshair state (<code>null</code> permitted, 1333 * but the method does nothing in that case). 1334 * @param x the x-value (in data space). 1335 * @param y the y-value (in data space). 1336 * @param transX the x-value translated to Java2D space. 1337 * @param transY the y-value translated to Java2D space. 1338 * @param orientation the plot orientation (<code>null</code> not 1339 * permitted). 1340 * 1341 * @deprecated Use {@link #updateCrosshairValues(CrosshairState, double, 1342 * double, int, int, double, double, PlotOrientation)} -- see bug 1343 * report 1086307. 1344 */ 1345 protected void updateCrosshairValues(CrosshairState crosshairState, 1346 double x, double y, double transX, double transY, 1347 PlotOrientation orientation) { 1348 updateCrosshairValues(crosshairState, x, y, 0, 0, transX, transY, 1349 orientation); 1350 } 1351 1352 /** 1353 * Considers the current (x, y) coordinate and updates the crosshair point 1354 * if it meets the criteria (usually means the (x, y) coordinate is the 1355 * closest to the anchor point so far). 1356 * 1357 * @param crosshairState the crosshair state (<code>null</code> permitted, 1358 * but the method does nothing in that case). 1359 * @param x the x-value (in data space). 1360 * @param y the y-value (in data space). 1361 * @param domainAxisIndex the index of the domain axis for the point. 1362 * @param rangeAxisIndex the index of the range axis for the point. 1363 * @param transX the x-value translated to Java2D space. 1364 * @param transY the y-value translated to Java2D space. 1365 * @param orientation the plot orientation (<code>null</code> not 1366 * permitted). 1367 * 1368 * @since 1.0.4 1369 */ 1370 protected void updateCrosshairValues(CrosshairState crosshairState, 1371 double x, double y, int domainAxisIndex, int rangeAxisIndex, 1372 double transX, double transY, PlotOrientation orientation) { 1373 1374 if (orientation == null) { 1375 throw new IllegalArgumentException("Null 'orientation' argument."); 1376 } 1377 1378 if (crosshairState != null) { 1379 // do we need to update the crosshair values? 1380 if (this.plot.isDomainCrosshairLockedOnData()) { 1381 if (this.plot.isRangeCrosshairLockedOnData()) { 1382 // both axes 1383 crosshairState.updateCrosshairPoint(x, y, domainAxisIndex, 1384 rangeAxisIndex, transX, transY, orientation); 1385 } 1386 else { 1387 // just the domain axis... 1388 crosshairState.updateCrosshairX(x, domainAxisIndex); 1389 } 1390 } 1391 else { 1392 if (this.plot.isRangeCrosshairLockedOnData()) { 1393 // just the range axis... 1394 crosshairState.updateCrosshairY(y, rangeAxisIndex); 1395 } 1396 } 1397 } 1398 1399 } 1400 1401 /** 1402 * Draws an item label. 1403 * 1404 * @param g2 the graphics device. 1405 * @param orientation the orientation. 1406 * @param dataset the dataset. 1407 * @param series the series index (zero-based). 1408 * @param item the item index (zero-based). 1409 * @param x the x coordinate (in Java2D space). 1410 * @param y the y coordinate (in Java2D space). 1411 * @param negative indicates a negative value (which affects the item 1412 * label position). 1413 */ 1414 protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, 1415 XYDataset dataset, int series, int item, double x, double y, 1416 boolean negative) { 1417 1418 XYItemLabelGenerator generator = getItemLabelGenerator(series, item); 1419 if (generator != null) { 1420 Font labelFont = getItemLabelFont(series, item); 1421 Paint paint = getItemLabelPaint(series, item); 1422 g2.setFont(labelFont); 1423 g2.setPaint(paint); 1424 String label = generator.generateLabel(dataset, series, item); 1425 1426 // get the label position.. 1427 ItemLabelPosition position = null; 1428 if (!negative) { 1429 position = getPositiveItemLabelPosition(series, item); 1430 } 1431 else { 1432 position = getNegativeItemLabelPosition(series, item); 1433 } 1434 1435 // work out the label anchor point... 1436 Point2D anchorPoint = calculateLabelAnchorPoint( 1437 position.getItemLabelAnchor(), x, y, orientation); 1438 TextUtilities.drawRotatedString(label, g2, 1439 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 1440 position.getTextAnchor(), position.getAngle(), 1441 position.getRotationAnchor()); 1442 } 1443 1444 } 1445 1446 /** 1447 * Draws all the annotations for the specified layer. 1448 * 1449 * @param g2 the graphics device. 1450 * @param dataArea the data area. 1451 * @param domainAxis the domain axis. 1452 * @param rangeAxis the range axis. 1453 * @param layer the layer. 1454 * @param info the plot rendering info. 1455 */ 1456 public void drawAnnotations(Graphics2D g2, 1457 Rectangle2D dataArea, 1458 ValueAxis domainAxis, 1459 ValueAxis rangeAxis, 1460 Layer layer, 1461 PlotRenderingInfo info) { 1462 1463 Iterator iterator = null; 1464 if (layer.equals(Layer.FOREGROUND)) { 1465 iterator = this.foregroundAnnotations.iterator(); 1466 } 1467 else if (layer.equals(Layer.BACKGROUND)) { 1468 iterator = this.backgroundAnnotations.iterator(); 1469 } 1470 else { 1471 // should not get here 1472 throw new RuntimeException("Unknown layer."); 1473 } 1474 while (iterator.hasNext()) { 1475 XYAnnotation annotation = (XYAnnotation) iterator.next(); 1476 annotation.draw(g2, this.plot, dataArea, domainAxis, rangeAxis, 1477 0, info); 1478 } 1479 1480 } 1481 1482 /** 1483 * Adds an entity to the collection. 1484 * 1485 * @param entities the entity collection being populated. 1486 * @param area the entity area (if <code>null</code> a default will be 1487 * used). 1488 * @param dataset the dataset. 1489 * @param series the series. 1490 * @param item the item. 1491 * @param entityX the entity's center x-coordinate in user space. 1492 * @param entityY the entity's center y-coordinate in user space. 1493 */ 1494 protected void addEntity(EntityCollection entities, Shape area, 1495 XYDataset dataset, int series, int item, 1496 double entityX, double entityY) { 1497 if (!getItemCreateEntity(series, item)) { 1498 return; 1499 } 1500 if (area == null) { 1501 area = new Ellipse2D.Double(entityX - this.defaultEntityRadius, 1502 entityY - this.defaultEntityRadius, 1503 this.defaultEntityRadius * 2, this.defaultEntityRadius * 2); 1504 } 1505 String tip = null; 1506 XYToolTipGenerator generator = getToolTipGenerator(series, item); 1507 if (generator != null) { 1508 tip = generator.generateToolTip(dataset, series, item); 1509 } 1510 String url = null; 1511 if (getURLGenerator() != null) { 1512 url = getURLGenerator().generateURL(dataset, series, item); 1513 } 1514 XYItemEntity entity = new XYItemEntity(area, dataset, series, item, 1515 tip, url); 1516 entities.add(entity); 1517 } 1518 1519 }