001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jfreechart/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * --------------------------------- 028 * AbstractCategoryItemRenderer.java 029 * --------------------------------- 030 * (C) Copyright 2002-2008, by Object Refinery Limited. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Richard Atkinson; 034 * 035 * Changes: 036 * -------- 037 * 29-May-2002 : Version 1 (DG); 038 * 06-Jun-2002 : Added accessor methods for the tool tip generator (DG); 039 * 11-Jun-2002 : Made constructors protected (DG); 040 * 26-Jun-2002 : Added axis to initialise method (DG); 041 * 05-Aug-2002 : Added urlGenerator member variable plus accessors (RA); 042 * 22-Aug-2002 : Added categoriesPaint attribute, based on code submitted by 043 * Janet Banks. This can be used when there is only one series, 044 * and you want each category item to have a different color (DG); 045 * 01-Oct-2002 : Fixed errors reported by Checkstyle (DG); 046 * 29-Oct-2002 : Fixed bug where background image for plot was not being 047 * drawn (DG); 048 * 05-Nov-2002 : Replaced references to CategoryDataset with TableDataset (DG); 049 * 26-Nov 2002 : Replaced the isStacked() method with getRangeType() (DG); 050 * 09-Jan-2003 : Renamed grid-line methods (DG); 051 * 17-Jan-2003 : Moved plot classes into separate package (DG); 052 * 25-Mar-2003 : Implemented Serializable (DG); 053 * 12-May-2003 : Modified to take into account the plot orientation (DG); 054 * 12-Aug-2003 : Very minor javadoc corrections (DB) 055 * 13-Aug-2003 : Implemented Cloneable (DG); 056 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 057 * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG); 058 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG); 059 * 11-Feb-2004 : Modified labelling for markers (DG); 060 * 12-Feb-2004 : Updated clone() method (DG); 061 * 15-Apr-2004 : Created a new CategoryToolTipGenerator interface (DG); 062 * 05-May-2004 : Fixed bug (948310) where interval markers extend outside axis 063 * range (DG); 064 * 14-Jun-2004 : Fixed bug in drawRangeMarker() method - now uses 'paint' and 065 * 'stroke' rather than 'outlinePaint' and 'outlineStroke' (DG); 066 * 15-Jun-2004 : Interval markers can now use GradientPaint (DG); 067 * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities 068 * --> TextUtilities (DG); 069 * 01-Oct-2004 : Fixed bug 1029697, problem with label alignment in 070 * drawRangeMarker() method (DG); 071 * 07-Jan-2005 : Renamed getRangeExtent() --> findRangeBounds() (DG); 072 * 21-Jan-2005 : Modified return type of calculateRangeMarkerTextAnchorPoint() 073 * method (DG); 074 * 08-Mar-2005 : Fixed positioning of marker labels (DG); 075 * 20-Apr-2005 : Added legend label, tooltip and URL generators (DG); 076 * 01-Jun-2005 : Handle one dimension of the marker label adjustment 077 * automatically (DG); 078 * 09-Jun-2005 : Added utility method for adding an item entity (DG); 079 * ------------- JFREECHART 1.0.x --------------------------------------------- 080 * 01-Mar-2006 : Updated getLegendItems() to check seriesVisibleInLegend 081 * flags (DG); 082 * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG); 083 * 23-Oct-2006 : Draw outlines for interval markers (DG); 084 * 24-Oct-2006 : Respect alpha setting in markers, as suggested by Sergei 085 * Ivanov in patch 1567843 (DG); 086 * 30-Nov-2006 : Added a check for series visibility in the getLegendItem() 087 * method (DG); 088 * 07-Dec-2006 : Fix for equals() method (DG); 089 * 22-Feb-2007 : Added createState() method (DG); 090 * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to 091 * Sergei Ivanov) (DG); 092 * 20-Apr-2007 : Updated getLegendItem() for renderer change, and deprecated 093 * itemLabelGenerator, toolTipGenerator and itemURLGenerator 094 * override fields (DG); 095 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG); 096 * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG); 097 * 26-Jun-2008 : Added crosshair support (DG); 098 * 099 */ 100 101 package org.jfree.chart.renderer.category; 102 103 import java.awt.AlphaComposite; 104 import java.awt.Composite; 105 import java.awt.Font; 106 import java.awt.GradientPaint; 107 import java.awt.Graphics2D; 108 import java.awt.Paint; 109 import java.awt.Shape; 110 import java.awt.Stroke; 111 import java.awt.geom.Line2D; 112 import java.awt.geom.Point2D; 113 import java.awt.geom.Rectangle2D; 114 import java.io.Serializable; 115 116 import org.jfree.chart.LegendItem; 117 import org.jfree.chart.LegendItemCollection; 118 import org.jfree.chart.axis.CategoryAxis; 119 import org.jfree.chart.axis.ValueAxis; 120 import org.jfree.chart.entity.CategoryItemEntity; 121 import org.jfree.chart.entity.EntityCollection; 122 import org.jfree.chart.event.RendererChangeEvent; 123 import org.jfree.chart.labels.CategoryItemLabelGenerator; 124 import org.jfree.chart.labels.CategorySeriesLabelGenerator; 125 import org.jfree.chart.labels.CategoryToolTipGenerator; 126 import org.jfree.chart.labels.ItemLabelPosition; 127 import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator; 128 import org.jfree.chart.plot.CategoryCrosshairState; 129 import org.jfree.chart.plot.CategoryMarker; 130 import org.jfree.chart.plot.CategoryPlot; 131 import org.jfree.chart.plot.DrawingSupplier; 132 import org.jfree.chart.plot.IntervalMarker; 133 import org.jfree.chart.plot.Marker; 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.renderer.AbstractRenderer; 138 import org.jfree.chart.urls.CategoryURLGenerator; 139 import org.jfree.data.Range; 140 import org.jfree.data.category.CategoryDataset; 141 import org.jfree.data.general.DatasetUtilities; 142 import org.jfree.text.TextUtilities; 143 import org.jfree.ui.GradientPaintTransformer; 144 import org.jfree.ui.LengthAdjustmentType; 145 import org.jfree.ui.RectangleAnchor; 146 import org.jfree.ui.RectangleEdge; 147 import org.jfree.ui.RectangleInsets; 148 import org.jfree.util.ObjectList; 149 import org.jfree.util.ObjectUtilities; 150 import org.jfree.util.PublicCloneable; 151 152 /** 153 * An abstract base class that you can use to implement a new 154 * {@link CategoryItemRenderer}. When you create a new 155 * {@link CategoryItemRenderer} you are not required to extend this class, 156 * but it makes the job easier. 157 */ 158 public abstract class AbstractCategoryItemRenderer extends AbstractRenderer 159 implements CategoryItemRenderer, Cloneable, PublicCloneable, 160 Serializable { 161 162 /** For serialization. */ 163 private static final long serialVersionUID = 1247553218442497391L; 164 165 /** The plot that the renderer is assigned to. */ 166 private CategoryPlot plot; 167 168 /** 169 * The item label generator for ALL series. 170 * 171 * @deprecated This field is redundant and deprecated as of version 1.0.6. 172 */ 173 private CategoryItemLabelGenerator itemLabelGenerator; 174 175 /** A list of item label generators (one per series). */ 176 private ObjectList itemLabelGeneratorList; 177 178 /** The base item label generator. */ 179 private CategoryItemLabelGenerator baseItemLabelGenerator; 180 181 /** 182 * The tool tip generator for ALL series. 183 * 184 * @deprecated This field is redundant and deprecated as of version 1.0.6. 185 */ 186 private CategoryToolTipGenerator toolTipGenerator; 187 188 /** A list of tool tip generators (one per series). */ 189 private ObjectList toolTipGeneratorList; 190 191 /** The base tool tip generator. */ 192 private CategoryToolTipGenerator baseToolTipGenerator; 193 194 /** 195 * The URL generator. 196 * 197 * @deprecated This field is redundant and deprecated as of version 1.0.6. 198 */ 199 private CategoryURLGenerator itemURLGenerator; 200 201 /** A list of item label generators (one per series). */ 202 private ObjectList itemURLGeneratorList; 203 204 /** The base item label generator. */ 205 private CategoryURLGenerator baseItemURLGenerator; 206 207 /** The legend item label generator. */ 208 private CategorySeriesLabelGenerator legendItemLabelGenerator; 209 210 /** The legend item tool tip generator. */ 211 private CategorySeriesLabelGenerator legendItemToolTipGenerator; 212 213 /** The legend item URL generator. */ 214 private CategorySeriesLabelGenerator legendItemURLGenerator; 215 216 /** The number of rows in the dataset (temporary record). */ 217 private transient int rowCount; 218 219 /** The number of columns in the dataset (temporary record). */ 220 private transient int columnCount; 221 222 /** 223 * Creates a new renderer with no tool tip generator and no URL generator. 224 * The defaults (no tool tip or URL generators) have been chosen to 225 * minimise the processing required to generate a default chart. If you 226 * require tool tips or URLs, then you can easily add the required 227 * generators. 228 */ 229 protected AbstractCategoryItemRenderer() { 230 this.itemLabelGenerator = null; 231 this.itemLabelGeneratorList = new ObjectList(); 232 this.toolTipGenerator = null; 233 this.toolTipGeneratorList = new ObjectList(); 234 this.itemURLGenerator = null; 235 this.itemURLGeneratorList = new ObjectList(); 236 this.legendItemLabelGenerator 237 = new StandardCategorySeriesLabelGenerator(); 238 } 239 240 /** 241 * Returns the number of passes through the dataset required by the 242 * renderer. This method returns <code>1</code>, subclasses should 243 * override if they need more passes. 244 * 245 * @return The pass count. 246 */ 247 public int getPassCount() { 248 return 1; 249 } 250 251 /** 252 * Returns the plot that the renderer has been assigned to (where 253 * <code>null</code> indicates that the renderer is not currently assigned 254 * to a plot). 255 * 256 * @return The plot (possibly <code>null</code>). 257 * 258 * @see #setPlot(CategoryPlot) 259 */ 260 public CategoryPlot getPlot() { 261 return this.plot; 262 } 263 264 /** 265 * Sets the plot that the renderer has been assigned to. This method is 266 * usually called by the {@link CategoryPlot}, in normal usage you 267 * shouldn't need to call this method directly. 268 * 269 * @param plot the plot (<code>null</code> not permitted). 270 * 271 * @see #getPlot() 272 */ 273 public void setPlot(CategoryPlot plot) { 274 if (plot == null) { 275 throw new IllegalArgumentException("Null 'plot' argument."); 276 } 277 this.plot = plot; 278 } 279 280 // ITEM LABEL GENERATOR 281 282 /** 283 * Returns the item label generator for a data item. This implementation 284 * simply passes control to the {@link #getSeriesItemLabelGenerator(int)} 285 * method. If, for some reason, you want a different generator for 286 * individual items, you can override this method. 287 * 288 * @param row the row index (zero based). 289 * @param column the column index (zero based). 290 * 291 * @return The generator (possibly <code>null</code>). 292 */ 293 public CategoryItemLabelGenerator getItemLabelGenerator(int row, 294 int column) { 295 return getSeriesItemLabelGenerator(row); 296 } 297 298 /** 299 * Returns the item label generator for a series. 300 * 301 * @param series the series index (zero based). 302 * 303 * @return The generator (possibly <code>null</code>). 304 * 305 * @see #setSeriesItemLabelGenerator(int, CategoryItemLabelGenerator) 306 */ 307 public CategoryItemLabelGenerator 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 CategoryItemLabelGenerator generator = (CategoryItemLabelGenerator) 316 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 * @deprecated This method should no longer be used (as of version 1.0.6). 331 * It is sufficient to rely on {@link #setSeriesItemLabelGenerator(int, 332 * CategoryItemLabelGenerator)} and 333 * {@link #setBaseItemLabelGenerator(CategoryItemLabelGenerator)}. 334 */ 335 public void setItemLabelGenerator(CategoryItemLabelGenerator generator) { 336 this.itemLabelGenerator = generator; 337 fireChangeEvent(); 338 } 339 340 /** 341 * Sets the item label generator for a series and sends a 342 * {@link RendererChangeEvent} to all registered listeners. 343 * 344 * @param series the series index (zero based). 345 * @param generator the generator (<code>null</code> permitted). 346 * 347 * @see #getSeriesItemLabelGenerator(int) 348 */ 349 public void setSeriesItemLabelGenerator(int series, 350 CategoryItemLabelGenerator generator) { 351 this.itemLabelGeneratorList.set(series, generator); 352 fireChangeEvent(); 353 } 354 355 /** 356 * Returns the base item label generator. 357 * 358 * @return The generator (possibly <code>null</code>). 359 * 360 * @see #setBaseItemLabelGenerator(CategoryItemLabelGenerator) 361 */ 362 public CategoryItemLabelGenerator getBaseItemLabelGenerator() { 363 return this.baseItemLabelGenerator; 364 } 365 366 /** 367 * Sets the base item label generator and sends a 368 * {@link RendererChangeEvent} to all registered listeners. 369 * 370 * @param generator the generator (<code>null</code> permitted). 371 * 372 * @see #getBaseItemLabelGenerator() 373 */ 374 public void setBaseItemLabelGenerator( 375 CategoryItemLabelGenerator generator) { 376 this.baseItemLabelGenerator = generator; 377 fireChangeEvent(); 378 } 379 380 // TOOL TIP GENERATOR 381 382 /** 383 * Returns the tool tip generator that should be used for the specified 384 * item. This method looks up the generator using the "three-layer" 385 * approach outlined in the general description of this interface. You 386 * can override this method if you want to return a different generator per 387 * item. 388 * 389 * @param row the row index (zero-based). 390 * @param column the column index (zero-based). 391 * 392 * @return The generator (possibly <code>null</code>). 393 */ 394 public CategoryToolTipGenerator getToolTipGenerator(int row, int column) { 395 396 CategoryToolTipGenerator result = null; 397 if (this.toolTipGenerator != null) { 398 result = this.toolTipGenerator; 399 } 400 else { 401 result = getSeriesToolTipGenerator(row); 402 if (result == null) { 403 result = this.baseToolTipGenerator; 404 } 405 } 406 return result; 407 } 408 409 /** 410 * Returns the tool tip generator that will be used for ALL items in the 411 * dataset (the "layer 0" generator). 412 * 413 * @return A tool tip generator (possibly <code>null</code>). 414 * 415 * @see #setToolTipGenerator(CategoryToolTipGenerator) 416 * 417 * @deprecated This method should no longer be used (as of version 1.0.6). 418 * It is sufficient to rely on {@link #getSeriesToolTipGenerator(int)} 419 * and {@link #getBaseToolTipGenerator()}. 420 */ 421 public CategoryToolTipGenerator getToolTipGenerator() { 422 return this.toolTipGenerator; 423 } 424 425 /** 426 * Sets the tool tip generator for ALL series and sends a 427 * {@link org.jfree.chart.event.RendererChangeEvent} to all registered 428 * listeners. 429 * 430 * @param generator the generator (<code>null</code> permitted). 431 * 432 * @see #getToolTipGenerator() 433 * 434 * @deprecated This method should no longer be used (as of version 1.0.6). 435 * It is sufficient to rely on {@link #setSeriesToolTipGenerator(int, 436 * CategoryToolTipGenerator)} and 437 * {@link #setBaseToolTipGenerator(CategoryToolTipGenerator)}. 438 */ 439 public void setToolTipGenerator(CategoryToolTipGenerator generator) { 440 this.toolTipGenerator = generator; 441 fireChangeEvent(); 442 } 443 444 /** 445 * Returns the tool tip generator for the specified series (a "layer 1" 446 * generator). 447 * 448 * @param series the series index (zero-based). 449 * 450 * @return The tool tip generator (possibly <code>null</code>). 451 * 452 * @see #setSeriesToolTipGenerator(int, CategoryToolTipGenerator) 453 */ 454 public CategoryToolTipGenerator getSeriesToolTipGenerator(int series) { 455 return (CategoryToolTipGenerator) this.toolTipGeneratorList.get(series); 456 } 457 458 /** 459 * Sets the tool tip generator for a series and sends a 460 * {@link RendererChangeEvent} to all registered listeners. 461 * 462 * @param series the series index (zero-based). 463 * @param generator the generator (<code>null</code> permitted). 464 * 465 * @see #getSeriesToolTipGenerator(int) 466 */ 467 public void setSeriesToolTipGenerator(int series, 468 CategoryToolTipGenerator generator) { 469 this.toolTipGeneratorList.set(series, generator); 470 fireChangeEvent(); 471 } 472 473 /** 474 * Returns the base tool tip generator (the "layer 2" generator). 475 * 476 * @return The tool tip generator (possibly <code>null</code>). 477 * 478 * @see #setBaseToolTipGenerator(CategoryToolTipGenerator) 479 */ 480 public CategoryToolTipGenerator getBaseToolTipGenerator() { 481 return this.baseToolTipGenerator; 482 } 483 484 /** 485 * Sets the base tool tip generator and sends a {@link RendererChangeEvent} 486 * to all registered listeners. 487 * 488 * @param generator the generator (<code>null</code> permitted). 489 * 490 * @see #getBaseToolTipGenerator() 491 */ 492 public void setBaseToolTipGenerator(CategoryToolTipGenerator generator) { 493 this.baseToolTipGenerator = generator; 494 fireChangeEvent(); 495 } 496 497 // URL GENERATOR 498 499 /** 500 * Returns the URL generator for a data item. This method just calls the 501 * getSeriesItemURLGenerator method, but you can override this behaviour if 502 * you want to. 503 * 504 * @param row the row index (zero based). 505 * @param column the column index (zero based). 506 * 507 * @return The URL generator. 508 */ 509 public CategoryURLGenerator getItemURLGenerator(int row, int column) { 510 return getSeriesItemURLGenerator(row); 511 } 512 513 /** 514 * Returns the URL generator for a series. 515 * 516 * @param series the series index (zero based). 517 * 518 * @return The URL generator for the series. 519 * 520 * @see #setSeriesItemURLGenerator(int, CategoryURLGenerator) 521 */ 522 public CategoryURLGenerator getSeriesItemURLGenerator(int series) { 523 524 // return the generator for ALL series, if there is one... 525 if (this.itemURLGenerator != null) { 526 return this.itemURLGenerator; 527 } 528 529 // otherwise look up the generator table 530 CategoryURLGenerator generator 531 = (CategoryURLGenerator) this.itemURLGeneratorList.get(series); 532 if (generator == null) { 533 generator = this.baseItemURLGenerator; 534 } 535 return generator; 536 537 } 538 539 /** 540 * Sets the item URL generator for ALL series and sends a 541 * {@link RendererChangeEvent} to all registered listeners. 542 * 543 * @param generator the generator. 544 * 545 * @deprecated This method should no longer be used (as of version 1.0.6). 546 * It is sufficient to rely on {@link #setSeriesItemURLGenerator(int, 547 * CategoryURLGenerator)} and 548 * {@link #setBaseItemURLGenerator(CategoryURLGenerator)}. 549 */ 550 public void setItemURLGenerator(CategoryURLGenerator generator) { 551 this.itemURLGenerator = generator; 552 fireChangeEvent(); 553 } 554 555 /** 556 * Sets the URL generator for a series and sends a 557 * {@link RendererChangeEvent} to all registered listeners. 558 * 559 * @param series the series index (zero based). 560 * @param generator the generator. 561 * 562 * @see #getSeriesItemURLGenerator(int) 563 */ 564 public void setSeriesItemURLGenerator(int series, 565 CategoryURLGenerator generator) { 566 this.itemURLGeneratorList.set(series, generator); 567 fireChangeEvent(); 568 } 569 570 /** 571 * Returns the base item URL generator. 572 * 573 * @return The item URL generator. 574 * 575 * @see #setBaseItemURLGenerator(CategoryURLGenerator) 576 */ 577 public CategoryURLGenerator getBaseItemURLGenerator() { 578 return this.baseItemURLGenerator; 579 } 580 581 /** 582 * Sets the base item URL generator and sends a 583 * {@link RendererChangeEvent} to all registered listeners. 584 * 585 * @param generator the item URL generator (<code>null</code> permitted). 586 * 587 * @see #getBaseItemURLGenerator() 588 */ 589 public void setBaseItemURLGenerator(CategoryURLGenerator generator) { 590 this.baseItemURLGenerator = generator; 591 fireChangeEvent(); 592 } 593 594 /** 595 * Returns the number of rows in the dataset. This value is updated in the 596 * {@link AbstractCategoryItemRenderer#initialise} method. 597 * 598 * @return The row count. 599 */ 600 public int getRowCount() { 601 return this.rowCount; 602 } 603 604 /** 605 * Returns the number of columns in the dataset. This value is updated in 606 * the {@link AbstractCategoryItemRenderer#initialise} method. 607 * 608 * @return The column count. 609 */ 610 public int getColumnCount() { 611 return this.columnCount; 612 } 613 614 /** 615 * Creates a new state instance---this method is called from the 616 * {@link #initialise(Graphics2D, Rectangle2D, CategoryPlot, int, 617 * PlotRenderingInfo)} method. Subclasses can override this method if 618 * they need to use a subclass of {@link CategoryItemRendererState}. 619 * 620 * @param info collects plot rendering info (<code>null</code> permitted). 621 * 622 * @return The new state instance (never <code>null</code>). 623 * 624 * @since 1.0.5 625 */ 626 protected CategoryItemRendererState createState(PlotRenderingInfo info) { 627 return new CategoryItemRendererState(info); 628 } 629 630 /** 631 * Initialises the renderer and returns a state object that will be used 632 * for the remainder of the drawing process for a single chart. The state 633 * object allows for the fact that the renderer may be used simultaneously 634 * by multiple threads (each thread will work with a separate state object). 635 * 636 * @param g2 the graphics device. 637 * @param dataArea the data area. 638 * @param plot the plot. 639 * @param rendererIndex the renderer index. 640 * @param info an object for returning information about the structure of 641 * the plot (<code>null</code> permitted). 642 * 643 * @return The renderer state. 644 */ 645 public CategoryItemRendererState initialise(Graphics2D g2, 646 Rectangle2D dataArea, 647 CategoryPlot plot, 648 int rendererIndex, 649 PlotRenderingInfo info) { 650 651 setPlot(plot); 652 CategoryDataset data = plot.getDataset(rendererIndex); 653 if (data != null) { 654 this.rowCount = data.getRowCount(); 655 this.columnCount = data.getColumnCount(); 656 } 657 else { 658 this.rowCount = 0; 659 this.columnCount = 0; 660 } 661 return createState(info); 662 663 } 664 665 /** 666 * Returns the range of values the renderer requires to display all the 667 * items from the specified dataset. 668 * 669 * @param dataset the dataset (<code>null</code> permitted). 670 * 671 * @return The range (or <code>null</code> if the dataset is 672 * <code>null</code> or empty). 673 */ 674 public Range findRangeBounds(CategoryDataset dataset) { 675 return DatasetUtilities.findRangeBounds(dataset); 676 } 677 678 /** 679 * Returns the Java2D coordinate for the middle of the specified data item. 680 * 681 * @param rowKey the row key. 682 * @param columnKey the column key. 683 * @param dataset the dataset. 684 * @param axis the axis. 685 * @param area the data area. 686 * @param edge the edge along which the axis lies. 687 * 688 * @return The Java2D coordinate for the middle of the item. 689 * 690 * @since 1.0.11 691 */ 692 public double getItemMiddle(Comparable rowKey, Comparable columnKey, 693 CategoryDataset dataset, CategoryAxis axis, Rectangle2D area, 694 RectangleEdge edge) { 695 return axis.getCategoryMiddle(columnKey, dataset.getColumnKeys(), area, 696 edge); 697 } 698 699 /** 700 * Draws a background for the data area. The default implementation just 701 * gets the plot to draw the background, but some renderers will override 702 * this behaviour. 703 * 704 * @param g2 the graphics device. 705 * @param plot the plot. 706 * @param dataArea the data area. 707 */ 708 public void drawBackground(Graphics2D g2, 709 CategoryPlot plot, 710 Rectangle2D dataArea) { 711 712 plot.drawBackground(g2, dataArea); 713 714 } 715 716 /** 717 * Draws an outline for the data area. The default implementation just 718 * gets the plot to draw the outline, but some renderers will override this 719 * behaviour. 720 * 721 * @param g2 the graphics device. 722 * @param plot the plot. 723 * @param dataArea the data area. 724 */ 725 public void drawOutline(Graphics2D g2, 726 CategoryPlot plot, 727 Rectangle2D dataArea) { 728 729 plot.drawOutline(g2, dataArea); 730 731 } 732 733 /** 734 * Draws a grid line against the domain axis. 735 * <P> 736 * Note that this default implementation assumes that the horizontal axis 737 * is the domain axis. If this is not the case, you will need to override 738 * this method. 739 * 740 * @param g2 the graphics device. 741 * @param plot the plot. 742 * @param dataArea the area for plotting data (not yet adjusted for any 743 * 3D effect). 744 * @param value the Java2D value at which the grid line should be drawn. 745 * 746 * @see #drawRangeGridline(Graphics2D, CategoryPlot, ValueAxis, 747 * Rectangle2D, double) 748 */ 749 public void drawDomainGridline(Graphics2D g2, 750 CategoryPlot plot, 751 Rectangle2D dataArea, 752 double value) { 753 754 Line2D line = null; 755 PlotOrientation orientation = plot.getOrientation(); 756 757 if (orientation == PlotOrientation.HORIZONTAL) { 758 line = new Line2D.Double(dataArea.getMinX(), value, 759 dataArea.getMaxX(), value); 760 } 761 else if (orientation == PlotOrientation.VERTICAL) { 762 line = new Line2D.Double(value, dataArea.getMinY(), value, 763 dataArea.getMaxY()); 764 } 765 766 Paint paint = plot.getDomainGridlinePaint(); 767 if (paint == null) { 768 paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT; 769 } 770 g2.setPaint(paint); 771 772 Stroke stroke = plot.getDomainGridlineStroke(); 773 if (stroke == null) { 774 stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE; 775 } 776 g2.setStroke(stroke); 777 778 g2.draw(line); 779 780 } 781 782 /** 783 * Draws a grid line against the range axis. 784 * 785 * @param g2 the graphics device. 786 * @param plot the plot. 787 * @param axis the value axis. 788 * @param dataArea the area for plotting data (not yet adjusted for any 789 * 3D effect). 790 * @param value the value at which the grid line should be drawn. 791 * 792 * @see #drawDomainGridline(Graphics2D, CategoryPlot, Rectangle2D, double) 793 * 794 */ 795 public void drawRangeGridline(Graphics2D g2, 796 CategoryPlot plot, 797 ValueAxis axis, 798 Rectangle2D dataArea, 799 double value) { 800 801 Range range = axis.getRange(); 802 if (!range.contains(value)) { 803 return; 804 } 805 806 PlotOrientation orientation = plot.getOrientation(); 807 double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); 808 Line2D line = null; 809 if (orientation == PlotOrientation.HORIZONTAL) { 810 line = new Line2D.Double(v, dataArea.getMinY(), v, 811 dataArea.getMaxY()); 812 } 813 else if (orientation == PlotOrientation.VERTICAL) { 814 line = new Line2D.Double(dataArea.getMinX(), v, 815 dataArea.getMaxX(), v); 816 } 817 818 Paint paint = plot.getRangeGridlinePaint(); 819 if (paint == null) { 820 paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT; 821 } 822 g2.setPaint(paint); 823 824 Stroke stroke = plot.getRangeGridlineStroke(); 825 if (stroke == null) { 826 stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE; 827 } 828 g2.setStroke(stroke); 829 830 g2.draw(line); 831 832 } 833 834 /** 835 * Draws a marker for the domain axis. 836 * 837 * @param g2 the graphics device (not <code>null</code>). 838 * @param plot the plot (not <code>null</code>). 839 * @param axis the range axis (not <code>null</code>). 840 * @param marker the marker to be drawn (not <code>null</code>). 841 * @param dataArea the area inside the axes (not <code>null</code>). 842 * 843 * @see #drawRangeMarker(Graphics2D, CategoryPlot, ValueAxis, Marker, 844 * Rectangle2D) 845 */ 846 public void drawDomainMarker(Graphics2D g2, 847 CategoryPlot plot, 848 CategoryAxis axis, 849 CategoryMarker marker, 850 Rectangle2D dataArea) { 851 852 Comparable category = marker.getKey(); 853 CategoryDataset dataset = plot.getDataset(plot.getIndexOf(this)); 854 int columnIndex = dataset.getColumnIndex(category); 855 if (columnIndex < 0) { 856 return; 857 } 858 859 final Composite savedComposite = g2.getComposite(); 860 g2.setComposite(AlphaComposite.getInstance( 861 AlphaComposite.SRC_OVER, marker.getAlpha())); 862 863 PlotOrientation orientation = plot.getOrientation(); 864 Rectangle2D bounds = null; 865 if (marker.getDrawAsLine()) { 866 double v = axis.getCategoryMiddle(columnIndex, 867 dataset.getColumnCount(), dataArea, 868 plot.getDomainAxisEdge()); 869 Line2D line = null; 870 if (orientation == PlotOrientation.HORIZONTAL) { 871 line = new Line2D.Double(dataArea.getMinX(), v, 872 dataArea.getMaxX(), v); 873 } 874 else if (orientation == PlotOrientation.VERTICAL) { 875 line = new Line2D.Double(v, dataArea.getMinY(), v, 876 dataArea.getMaxY()); 877 } 878 g2.setPaint(marker.getPaint()); 879 g2.setStroke(marker.getStroke()); 880 g2.draw(line); 881 bounds = line.getBounds2D(); 882 } 883 else { 884 double v0 = axis.getCategoryStart(columnIndex, 885 dataset.getColumnCount(), dataArea, 886 plot.getDomainAxisEdge()); 887 double v1 = axis.getCategoryEnd(columnIndex, 888 dataset.getColumnCount(), dataArea, 889 plot.getDomainAxisEdge()); 890 Rectangle2D area = null; 891 if (orientation == PlotOrientation.HORIZONTAL) { 892 area = new Rectangle2D.Double(dataArea.getMinX(), v0, 893 dataArea.getWidth(), (v1 - v0)); 894 } 895 else if (orientation == PlotOrientation.VERTICAL) { 896 area = new Rectangle2D.Double(v0, dataArea.getMinY(), 897 (v1 - v0), dataArea.getHeight()); 898 } 899 g2.setPaint(marker.getPaint()); 900 g2.fill(area); 901 bounds = area; 902 } 903 904 String label = marker.getLabel(); 905 RectangleAnchor anchor = marker.getLabelAnchor(); 906 if (label != null) { 907 Font labelFont = marker.getLabelFont(); 908 g2.setFont(labelFont); 909 g2.setPaint(marker.getLabelPaint()); 910 Point2D coordinates = calculateDomainMarkerTextAnchorPoint( 911 g2, orientation, dataArea, bounds, marker.getLabelOffset(), 912 marker.getLabelOffsetType(), anchor); 913 TextUtilities.drawAlignedString(label, g2, 914 (float) coordinates.getX(), (float) coordinates.getY(), 915 marker.getLabelTextAnchor()); 916 } 917 g2.setComposite(savedComposite); 918 } 919 920 /** 921 * Draws a marker for the range axis. 922 * 923 * @param g2 the graphics device (not <code>null</code>). 924 * @param plot the plot (not <code>null</code>). 925 * @param axis the range axis (not <code>null</code>). 926 * @param marker the marker to be drawn (not <code>null</code>). 927 * @param dataArea the area inside the axes (not <code>null</code>). 928 * 929 * @see #drawDomainMarker(Graphics2D, CategoryPlot, CategoryAxis, 930 * CategoryMarker, Rectangle2D) 931 */ 932 public void drawRangeMarker(Graphics2D g2, 933 CategoryPlot plot, 934 ValueAxis axis, 935 Marker marker, 936 Rectangle2D dataArea) { 937 938 if (marker instanceof ValueMarker) { 939 ValueMarker vm = (ValueMarker) marker; 940 double value = vm.getValue(); 941 Range range = axis.getRange(); 942 943 if (!range.contains(value)) { 944 return; 945 } 946 947 final Composite savedComposite = g2.getComposite(); 948 g2.setComposite(AlphaComposite.getInstance( 949 AlphaComposite.SRC_OVER, marker.getAlpha())); 950 951 PlotOrientation orientation = plot.getOrientation(); 952 double v = axis.valueToJava2D(value, dataArea, 953 plot.getRangeAxisEdge()); 954 Line2D line = null; 955 if (orientation == PlotOrientation.HORIZONTAL) { 956 line = new Line2D.Double(v, dataArea.getMinY(), v, 957 dataArea.getMaxY()); 958 } 959 else if (orientation == PlotOrientation.VERTICAL) { 960 line = new Line2D.Double(dataArea.getMinX(), v, 961 dataArea.getMaxX(), v); 962 } 963 964 g2.setPaint(marker.getPaint()); 965 g2.setStroke(marker.getStroke()); 966 g2.draw(line); 967 968 String label = marker.getLabel(); 969 RectangleAnchor anchor = marker.getLabelAnchor(); 970 if (label != null) { 971 Font labelFont = marker.getLabelFont(); 972 g2.setFont(labelFont); 973 g2.setPaint(marker.getLabelPaint()); 974 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 975 g2, orientation, dataArea, line.getBounds2D(), 976 marker.getLabelOffset(), LengthAdjustmentType.EXPAND, 977 anchor); 978 TextUtilities.drawAlignedString(label, g2, 979 (float) coordinates.getX(), (float) coordinates.getY(), 980 marker.getLabelTextAnchor()); 981 } 982 g2.setComposite(savedComposite); 983 } 984 else if (marker instanceof IntervalMarker) { 985 IntervalMarker im = (IntervalMarker) marker; 986 double start = im.getStartValue(); 987 double end = im.getEndValue(); 988 Range range = axis.getRange(); 989 if (!(range.intersects(start, end))) { 990 return; 991 } 992 993 final Composite savedComposite = g2.getComposite(); 994 g2.setComposite(AlphaComposite.getInstance( 995 AlphaComposite.SRC_OVER, marker.getAlpha())); 996 997 double start2d = axis.valueToJava2D(start, dataArea, 998 plot.getRangeAxisEdge()); 999 double end2d = axis.valueToJava2D(end, dataArea, 1000 plot.getRangeAxisEdge()); 1001 double low = Math.min(start2d, end2d); 1002 double high = Math.max(start2d, end2d); 1003 1004 PlotOrientation orientation = plot.getOrientation(); 1005 Rectangle2D rect = null; 1006 if (orientation == PlotOrientation.HORIZONTAL) { 1007 // clip left and right bounds to data area 1008 low = Math.max(low, dataArea.getMinX()); 1009 high = Math.min(high, dataArea.getMaxX()); 1010 rect = new Rectangle2D.Double(low, 1011 dataArea.getMinY(), high - low, 1012 dataArea.getHeight()); 1013 } 1014 else if (orientation == PlotOrientation.VERTICAL) { 1015 // clip top and bottom bounds to data area 1016 low = Math.max(low, dataArea.getMinY()); 1017 high = Math.min(high, dataArea.getMaxY()); 1018 rect = new Rectangle2D.Double(dataArea.getMinX(), 1019 low, dataArea.getWidth(), 1020 high - low); 1021 } 1022 Paint p = marker.getPaint(); 1023 if (p instanceof GradientPaint) { 1024 GradientPaint gp = (GradientPaint) p; 1025 GradientPaintTransformer t = im.getGradientPaintTransformer(); 1026 if (t != null) { 1027 gp = t.transform(gp, rect); 1028 } 1029 g2.setPaint(gp); 1030 } 1031 else { 1032 g2.setPaint(p); 1033 } 1034 g2.fill(rect); 1035 1036 // now draw the outlines, if visible... 1037 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { 1038 if (orientation == PlotOrientation.VERTICAL) { 1039 Line2D line = new Line2D.Double(); 1040 double x0 = dataArea.getMinX(); 1041 double x1 = dataArea.getMaxX(); 1042 g2.setPaint(im.getOutlinePaint()); 1043 g2.setStroke(im.getOutlineStroke()); 1044 if (range.contains(start)) { 1045 line.setLine(x0, start2d, x1, start2d); 1046 g2.draw(line); 1047 } 1048 if (range.contains(end)) { 1049 line.setLine(x0, end2d, x1, end2d); 1050 g2.draw(line); 1051 } 1052 } 1053 else { // PlotOrientation.HORIZONTAL 1054 Line2D line = new Line2D.Double(); 1055 double y0 = dataArea.getMinY(); 1056 double y1 = dataArea.getMaxY(); 1057 g2.setPaint(im.getOutlinePaint()); 1058 g2.setStroke(im.getOutlineStroke()); 1059 if (range.contains(start)) { 1060 line.setLine(start2d, y0, start2d, y1); 1061 g2.draw(line); 1062 } 1063 if (range.contains(end)) { 1064 line.setLine(end2d, y0, end2d, y1); 1065 g2.draw(line); 1066 } 1067 } 1068 } 1069 1070 String label = marker.getLabel(); 1071 RectangleAnchor anchor = marker.getLabelAnchor(); 1072 if (label != null) { 1073 Font labelFont = marker.getLabelFont(); 1074 g2.setFont(labelFont); 1075 g2.setPaint(marker.getLabelPaint()); 1076 Point2D coordinates = calculateRangeMarkerTextAnchorPoint( 1077 g2, orientation, dataArea, rect, 1078 marker.getLabelOffset(), marker.getLabelOffsetType(), 1079 anchor); 1080 TextUtilities.drawAlignedString(label, g2, 1081 (float) coordinates.getX(), (float) coordinates.getY(), 1082 marker.getLabelTextAnchor()); 1083 } 1084 g2.setComposite(savedComposite); 1085 } 1086 } 1087 1088 /** 1089 * Calculates the (x, y) coordinates for drawing the label for a marker on 1090 * the range axis. 1091 * 1092 * @param g2 the graphics device. 1093 * @param orientation the plot orientation. 1094 * @param dataArea the data area. 1095 * @param markerArea the rectangle surrounding the marker. 1096 * @param markerOffset the marker offset. 1097 * @param labelOffsetType the label offset type. 1098 * @param anchor the label anchor. 1099 * 1100 * @return The coordinates for drawing the marker label. 1101 */ 1102 protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2, 1103 PlotOrientation orientation, 1104 Rectangle2D dataArea, 1105 Rectangle2D markerArea, 1106 RectangleInsets markerOffset, 1107 LengthAdjustmentType labelOffsetType, 1108 RectangleAnchor anchor) { 1109 1110 Rectangle2D anchorRect = null; 1111 if (orientation == PlotOrientation.HORIZONTAL) { 1112 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1113 LengthAdjustmentType.CONTRACT, labelOffsetType); 1114 } 1115 else if (orientation == PlotOrientation.VERTICAL) { 1116 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1117 labelOffsetType, LengthAdjustmentType.CONTRACT); 1118 } 1119 return RectangleAnchor.coordinates(anchorRect, anchor); 1120 1121 } 1122 1123 /** 1124 * Calculates the (x, y) coordinates for drawing a marker label. 1125 * 1126 * @param g2 the graphics device. 1127 * @param orientation the plot orientation. 1128 * @param dataArea the data area. 1129 * @param markerArea the rectangle surrounding the marker. 1130 * @param markerOffset the marker offset. 1131 * @param labelOffsetType the label offset type. 1132 * @param anchor the label anchor. 1133 * 1134 * @return The coordinates for drawing the marker label. 1135 */ 1136 protected Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2, 1137 PlotOrientation orientation, 1138 Rectangle2D dataArea, 1139 Rectangle2D markerArea, 1140 RectangleInsets markerOffset, 1141 LengthAdjustmentType labelOffsetType, 1142 RectangleAnchor anchor) { 1143 1144 Rectangle2D anchorRect = null; 1145 if (orientation == PlotOrientation.HORIZONTAL) { 1146 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1147 labelOffsetType, LengthAdjustmentType.CONTRACT); 1148 } 1149 else if (orientation == PlotOrientation.VERTICAL) { 1150 anchorRect = markerOffset.createAdjustedRectangle(markerArea, 1151 LengthAdjustmentType.CONTRACT, labelOffsetType); 1152 } 1153 return RectangleAnchor.coordinates(anchorRect, anchor); 1154 1155 } 1156 1157 /** 1158 * Returns a legend item for a series. This default implementation will 1159 * return <code>null</code> if {@link #isSeriesVisible(int)} or 1160 * {@link #isSeriesVisibleInLegend(int)} returns <code>false</code>. 1161 * 1162 * @param datasetIndex the dataset index (zero-based). 1163 * @param series the series index (zero-based). 1164 * 1165 * @return The legend item (possibly <code>null</code>). 1166 * 1167 * @see #getLegendItems() 1168 */ 1169 public LegendItem getLegendItem(int datasetIndex, int series) { 1170 1171 CategoryPlot p = getPlot(); 1172 if (p == null) { 1173 return null; 1174 } 1175 1176 // check that a legend item needs to be displayed... 1177 if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { 1178 return null; 1179 } 1180 1181 CategoryDataset dataset = p.getDataset(datasetIndex); 1182 String label = this.legendItemLabelGenerator.generateLabel(dataset, 1183 series); 1184 String description = label; 1185 String toolTipText = null; 1186 if (this.legendItemToolTipGenerator != null) { 1187 toolTipText = this.legendItemToolTipGenerator.generateLabel( 1188 dataset, series); 1189 } 1190 String urlText = null; 1191 if (this.legendItemURLGenerator != null) { 1192 urlText = this.legendItemURLGenerator.generateLabel(dataset, 1193 series); 1194 } 1195 Shape shape = lookupLegendShape(series); 1196 Paint paint = lookupSeriesPaint(series); 1197 Paint outlinePaint = lookupSeriesOutlinePaint(series); 1198 Stroke outlineStroke = lookupSeriesOutlineStroke(series); 1199 1200 LegendItem item = new LegendItem(label, description, toolTipText, 1201 urlText, shape, paint, outlineStroke, outlinePaint); 1202 item.setLabelFont(lookupLegendTextFont(series)); 1203 Paint labelPaint = lookupLegendTextPaint(series); 1204 if (labelPaint != null) { 1205 item.setLabelPaint(labelPaint); 1206 } 1207 item.setSeriesKey(dataset.getRowKey(series)); 1208 item.setSeriesIndex(series); 1209 item.setDataset(dataset); 1210 item.setDatasetIndex(datasetIndex); 1211 return item; 1212 } 1213 1214 /** 1215 * Tests this renderer for equality with another object. 1216 * 1217 * @param obj the object. 1218 * 1219 * @return <code>true</code> or <code>false</code>. 1220 */ 1221 public boolean equals(Object obj) { 1222 1223 if (obj == this) { 1224 return true; 1225 } 1226 if (!(obj instanceof AbstractCategoryItemRenderer)) { 1227 return false; 1228 } 1229 AbstractCategoryItemRenderer that = (AbstractCategoryItemRenderer) obj; 1230 1231 if (!ObjectUtilities.equal(this.itemLabelGenerator, 1232 that.itemLabelGenerator)) { 1233 return false; 1234 } 1235 if (!ObjectUtilities.equal(this.itemLabelGeneratorList, 1236 that.itemLabelGeneratorList)) { 1237 return false; 1238 } 1239 if (!ObjectUtilities.equal(this.baseItemLabelGenerator, 1240 that.baseItemLabelGenerator)) { 1241 return false; 1242 } 1243 if (!ObjectUtilities.equal(this.toolTipGenerator, 1244 that.toolTipGenerator)) { 1245 return false; 1246 } 1247 if (!ObjectUtilities.equal(this.toolTipGeneratorList, 1248 that.toolTipGeneratorList)) { 1249 return false; 1250 } 1251 if (!ObjectUtilities.equal(this.baseToolTipGenerator, 1252 that.baseToolTipGenerator)) { 1253 return false; 1254 } 1255 if (!ObjectUtilities.equal(this.itemURLGenerator, 1256 that.itemURLGenerator)) { 1257 return false; 1258 } 1259 if (!ObjectUtilities.equal(this.itemURLGeneratorList, 1260 that.itemURLGeneratorList)) { 1261 return false; 1262 } 1263 if (!ObjectUtilities.equal(this.baseItemURLGenerator, 1264 that.baseItemURLGenerator)) { 1265 return false; 1266 } 1267 if (!ObjectUtilities.equal(this.legendItemLabelGenerator, 1268 that.legendItemLabelGenerator)) { 1269 return false; 1270 } 1271 if (!ObjectUtilities.equal(this.legendItemToolTipGenerator, 1272 that.legendItemToolTipGenerator)) { 1273 return false; 1274 } 1275 if (!ObjectUtilities.equal(this.legendItemURLGenerator, 1276 that.legendItemURLGenerator)) { 1277 return false; 1278 } 1279 return super.equals(obj); 1280 } 1281 1282 /** 1283 * Returns a hash code for the renderer. 1284 * 1285 * @return The hash code. 1286 */ 1287 public int hashCode() { 1288 int result = super.hashCode(); 1289 return result; 1290 } 1291 1292 /** 1293 * Returns the drawing supplier from the plot. 1294 * 1295 * @return The drawing supplier (possibly <code>null</code>). 1296 */ 1297 public DrawingSupplier getDrawingSupplier() { 1298 DrawingSupplier result = null; 1299 CategoryPlot cp = getPlot(); 1300 if (cp != null) { 1301 result = cp.getDrawingSupplier(); 1302 } 1303 return result; 1304 } 1305 1306 /** 1307 * Considers the current (x, y) coordinate and updates the crosshair point 1308 * if it meets the criteria (usually means the (x, y) coordinate is the 1309 * closest to the anchor point so far). 1310 * 1311 * @param crosshairState the crosshair state (<code>null</code> permitted, 1312 * but the method does nothing in that case). 1313 * @param rowKey the row key. 1314 * @param columnKey the column key. 1315 * @param value the data value. 1316 * @param datasetIndex the dataset index. 1317 * @param transX the x-value translated to Java2D space. 1318 * @param transY the y-value translated to Java2D space. 1319 * @param orientation the plot orientation (<code>null</code> not 1320 * permitted). 1321 * 1322 * @since 1.0.11 1323 */ 1324 protected void updateCrosshairValues(CategoryCrosshairState crosshairState, 1325 Comparable rowKey, Comparable columnKey, double value, 1326 int datasetIndex, 1327 double transX, double transY, PlotOrientation orientation) { 1328 1329 if (orientation == null) { 1330 throw new IllegalArgumentException("Null 'orientation' argument."); 1331 } 1332 1333 if (crosshairState != null) { 1334 if (this.plot.isRangeCrosshairLockedOnData()) { 1335 // both axes 1336 crosshairState.updateCrosshairPoint(rowKey, columnKey, value, 1337 datasetIndex, transX, transY, orientation); 1338 } 1339 else { 1340 crosshairState.updateCrosshairX(rowKey, columnKey, 1341 datasetIndex, transX, orientation); 1342 } 1343 } 1344 } 1345 1346 /** 1347 * Draws an item label. 1348 * 1349 * @param g2 the graphics device. 1350 * @param orientation the orientation. 1351 * @param dataset the dataset. 1352 * @param row the row. 1353 * @param column the column. 1354 * @param x the x coordinate (in Java2D space). 1355 * @param y the y coordinate (in Java2D space). 1356 * @param negative indicates a negative value (which affects the item 1357 * label position). 1358 */ 1359 protected void drawItemLabel(Graphics2D g2, 1360 PlotOrientation orientation, 1361 CategoryDataset dataset, 1362 int row, int column, 1363 double x, double y, 1364 boolean negative) { 1365 1366 CategoryItemLabelGenerator generator 1367 = getItemLabelGenerator(row, column); 1368 if (generator != null) { 1369 Font labelFont = getItemLabelFont(row, column); 1370 Paint paint = getItemLabelPaint(row, column); 1371 g2.setFont(labelFont); 1372 g2.setPaint(paint); 1373 String label = generator.generateLabel(dataset, row, column); 1374 ItemLabelPosition position = null; 1375 if (!negative) { 1376 position = getPositiveItemLabelPosition(row, column); 1377 } 1378 else { 1379 position = getNegativeItemLabelPosition(row, column); 1380 } 1381 Point2D anchorPoint = calculateLabelAnchorPoint( 1382 position.getItemLabelAnchor(), x, y, orientation); 1383 TextUtilities.drawRotatedString(label, g2, 1384 (float) anchorPoint.getX(), (float) anchorPoint.getY(), 1385 position.getTextAnchor(), 1386 position.getAngle(), position.getRotationAnchor()); 1387 } 1388 1389 } 1390 1391 /** 1392 * Returns an independent copy of the renderer. The <code>plot</code> 1393 * reference is shallow copied. 1394 * 1395 * @return A clone. 1396 * 1397 * @throws CloneNotSupportedException can be thrown if one of the objects 1398 * belonging to the renderer does not support cloning (for example, 1399 * an item label generator). 1400 */ 1401 public Object clone() throws CloneNotSupportedException { 1402 1403 AbstractCategoryItemRenderer clone 1404 = (AbstractCategoryItemRenderer) super.clone(); 1405 1406 if (this.itemLabelGenerator != null) { 1407 if (this.itemLabelGenerator instanceof PublicCloneable) { 1408 PublicCloneable pc = (PublicCloneable) this.itemLabelGenerator; 1409 clone.itemLabelGenerator 1410 = (CategoryItemLabelGenerator) pc.clone(); 1411 } 1412 else { 1413 throw new CloneNotSupportedException( 1414 "ItemLabelGenerator not cloneable."); 1415 } 1416 } 1417 1418 if (this.itemLabelGeneratorList != null) { 1419 clone.itemLabelGeneratorList 1420 = (ObjectList) this.itemLabelGeneratorList.clone(); 1421 } 1422 1423 if (this.baseItemLabelGenerator != null) { 1424 if (this.baseItemLabelGenerator instanceof PublicCloneable) { 1425 PublicCloneable pc 1426 = (PublicCloneable) this.baseItemLabelGenerator; 1427 clone.baseItemLabelGenerator 1428 = (CategoryItemLabelGenerator) pc.clone(); 1429 } 1430 else { 1431 throw new CloneNotSupportedException( 1432 "ItemLabelGenerator not cloneable."); 1433 } 1434 } 1435 1436 if (this.toolTipGenerator != null) { 1437 if (this.toolTipGenerator instanceof PublicCloneable) { 1438 PublicCloneable pc = (PublicCloneable) this.toolTipGenerator; 1439 clone.toolTipGenerator = (CategoryToolTipGenerator) pc.clone(); 1440 } 1441 else { 1442 throw new CloneNotSupportedException( 1443 "Tool tip generator not cloneable."); 1444 } 1445 } 1446 1447 if (this.toolTipGeneratorList != null) { 1448 clone.toolTipGeneratorList 1449 = (ObjectList) this.toolTipGeneratorList.clone(); 1450 } 1451 1452 if (this.baseToolTipGenerator != null) { 1453 if (this.baseToolTipGenerator instanceof PublicCloneable) { 1454 PublicCloneable pc 1455 = (PublicCloneable) this.baseToolTipGenerator; 1456 clone.baseToolTipGenerator 1457 = (CategoryToolTipGenerator) pc.clone(); 1458 } 1459 else { 1460 throw new CloneNotSupportedException( 1461 "Base tool tip generator not cloneable."); 1462 } 1463 } 1464 1465 if (this.itemURLGenerator != null) { 1466 if (this.itemURLGenerator instanceof PublicCloneable) { 1467 PublicCloneable pc = (PublicCloneable) this.itemURLGenerator; 1468 clone.itemURLGenerator = (CategoryURLGenerator) pc.clone(); 1469 } 1470 else { 1471 throw new CloneNotSupportedException( 1472 "Item URL generator not cloneable."); 1473 } 1474 } 1475 1476 if (this.itemURLGeneratorList != null) { 1477 clone.itemURLGeneratorList 1478 = (ObjectList) this.itemURLGeneratorList.clone(); 1479 } 1480 1481 if (this.baseItemURLGenerator != null) { 1482 if (this.baseItemURLGenerator instanceof PublicCloneable) { 1483 PublicCloneable pc 1484 = (PublicCloneable) this.baseItemURLGenerator; 1485 clone.baseItemURLGenerator = (CategoryURLGenerator) pc.clone(); 1486 } 1487 else { 1488 throw new CloneNotSupportedException( 1489 "Base item URL generator not cloneable."); 1490 } 1491 } 1492 1493 if (this.legendItemLabelGenerator instanceof PublicCloneable) { 1494 clone.legendItemLabelGenerator = (CategorySeriesLabelGenerator) 1495 ObjectUtilities.clone(this.legendItemLabelGenerator); 1496 } 1497 if (this.legendItemToolTipGenerator instanceof PublicCloneable) { 1498 clone.legendItemToolTipGenerator = (CategorySeriesLabelGenerator) 1499 ObjectUtilities.clone(this.legendItemToolTipGenerator); 1500 } 1501 if (this.legendItemURLGenerator instanceof PublicCloneable) { 1502 clone.legendItemURLGenerator = (CategorySeriesLabelGenerator) 1503 ObjectUtilities.clone(this.legendItemURLGenerator); 1504 } 1505 return clone; 1506 } 1507 1508 /** 1509 * Returns a domain axis for a plot. 1510 * 1511 * @param plot the plot. 1512 * @param index the axis index. 1513 * 1514 * @return A domain axis. 1515 */ 1516 protected CategoryAxis getDomainAxis(CategoryPlot plot, int index) { 1517 CategoryAxis result = plot.getDomainAxis(index); 1518 if (result == null) { 1519 result = plot.getDomainAxis(); 1520 } 1521 return result; 1522 } 1523 1524 /** 1525 * Returns a range axis for a plot. 1526 * 1527 * @param plot the plot. 1528 * @param index the axis index. 1529 * 1530 * @return A range axis. 1531 */ 1532 protected ValueAxis getRangeAxis(CategoryPlot plot, int index) { 1533 ValueAxis result = plot.getRangeAxis(index); 1534 if (result == null) { 1535 result = plot.getRangeAxis(); 1536 } 1537 return result; 1538 } 1539 1540 /** 1541 * Returns a (possibly empty) collection of legend items for the series 1542 * that this renderer is responsible for drawing. 1543 * 1544 * @return The legend item collection (never <code>null</code>). 1545 * 1546 * @see #getLegendItem(int, int) 1547 */ 1548 public LegendItemCollection getLegendItems() { 1549 if (this.plot == null) { 1550 return new LegendItemCollection(); 1551 } 1552 LegendItemCollection result = new LegendItemCollection(); 1553 int index = this.plot.getIndexOf(this); 1554 CategoryDataset dataset = this.plot.getDataset(index); 1555 if (dataset != null) { 1556 int seriesCount = dataset.getRowCount(); 1557 for (int i = 0; i < seriesCount; i++) { 1558 if (isSeriesVisibleInLegend(i)) { 1559 LegendItem item = getLegendItem(index, i); 1560 if (item != null) { 1561 result.add(item); 1562 } 1563 } 1564 } 1565 1566 } 1567 return result; 1568 } 1569 1570 /** 1571 * Returns the legend item label generator. 1572 * 1573 * @return The label generator (never <code>null</code>). 1574 * 1575 * @see #setLegendItemLabelGenerator(CategorySeriesLabelGenerator) 1576 */ 1577 public CategorySeriesLabelGenerator getLegendItemLabelGenerator() { 1578 return this.legendItemLabelGenerator; 1579 } 1580 1581 /** 1582 * Sets the legend item label generator and sends a 1583 * {@link RendererChangeEvent} to all registered listeners. 1584 * 1585 * @param generator the generator (<code>null</code> not permitted). 1586 * 1587 * @see #getLegendItemLabelGenerator() 1588 */ 1589 public void setLegendItemLabelGenerator( 1590 CategorySeriesLabelGenerator generator) { 1591 if (generator == null) { 1592 throw new IllegalArgumentException("Null 'generator' argument."); 1593 } 1594 this.legendItemLabelGenerator = generator; 1595 fireChangeEvent(); 1596 } 1597 1598 /** 1599 * Returns the legend item tool tip generator. 1600 * 1601 * @return The tool tip generator (possibly <code>null</code>). 1602 * 1603 * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator) 1604 */ 1605 public CategorySeriesLabelGenerator getLegendItemToolTipGenerator() { 1606 return this.legendItemToolTipGenerator; 1607 } 1608 1609 /** 1610 * Sets the legend item tool tip generator and sends a 1611 * {@link RendererChangeEvent} to all registered listeners. 1612 * 1613 * @param generator the generator (<code>null</code> permitted). 1614 * 1615 * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator) 1616 */ 1617 public void setLegendItemToolTipGenerator( 1618 CategorySeriesLabelGenerator generator) { 1619 this.legendItemToolTipGenerator = generator; 1620 fireChangeEvent(); 1621 } 1622 1623 /** 1624 * Returns the legend item URL generator. 1625 * 1626 * @return The URL generator (possibly <code>null</code>). 1627 * 1628 * @see #setLegendItemURLGenerator(CategorySeriesLabelGenerator) 1629 */ 1630 public CategorySeriesLabelGenerator getLegendItemURLGenerator() { 1631 return this.legendItemURLGenerator; 1632 } 1633 1634 /** 1635 * Sets the legend item URL generator and sends a 1636 * {@link RendererChangeEvent} to all registered listeners. 1637 * 1638 * @param generator the generator (<code>null</code> permitted). 1639 * 1640 * @see #getLegendItemURLGenerator() 1641 */ 1642 public void setLegendItemURLGenerator( 1643 CategorySeriesLabelGenerator generator) { 1644 this.legendItemURLGenerator = generator; 1645 fireChangeEvent(); 1646 } 1647 1648 /** 1649 * Adds an entity with the specified hotspot. 1650 * 1651 * @param entities the entity collection. 1652 * @param dataset the dataset. 1653 * @param row the row index. 1654 * @param column the column index. 1655 * @param hotspot the hotspot. 1656 */ 1657 protected void addItemEntity(EntityCollection entities, 1658 CategoryDataset dataset, int row, int column, 1659 Shape hotspot) { 1660 1661 String tip = null; 1662 CategoryToolTipGenerator tipster = getToolTipGenerator(row, column); 1663 if (tipster != null) { 1664 tip = tipster.generateToolTip(dataset, row, column); 1665 } 1666 String url = null; 1667 CategoryURLGenerator urlster = getItemURLGenerator(row, column); 1668 if (urlster != null) { 1669 url = urlster.generateURL(dataset, row, column); 1670 } 1671 CategoryItemEntity entity = new CategoryItemEntity(hotspot, tip, url, 1672 dataset, dataset.getRowKey(row), dataset.getColumnKey(column)); 1673 entities.add(entity); 1674 1675 } 1676 1677 }