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