001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * --------- 028 * Plot.java 029 * --------- 030 * (C) Copyright 2000-2013, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): Sylvain Vieujot; 034 * Jeremy Bowman; 035 * Andreas Schneider; 036 * Gideon Krause; 037 * Nicolas Brodu; 038 * Michal Krause; 039 * Richard West, Advanced Micro Devices, Inc.; 040 * Peter Kolb - patches 2603321, 2809117; 041 * 042 * Changes 043 * ------- 044 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG); 045 * 18-Sep-2001 : Updated header info and fixed DOS encoding problem (DG); 046 * 19-Oct-2001 : Moved series paint and stroke methods from JFreeChart 047 * class (DG); 048 * 23-Oct-2001 : Created renderer for LinePlot class (DG); 049 * 07-Nov-2001 : Changed type names for ChartChangeEvent (DG); 050 * Tidied up some Javadoc comments (DG); 051 * 13-Nov-2001 : Changes to allow for null axes on plots such as PiePlot (DG); 052 * Added plot/axis compatibility checks (DG); 053 * 12-Dec-2001 : Changed constructors to protected, and removed unnecessary 054 * 'throws' clauses (DG); 055 * 13-Dec-2001 : Added tooltips (DG); 056 * 22-Jan-2002 : Added handleClick() method, as part of implementation for 057 * crosshairs (DG); 058 * Moved tooltips reference into ChartInfo class (DG); 059 * 23-Jan-2002 : Added test for null axes in chartChanged() method, thanks 060 * to Barry Evans for the bug report (number 506979 on 061 * SourceForge) (DG); 062 * Added a zoom() method (DG); 063 * 05-Feb-2002 : Updated setBackgroundPaint(), setOutlineStroke() and 064 * setOutlinePaint() to better handle null values, as suggested 065 * by Sylvain Vieujot (DG); 066 * 06-Feb-2002 : Added background image, plus alpha transparency for background 067 * and foreground (DG); 068 * 06-Mar-2002 : Added AxisConstants interface (DG); 069 * 26-Mar-2002 : Changed zoom method from empty to abstract (DG); 070 * 23-Apr-2002 : Moved dataset from JFreeChart class (DG); 071 * 11-May-2002 : Added ShapeFactory interface for getShape() methods, 072 * contributed by Jeremy Bowman (DG); 073 * 28-May-2002 : Fixed bug in setSeriesPaint(int, Paint) for subplots (AS); 074 * 25-Jun-2002 : Removed redundant imports (DG); 075 * 30-Jul-2002 : Added 'no data' message for charts with null or empty 076 * datasets (DG); 077 * 21-Aug-2002 : Added code to extend series array if necessary (refer to 078 * SourceForge bug id 594547 for details) (DG); 079 * 17-Sep-2002 : Fixed bug in getSeriesOutlineStroke() method, reported by 080 * Andreas Schroeder (DG); 081 * 23-Sep-2002 : Added getLegendItems() abstract method (DG); 082 * 24-Sep-2002 : Removed firstSeriesIndex, subplots now use their own paint 083 * settings, there is a new mechanism for the legend to collect 084 * the legend items (DG); 085 * 27-Sep-2002 : Added dataset group (DG); 086 * 14-Oct-2002 : Moved listener storage into EventListenerList. Changed some 087 * abstract methods to empty implementations (DG); 088 * 28-Oct-2002 : Added a getBackgroundImage() method (DG); 089 * 21-Nov-2002 : Added a plot index for identifying subplots in combined and 090 * overlaid charts (DG); 091 * 22-Nov-2002 : Changed all attributes from 'protected' to 'private'. Added 092 * dataAreaRatio attribute from David M O'Donnell's code (DG); 093 * 09-Jan-2003 : Integrated fix for plot border contributed by Gideon 094 * Krause (DG); 095 * 17-Jan-2003 : Moved to com.jrefinery.chart.plot (DG); 096 * 23-Jan-2003 : Removed one constructor (DG); 097 * 26-Mar-2003 : Implemented Serializable (DG); 098 * 14-Jul-2003 : Moved the dataset and secondaryDataset attributes to the 099 * CategoryPlot and XYPlot classes (DG); 100 * 21-Jul-2003 : Moved DrawingSupplier from CategoryPlot and XYPlot up to this 101 * class (DG); 102 * 20-Aug-2003 : Implemented Cloneable (DG); 103 * 11-Sep-2003 : Listeners and clone (NB); 104 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG); 105 * 03-Dec-2003 : Modified draw method to accept anchor (DG); 106 * 12-Mar-2004 : Fixed clipping bug in drawNoDataMessage() method (DG); 107 * 07-Apr-2004 : Modified string bounds calculation (DG); 108 * 04-Nov-2004 : Added default shapes for legend items (DG); 109 * 25-Nov-2004 : Some changes to the clone() method implementation (DG); 110 * 23-Feb-2005 : Implemented new LegendItemSource interface (and also 111 * PublicCloneable) (DG); 112 * 21-Apr-2005 : Replaced Insets with RectangleInsets (DG); 113 * 05-May-2005 : Removed unused draw() method (DG); 114 * 06-Jun-2005 : Fixed bugs in equals() method (DG); 115 * 01-Sep-2005 : Moved dataAreaRatio from here to ContourPlot (DG); 116 * ------------- JFREECHART 1.0.x --------------------------------------------- 117 * 30-Jun-2006 : Added background image alpha - see bug report 1514904 (DG); 118 * 05-Sep-2006 : Implemented the MarkerChangeListener interface (DG); 119 * 11-Jan-2007 : Added some argument checks, event notifications, and many 120 * API doc updates (DG); 121 * 03-Apr-2007 : Made drawBackgroundImage() public (DG); 122 * 07-Jun-2007 : Added new fillBackground() method to handle GradientPaint 123 * taking into account orientation (DG); 124 * 25-Mar-2008 : Added fireChangeEvent() method - see patch 1914411 (DG); 125 * 15-Aug-2008 : Added setDrawingSupplier() method with notify flag (DG); 126 * 13-Jan-2009 : Added notify flag (DG); 127 * 19-Mar-2009 : Added entity support - see patch 2603321 by Peter Kolb (DG); 128 * 24-Jun-2009 : Implemented AnnotationChangeListener (see patch 2809117 by 129 * PK) (DG); 130 * 13-Jul-2009 : Plot background image should be clipped if necessary (DG); 131 * 02-Jul-2013 : Use ParamChecks (DG); 132 * 29-Jul-2014 : Add hint to normalise stroke for plot border (DG); 133 * 134 */ 135 136package org.jfree.chart.plot; 137 138import java.awt.AlphaComposite; 139import java.awt.BasicStroke; 140import java.awt.Color; 141import java.awt.Composite; 142import java.awt.Font; 143import java.awt.GradientPaint; 144import java.awt.Graphics2D; 145import java.awt.Image; 146import java.awt.Paint; 147import java.awt.RenderingHints; 148import java.awt.Shape; 149import java.awt.Stroke; 150import java.awt.geom.Ellipse2D; 151import java.awt.geom.Point2D; 152import java.awt.geom.Rectangle2D; 153import java.io.IOException; 154import java.io.ObjectInputStream; 155import java.io.ObjectOutputStream; 156import java.io.Serializable; 157 158import javax.swing.event.EventListenerList; 159 160import org.jfree.chart.JFreeChart; 161import org.jfree.chart.LegendItemCollection; 162import org.jfree.chart.LegendItemSource; 163import org.jfree.chart.annotations.Annotation; 164import org.jfree.chart.axis.AxisLocation; 165import org.jfree.chart.entity.EntityCollection; 166import org.jfree.chart.entity.PlotEntity; 167import org.jfree.chart.event.AnnotationChangeEvent; 168import org.jfree.chart.event.AnnotationChangeListener; 169import org.jfree.chart.event.AxisChangeEvent; 170import org.jfree.chart.event.AxisChangeListener; 171import org.jfree.chart.event.ChartChangeEventType; 172import org.jfree.chart.event.MarkerChangeEvent; 173import org.jfree.chart.event.MarkerChangeListener; 174import org.jfree.chart.event.PlotChangeEvent; 175import org.jfree.chart.event.PlotChangeListener; 176import org.jfree.chart.util.ParamChecks; 177import org.jfree.data.general.DatasetChangeEvent; 178import org.jfree.data.general.DatasetChangeListener; 179import org.jfree.data.general.DatasetGroup; 180import org.jfree.io.SerialUtilities; 181import org.jfree.text.G2TextMeasurer; 182import org.jfree.text.TextBlock; 183import org.jfree.text.TextBlockAnchor; 184import org.jfree.text.TextUtilities; 185import org.jfree.ui.Align; 186import org.jfree.ui.RectangleEdge; 187import org.jfree.ui.RectangleInsets; 188import org.jfree.util.ObjectUtilities; 189import org.jfree.util.PaintUtilities; 190import org.jfree.util.PublicCloneable; 191 192/** 193 * The base class for all plots in JFreeChart. The {@link JFreeChart} class 194 * delegates the drawing of axes and data to the plot. This base class 195 * provides facilities common to most plot types. 196 */ 197public abstract class Plot implements AxisChangeListener, 198 DatasetChangeListener, AnnotationChangeListener, MarkerChangeListener, 199 LegendItemSource, PublicCloneable, Cloneable, Serializable { 200 201 /** For serialization. */ 202 private static final long serialVersionUID = -8831571430103671324L; 203 204 /** Useful constant representing zero. */ 205 public static final Number ZERO = new Integer(0); 206 207 /** The default insets. */ 208 public static final RectangleInsets DEFAULT_INSETS 209 = new RectangleInsets(4.0, 8.0, 4.0, 8.0); 210 211 /** The default outline stroke. */ 212 public static final Stroke DEFAULT_OUTLINE_STROKE = new BasicStroke(0.5f, 213 BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); 214 215 /** The default outline color. */ 216 public static final Paint DEFAULT_OUTLINE_PAINT = Color.gray; 217 218 /** The default foreground alpha transparency. */ 219 public static final float DEFAULT_FOREGROUND_ALPHA = 1.0f; 220 221 /** The default background alpha transparency. */ 222 public static final float DEFAULT_BACKGROUND_ALPHA = 1.0f; 223 224 /** The default background color. */ 225 public static final Paint DEFAULT_BACKGROUND_PAINT = Color.white; 226 227 /** The minimum width at which the plot should be drawn. */ 228 public static final int MINIMUM_WIDTH_TO_DRAW = 10; 229 230 /** The minimum height at which the plot should be drawn. */ 231 public static final int MINIMUM_HEIGHT_TO_DRAW = 10; 232 233 /** A default box shape for legend items. */ 234 public static final Shape DEFAULT_LEGEND_ITEM_BOX 235 = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0); 236 237 /** A default circle shape for legend items. */ 238 public static final Shape DEFAULT_LEGEND_ITEM_CIRCLE 239 = new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0); 240 241 /** The parent plot (<code>null</code> if this is the root plot). */ 242 private Plot parent; 243 244 /** The dataset group (to be used for thread synchronisation). */ 245 private DatasetGroup datasetGroup; 246 247 /** The message to display if no data is available. */ 248 private String noDataMessage; 249 250 /** The font used to display the 'no data' message. */ 251 private Font noDataMessageFont; 252 253 /** The paint used to draw the 'no data' message. */ 254 private transient Paint noDataMessagePaint; 255 256 /** Amount of blank space around the plot area. */ 257 private RectangleInsets insets; 258 259 /** 260 * A flag that controls whether or not the plot outline is drawn. 261 * 262 * @since 1.0.6 263 */ 264 private boolean outlineVisible; 265 266 /** The Stroke used to draw an outline around the plot. */ 267 private transient Stroke outlineStroke; 268 269 /** The Paint used to draw an outline around the plot. */ 270 private transient Paint outlinePaint; 271 272 /** An optional color used to fill the plot background. */ 273 private transient Paint backgroundPaint; 274 275 /** An optional image for the plot background. */ 276 private transient Image backgroundImage; // not currently serialized 277 278 /** The alignment for the background image. */ 279 private int backgroundImageAlignment = Align.FIT; 280 281 /** The alpha value used to draw the background image. */ 282 private float backgroundImageAlpha = 0.5f; 283 284 /** The alpha-transparency for the plot. */ 285 private float foregroundAlpha; 286 287 /** The alpha transparency for the background paint. */ 288 private float backgroundAlpha; 289 290 /** The drawing supplier. */ 291 private DrawingSupplier drawingSupplier; 292 293 /** Storage for registered change listeners. */ 294 private transient EventListenerList listenerList; 295 296 /** 297 * A flag that controls whether or not the plot will notify listeners 298 * of changes (defaults to true, but sometimes it is useful to disable 299 * this). 300 * 301 * @since 1.0.13 302 */ 303 private boolean notify; 304 305 /** 306 * Creates a new plot. 307 */ 308 protected Plot() { 309 310 this.parent = null; 311 this.insets = DEFAULT_INSETS; 312 this.backgroundPaint = DEFAULT_BACKGROUND_PAINT; 313 this.backgroundAlpha = DEFAULT_BACKGROUND_ALPHA; 314 this.backgroundImage = null; 315 this.outlineVisible = true; 316 this.outlineStroke = DEFAULT_OUTLINE_STROKE; 317 this.outlinePaint = DEFAULT_OUTLINE_PAINT; 318 this.foregroundAlpha = DEFAULT_FOREGROUND_ALPHA; 319 320 this.noDataMessage = null; 321 this.noDataMessageFont = new Font("SansSerif", Font.PLAIN, 12); 322 this.noDataMessagePaint = Color.black; 323 324 this.drawingSupplier = new DefaultDrawingSupplier(); 325 326 this.notify = true; 327 this.listenerList = new EventListenerList(); 328 329 } 330 331 /** 332 * Returns the dataset group for the plot (not currently used). 333 * 334 * @return The dataset group. 335 * 336 * @see #setDatasetGroup(DatasetGroup) 337 */ 338 public DatasetGroup getDatasetGroup() { 339 return this.datasetGroup; 340 } 341 342 /** 343 * Sets the dataset group (not currently used). 344 * 345 * @param group the dataset group (<code>null</code> permitted). 346 * 347 * @see #getDatasetGroup() 348 */ 349 protected void setDatasetGroup(DatasetGroup group) { 350 this.datasetGroup = group; 351 } 352 353 /** 354 * Returns the string that is displayed when the dataset is empty or 355 * <code>null</code>. 356 * 357 * @return The 'no data' message (<code>null</code> possible). 358 * 359 * @see #setNoDataMessage(String) 360 * @see #getNoDataMessageFont() 361 * @see #getNoDataMessagePaint() 362 */ 363 public String getNoDataMessage() { 364 return this.noDataMessage; 365 } 366 367 /** 368 * Sets the message that is displayed when the dataset is empty or 369 * <code>null</code>, and sends a {@link PlotChangeEvent} to all registered 370 * listeners. 371 * 372 * @param message the message (<code>null</code> permitted). 373 * 374 * @see #getNoDataMessage() 375 */ 376 public void setNoDataMessage(String message) { 377 this.noDataMessage = message; 378 fireChangeEvent(); 379 } 380 381 /** 382 * Returns the font used to display the 'no data' message. 383 * 384 * @return The font (never <code>null</code>). 385 * 386 * @see #setNoDataMessageFont(Font) 387 * @see #getNoDataMessage() 388 */ 389 public Font getNoDataMessageFont() { 390 return this.noDataMessageFont; 391 } 392 393 /** 394 * Sets the font used to display the 'no data' message and sends a 395 * {@link PlotChangeEvent} to all registered listeners. 396 * 397 * @param font the font (<code>null</code> not permitted). 398 * 399 * @see #getNoDataMessageFont() 400 */ 401 public void setNoDataMessageFont(Font font) { 402 ParamChecks.nullNotPermitted(font, "font"); 403 this.noDataMessageFont = font; 404 fireChangeEvent(); 405 } 406 407 /** 408 * Returns the paint used to display the 'no data' message. 409 * 410 * @return The paint (never <code>null</code>). 411 * 412 * @see #setNoDataMessagePaint(Paint) 413 * @see #getNoDataMessage() 414 */ 415 public Paint getNoDataMessagePaint() { 416 return this.noDataMessagePaint; 417 } 418 419 /** 420 * Sets the paint used to display the 'no data' message and sends a 421 * {@link PlotChangeEvent} to all registered listeners. 422 * 423 * @param paint the paint (<code>null</code> not permitted). 424 * 425 * @see #getNoDataMessagePaint() 426 */ 427 public void setNoDataMessagePaint(Paint paint) { 428 ParamChecks.nullNotPermitted(paint, "paint"); 429 this.noDataMessagePaint = paint; 430 fireChangeEvent(); 431 } 432 433 /** 434 * Returns a short string describing the plot type. 435 * <P> 436 * Note: this gets used in the chart property editing user interface, 437 * but there needs to be a better mechanism for identifying the plot type. 438 * 439 * @return A short string describing the plot type (never 440 * <code>null</code>). 441 */ 442 public abstract String getPlotType(); 443 444 /** 445 * Returns the parent plot (or <code>null</code> if this plot is not part 446 * of a combined plot). 447 * 448 * @return The parent plot. 449 * 450 * @see #setParent(Plot) 451 * @see #getRootPlot() 452 */ 453 public Plot getParent() { 454 return this.parent; 455 } 456 457 /** 458 * Sets the parent plot. This method is intended for internal use, you 459 * shouldn't need to call it directly. 460 * 461 * @param parent the parent plot (<code>null</code> permitted). 462 * 463 * @see #getParent() 464 */ 465 public void setParent(Plot parent) { 466 this.parent = parent; 467 } 468 469 /** 470 * Returns the root plot. 471 * 472 * @return The root plot. 473 * 474 * @see #getParent() 475 */ 476 public Plot getRootPlot() { 477 478 Plot p = getParent(); 479 if (p == null) { 480 return this; 481 } 482 return p.getRootPlot(); 483 484 } 485 486 /** 487 * Returns <code>true</code> if this plot is part of a combined plot 488 * structure (that is, {@link #getParent()} returns a non-<code>null</code> 489 * value), and <code>false</code> otherwise. 490 * 491 * @return <code>true</code> if this plot is part of a combined plot 492 * structure. 493 * 494 * @see #getParent() 495 */ 496 public boolean isSubplot() { 497 return (getParent() != null); 498 } 499 500 /** 501 * Returns the insets for the plot area. 502 * 503 * @return The insets (never <code>null</code>). 504 * 505 * @see #setInsets(RectangleInsets) 506 */ 507 public RectangleInsets getInsets() { 508 return this.insets; 509 } 510 511 /** 512 * Sets the insets for the plot and sends a {@link PlotChangeEvent} to 513 * all registered listeners. 514 * 515 * @param insets the new insets (<code>null</code> not permitted). 516 * 517 * @see #getInsets() 518 * @see #setInsets(RectangleInsets, boolean) 519 */ 520 public void setInsets(RectangleInsets insets) { 521 setInsets(insets, true); 522 } 523 524 /** 525 * Sets the insets for the plot and, if requested, and sends a 526 * {@link PlotChangeEvent} to all registered listeners. 527 * 528 * @param insets the new insets (<code>null</code> not permitted). 529 * @param notify a flag that controls whether the registered listeners are 530 * notified. 531 * 532 * @see #getInsets() 533 * @see #setInsets(RectangleInsets) 534 */ 535 public void setInsets(RectangleInsets insets, boolean notify) { 536 ParamChecks.nullNotPermitted(insets, "insets"); 537 if (!this.insets.equals(insets)) { 538 this.insets = insets; 539 if (notify) { 540 fireChangeEvent(); 541 } 542 } 543 544 } 545 546 /** 547 * Returns the background color of the plot area. 548 * 549 * @return The paint (possibly <code>null</code>). 550 * 551 * @see #setBackgroundPaint(Paint) 552 */ 553 public Paint getBackgroundPaint() { 554 return this.backgroundPaint; 555 } 556 557 /** 558 * Sets the background color of the plot area and sends a 559 * {@link PlotChangeEvent} to all registered listeners. 560 * 561 * @param paint the paint (<code>null</code> permitted). 562 * 563 * @see #getBackgroundPaint() 564 */ 565 public void setBackgroundPaint(Paint paint) { 566 567 if (paint == null) { 568 if (this.backgroundPaint != null) { 569 this.backgroundPaint = null; 570 fireChangeEvent(); 571 } 572 } 573 else { 574 if (this.backgroundPaint != null) { 575 if (this.backgroundPaint.equals(paint)) { 576 return; // nothing to do 577 } 578 } 579 this.backgroundPaint = paint; 580 fireChangeEvent(); 581 } 582 583 } 584 585 /** 586 * Returns the alpha transparency of the plot area background. 587 * 588 * @return The alpha transparency. 589 * 590 * @see #setBackgroundAlpha(float) 591 */ 592 public float getBackgroundAlpha() { 593 return this.backgroundAlpha; 594 } 595 596 /** 597 * Sets the alpha transparency of the plot area background, and notifies 598 * registered listeners that the plot has been modified. 599 * 600 * @param alpha the new alpha value (in the range 0.0f to 1.0f). 601 * 602 * @see #getBackgroundAlpha() 603 */ 604 public void setBackgroundAlpha(float alpha) { 605 if (this.backgroundAlpha != alpha) { 606 this.backgroundAlpha = alpha; 607 fireChangeEvent(); 608 } 609 } 610 611 /** 612 * Returns the drawing supplier for the plot. 613 * 614 * @return The drawing supplier (possibly <code>null</code>). 615 * 616 * @see #setDrawingSupplier(DrawingSupplier) 617 */ 618 public DrawingSupplier getDrawingSupplier() { 619 DrawingSupplier result; 620 Plot p = getParent(); 621 if (p != null) { 622 result = p.getDrawingSupplier(); 623 } 624 else { 625 result = this.drawingSupplier; 626 } 627 return result; 628 } 629 630 /** 631 * Sets the drawing supplier for the plot and sends a 632 * {@link PlotChangeEvent} to all registered listeners. The drawing 633 * supplier is responsible for supplying a limitless (possibly repeating) 634 * sequence of <code>Paint</code>, <code>Stroke</code> and 635 * <code>Shape</code> objects that the plot's renderer(s) can use to 636 * populate its (their) tables. 637 * 638 * @param supplier the new supplier. 639 * 640 * @see #getDrawingSupplier() 641 */ 642 public void setDrawingSupplier(DrawingSupplier supplier) { 643 this.drawingSupplier = supplier; 644 fireChangeEvent(); 645 } 646 647 /** 648 * Sets the drawing supplier for the plot and, if requested, sends a 649 * {@link PlotChangeEvent} to all registered listeners. The drawing 650 * supplier is responsible for supplying a limitless (possibly repeating) 651 * sequence of <code>Paint</code>, <code>Stroke</code> and 652 * <code>Shape</code> objects that the plot's renderer(s) can use to 653 * populate its (their) tables. 654 * 655 * @param supplier the new supplier. 656 * @param notify notify listeners? 657 * 658 * @see #getDrawingSupplier() 659 * 660 * @since 1.0.11 661 */ 662 public void setDrawingSupplier(DrawingSupplier supplier, boolean notify) { 663 this.drawingSupplier = supplier; 664 if (notify) { 665 fireChangeEvent(); 666 } 667 } 668 669 /** 670 * Returns the background image that is used to fill the plot's background 671 * area. 672 * 673 * @return The image (possibly <code>null</code>). 674 * 675 * @see #setBackgroundImage(Image) 676 */ 677 public Image getBackgroundImage() { 678 return this.backgroundImage; 679 } 680 681 /** 682 * Sets the background image for the plot and sends a 683 * {@link PlotChangeEvent} to all registered listeners. 684 * 685 * @param image the image (<code>null</code> permitted). 686 * 687 * @see #getBackgroundImage() 688 */ 689 public void setBackgroundImage(Image image) { 690 this.backgroundImage = image; 691 fireChangeEvent(); 692 } 693 694 /** 695 * Returns the background image alignment. Alignment constants are defined 696 * in the <code>org.jfree.ui.Align</code> class in the JCommon class 697 * library. 698 * 699 * @return The alignment. 700 * 701 * @see #setBackgroundImageAlignment(int) 702 */ 703 public int getBackgroundImageAlignment() { 704 return this.backgroundImageAlignment; 705 } 706 707 /** 708 * Sets the alignment for the background image and sends a 709 * {@link PlotChangeEvent} to all registered listeners. Alignment options 710 * are defined by the {@link org.jfree.ui.Align} class in the JCommon 711 * class library. 712 * 713 * @param alignment the alignment. 714 * 715 * @see #getBackgroundImageAlignment() 716 */ 717 public void setBackgroundImageAlignment(int alignment) { 718 if (this.backgroundImageAlignment != alignment) { 719 this.backgroundImageAlignment = alignment; 720 fireChangeEvent(); 721 } 722 } 723 724 /** 725 * Returns the alpha transparency used to draw the background image. This 726 * is a value in the range 0.0f to 1.0f, where 0.0f is fully transparent 727 * and 1.0f is fully opaque. 728 * 729 * @return The alpha transparency. 730 * 731 * @see #setBackgroundImageAlpha(float) 732 */ 733 public float getBackgroundImageAlpha() { 734 return this.backgroundImageAlpha; 735 } 736 737 /** 738 * Sets the alpha transparency used when drawing the background image. 739 * 740 * @param alpha the alpha transparency (in the range 0.0f to 1.0f, where 741 * 0.0f is fully transparent, and 1.0f is fully opaque). 742 * 743 * @throws IllegalArgumentException if <code>alpha</code> is not within 744 * the specified range. 745 * 746 * @see #getBackgroundImageAlpha() 747 */ 748 public void setBackgroundImageAlpha(float alpha) { 749 if (alpha < 0.0f || alpha > 1.0f) { 750 throw new IllegalArgumentException( 751 "The 'alpha' value must be in the range 0.0f to 1.0f."); 752 } 753 if (this.backgroundImageAlpha != alpha) { 754 this.backgroundImageAlpha = alpha; 755 fireChangeEvent(); 756 } 757 } 758 759 /** 760 * Returns the flag that controls whether or not the plot outline is 761 * drawn. The default value is <code>true</code>. Note that for 762 * historical reasons, the plot's outline paint and stroke can take on 763 * <code>null</code> values, in which case the outline will not be drawn 764 * even if this flag is set to <code>true</code>. 765 * 766 * @return The outline visibility flag. 767 * 768 * @since 1.0.6 769 * 770 * @see #setOutlineVisible(boolean) 771 */ 772 public boolean isOutlineVisible() { 773 return this.outlineVisible; 774 } 775 776 /** 777 * Sets the flag that controls whether or not the plot's outline is 778 * drawn, and sends a {@link PlotChangeEvent} to all registered listeners. 779 * 780 * @param visible the new flag value. 781 * 782 * @since 1.0.6 783 * 784 * @see #isOutlineVisible() 785 */ 786 public void setOutlineVisible(boolean visible) { 787 this.outlineVisible = visible; 788 fireChangeEvent(); 789 } 790 791 /** 792 * Returns the stroke used to outline the plot area. 793 * 794 * @return The stroke (possibly <code>null</code>). 795 * 796 * @see #setOutlineStroke(Stroke) 797 */ 798 public Stroke getOutlineStroke() { 799 return this.outlineStroke; 800 } 801 802 /** 803 * Sets the stroke used to outline the plot area and sends a 804 * {@link PlotChangeEvent} to all registered listeners. If you set this 805 * attribute to <code>null</code>, no outline will be drawn. 806 * 807 * @param stroke the stroke (<code>null</code> permitted). 808 * 809 * @see #getOutlineStroke() 810 */ 811 public void setOutlineStroke(Stroke stroke) { 812 if (stroke == null) { 813 if (this.outlineStroke != null) { 814 this.outlineStroke = null; 815 fireChangeEvent(); 816 } 817 } 818 else { 819 if (this.outlineStroke != null) { 820 if (this.outlineStroke.equals(stroke)) { 821 return; // nothing to do 822 } 823 } 824 this.outlineStroke = stroke; 825 fireChangeEvent(); 826 } 827 } 828 829 /** 830 * Returns the color used to draw the outline of the plot area. 831 * 832 * @return The color (possibly <code>null</code>). 833 * 834 * @see #setOutlinePaint(Paint) 835 */ 836 public Paint getOutlinePaint() { 837 return this.outlinePaint; 838 } 839 840 /** 841 * Sets the paint used to draw the outline of the plot area and sends a 842 * {@link PlotChangeEvent} to all registered listeners. If you set this 843 * attribute to <code>null</code>, no outline will be drawn. 844 * 845 * @param paint the paint (<code>null</code> permitted). 846 * 847 * @see #getOutlinePaint() 848 */ 849 public void setOutlinePaint(Paint paint) { 850 if (paint == null) { 851 if (this.outlinePaint != null) { 852 this.outlinePaint = null; 853 fireChangeEvent(); 854 } 855 } 856 else { 857 if (this.outlinePaint != null) { 858 if (this.outlinePaint.equals(paint)) { 859 return; // nothing to do 860 } 861 } 862 this.outlinePaint = paint; 863 fireChangeEvent(); 864 } 865 } 866 867 /** 868 * Returns the alpha-transparency for the plot foreground. 869 * 870 * @return The alpha-transparency. 871 * 872 * @see #setForegroundAlpha(float) 873 */ 874 public float getForegroundAlpha() { 875 return this.foregroundAlpha; 876 } 877 878 /** 879 * Sets the alpha-transparency for the plot and sends a 880 * {@link PlotChangeEvent} to all registered listeners. 881 * 882 * @param alpha the new alpha transparency. 883 * 884 * @see #getForegroundAlpha() 885 */ 886 public void setForegroundAlpha(float alpha) { 887 if (this.foregroundAlpha != alpha) { 888 this.foregroundAlpha = alpha; 889 fireChangeEvent(); 890 } 891 } 892 893 /** 894 * Returns the legend items for the plot. By default, this method returns 895 * <code>null</code>. Subclasses should override to return a 896 * {@link LegendItemCollection}. 897 * 898 * @return The legend items for the plot (possibly <code>null</code>). 899 */ 900 @Override 901 public LegendItemCollection getLegendItems() { 902 return null; 903 } 904 905 /** 906 * Returns a flag that controls whether or not change events are sent to 907 * registered listeners. 908 * 909 * @return A boolean. 910 * 911 * @see #setNotify(boolean) 912 * 913 * @since 1.0.13 914 */ 915 public boolean isNotify() { 916 return this.notify; 917 } 918 919 /** 920 * Sets a flag that controls whether or not listeners receive 921 * {@link PlotChangeEvent} notifications. 922 * 923 * @param notify a boolean. 924 * 925 * @see #isNotify() 926 * 927 * @since 1.0.13 928 */ 929 public void setNotify(boolean notify) { 930 this.notify = notify; 931 // if the flag is being set to true, there may be queued up changes... 932 if (notify) { 933 notifyListeners(new PlotChangeEvent(this)); 934 } 935 } 936 937 /** 938 * Registers an object for notification of changes to the plot. 939 * 940 * @param listener the object to be registered. 941 * 942 * @see #removeChangeListener(PlotChangeListener) 943 */ 944 public void addChangeListener(PlotChangeListener listener) { 945 this.listenerList.add(PlotChangeListener.class, listener); 946 } 947 948 /** 949 * Unregisters an object for notification of changes to the plot. 950 * 951 * @param listener the object to be unregistered. 952 * 953 * @see #addChangeListener(PlotChangeListener) 954 */ 955 public void removeChangeListener(PlotChangeListener listener) { 956 this.listenerList.remove(PlotChangeListener.class, listener); 957 } 958 959 /** 960 * Notifies all registered listeners that the plot has been modified. 961 * 962 * @param event information about the change event. 963 */ 964 public void notifyListeners(PlotChangeEvent event) { 965 // if the 'notify' flag has been switched to false, we don't notify 966 // the listeners 967 if (!this.notify) { 968 return; 969 } 970 Object[] listeners = this.listenerList.getListenerList(); 971 for (int i = listeners.length - 2; i >= 0; i -= 2) { 972 if (listeners[i] == PlotChangeListener.class) { 973 ((PlotChangeListener) listeners[i + 1]).plotChanged(event); 974 } 975 } 976 } 977 978 /** 979 * Sends a {@link PlotChangeEvent} to all registered listeners. 980 * 981 * @since 1.0.10 982 */ 983 protected void fireChangeEvent() { 984 notifyListeners(new PlotChangeEvent(this)); 985 } 986 987 /** 988 * Draws the plot within the specified area. The anchor is a point on the 989 * chart that is specified externally (for instance, it may be the last 990 * point of the last mouse click performed by the user) - plots can use or 991 * ignore this value as they see fit. 992 * <br><br> 993 * Subclasses need to provide an implementation of this method, obviously. 994 * 995 * @param g2 the graphics device. 996 * @param area the plot area. 997 * @param anchor the anchor point (<code>null</code> permitted). 998 * @param parentState the parent state (if any). 999 * @param info carries back plot rendering info. 1000 */ 1001 public abstract void draw(Graphics2D g2, 1002 Rectangle2D area, 1003 Point2D anchor, 1004 PlotState parentState, 1005 PlotRenderingInfo info); 1006 1007 /** 1008 * Draws the plot background (the background color and/or image). 1009 * <P> 1010 * This method will be called during the chart drawing process and is 1011 * declared public so that it can be accessed by the renderers used by 1012 * certain subclasses. You shouldn't need to call this method directly. 1013 * 1014 * @param g2 the graphics device. 1015 * @param area the area within which the plot should be drawn. 1016 */ 1017 public void drawBackground(Graphics2D g2, Rectangle2D area) { 1018 // some subclasses override this method completely, so don't put 1019 // anything here that *must* be done 1020 fillBackground(g2, area); 1021 drawBackgroundImage(g2, area); 1022 } 1023 1024 /** 1025 * Fills the specified area with the background paint. 1026 * 1027 * @param g2 the graphics device. 1028 * @param area the area. 1029 * 1030 * @see #getBackgroundPaint() 1031 * @see #getBackgroundAlpha() 1032 * @see #fillBackground(Graphics2D, Rectangle2D, PlotOrientation) 1033 */ 1034 protected void fillBackground(Graphics2D g2, Rectangle2D area) { 1035 fillBackground(g2, area, PlotOrientation.VERTICAL); 1036 } 1037 1038 /** 1039 * Fills the specified area with the background paint. If the background 1040 * paint is an instance of <code>GradientPaint</code>, the gradient will 1041 * run in the direction suggested by the plot's orientation. 1042 * 1043 * @param g2 the graphics target. 1044 * @param area the plot area. 1045 * @param orientation the plot orientation (<code>null</code> not 1046 * permitted). 1047 * 1048 * @since 1.0.6 1049 */ 1050 protected void fillBackground(Graphics2D g2, Rectangle2D area, 1051 PlotOrientation orientation) { 1052 ParamChecks.nullNotPermitted(orientation, "orientation"); 1053 if (this.backgroundPaint == null) { 1054 return; 1055 } 1056 Paint p = this.backgroundPaint; 1057 if (p instanceof GradientPaint) { 1058 GradientPaint gp = (GradientPaint) p; 1059 if (orientation == PlotOrientation.VERTICAL) { 1060 p = new GradientPaint((float) area.getCenterX(), 1061 (float) area.getMaxY(), gp.getColor1(), 1062 (float) area.getCenterX(), (float) area.getMinY(), 1063 gp.getColor2()); 1064 } 1065 else if (orientation == PlotOrientation.HORIZONTAL) { 1066 p = new GradientPaint((float) area.getMinX(), 1067 (float) area.getCenterY(), gp.getColor1(), 1068 (float) area.getMaxX(), (float) area.getCenterY(), 1069 gp.getColor2()); 1070 } 1071 } 1072 Composite originalComposite = g2.getComposite(); 1073 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1074 this.backgroundAlpha)); 1075 g2.setPaint(p); 1076 g2.fill(area); 1077 g2.setComposite(originalComposite); 1078 } 1079 1080 /** 1081 * Draws the background image (if there is one) aligned within the 1082 * specified area. 1083 * 1084 * @param g2 the graphics device. 1085 * @param area the area. 1086 * 1087 * @see #getBackgroundImage() 1088 * @see #getBackgroundImageAlignment() 1089 * @see #getBackgroundImageAlpha() 1090 */ 1091 public void drawBackgroundImage(Graphics2D g2, Rectangle2D area) { 1092 if (this.backgroundImage == null) { 1093 return; // nothing to do 1094 } 1095 Composite savedComposite = g2.getComposite(); 1096 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1097 this.backgroundImageAlpha)); 1098 Rectangle2D dest = new Rectangle2D.Double(0.0, 0.0, 1099 this.backgroundImage.getWidth(null), 1100 this.backgroundImage.getHeight(null)); 1101 Align.align(dest, area, this.backgroundImageAlignment); 1102 Shape savedClip = g2.getClip(); 1103 g2.clip(area); 1104 g2.drawImage(this.backgroundImage, (int) dest.getX(), 1105 (int) dest.getY(), (int) dest.getWidth() + 1, 1106 (int) dest.getHeight() + 1, null); 1107 g2.setClip(savedClip); 1108 g2.setComposite(savedComposite); 1109 } 1110 1111 /** 1112 * Draws the plot outline. This method will be called during the chart 1113 * drawing process and is declared public so that it can be accessed by the 1114 * renderers used by certain subclasses. You shouldn't need to call this 1115 * method directly. 1116 * 1117 * @param g2 the graphics device. 1118 * @param area the area within which the plot should be drawn. 1119 */ 1120 public void drawOutline(Graphics2D g2, Rectangle2D area) { 1121 if (!this.outlineVisible) { 1122 return; 1123 } 1124 if ((this.outlineStroke != null) && (this.outlinePaint != null)) { 1125 g2.setStroke(this.outlineStroke); 1126 g2.setPaint(this.outlinePaint); 1127 Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); 1128 g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); 1129 g2.draw(area); 1130 g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); 1131 } 1132 } 1133 1134 /** 1135 * Draws a message to state that there is no data to plot. 1136 * 1137 * @param g2 the graphics device. 1138 * @param area the area within which the plot should be drawn. 1139 */ 1140 protected void drawNoDataMessage(Graphics2D g2, Rectangle2D area) { 1141 Shape savedClip = g2.getClip(); 1142 g2.clip(area); 1143 String message = this.noDataMessage; 1144 if (message != null) { 1145 g2.setFont(this.noDataMessageFont); 1146 g2.setPaint(this.noDataMessagePaint); 1147 TextBlock block = TextUtilities.createTextBlock( 1148 this.noDataMessage, this.noDataMessageFont, 1149 this.noDataMessagePaint, 0.9f * (float) area.getWidth(), 1150 new G2TextMeasurer(g2)); 1151 block.draw(g2, (float) area.getCenterX(), 1152 (float) area.getCenterY(), TextBlockAnchor.CENTER); 1153 } 1154 g2.setClip(savedClip); 1155 } 1156 1157 /** 1158 * Creates a plot entity that contains a reference to the plot and the 1159 * data area as shape. 1160 * 1161 * @param dataArea the data area used as hot spot for the entity. 1162 * @param plotState the plot rendering info containing a reference to the 1163 * EntityCollection. 1164 * @param toolTip the tool tip (defined in the respective Plot 1165 * subclass) (<code>null</code> permitted). 1166 * @param urlText the url (defined in the respective Plot subclass) 1167 * (<code>null</code> permitted). 1168 * 1169 * @since 1.0.13 1170 */ 1171 protected void createAndAddEntity(Rectangle2D dataArea, 1172 PlotRenderingInfo plotState, String toolTip, String urlText) { 1173 if (plotState != null && plotState.getOwner() != null) { 1174 EntityCollection e = plotState.getOwner().getEntityCollection(); 1175 if (e != null) { 1176 e.add(new PlotEntity(dataArea, this, toolTip, urlText)); 1177 } 1178 } 1179 } 1180 1181 /** 1182 * Handles a 'click' on the plot. Since the plot does not maintain any 1183 * information about where it has been drawn, the plot rendering info is 1184 * supplied as an argument so that the plot dimensions can be determined. 1185 * 1186 * @param x the x coordinate (in Java2D space). 1187 * @param y the y coordinate (in Java2D space). 1188 * @param info an object containing information about the dimensions of 1189 * the plot. 1190 */ 1191 public void handleClick(int x, int y, PlotRenderingInfo info) { 1192 // provides a 'no action' default 1193 } 1194 1195 /** 1196 * Performs a zoom on the plot. Subclasses should override if zooming is 1197 * appropriate for the type of plot. 1198 * 1199 * @param percent the zoom percentage. 1200 */ 1201 public void zoom(double percent) { 1202 // do nothing by default. 1203 } 1204 1205 /** 1206 * Receives notification of a change to an {@link Annotation} added to 1207 * this plot. 1208 * 1209 * @param event information about the event (not used here). 1210 * 1211 * @since 1.0.14 1212 */ 1213 @Override 1214 public void annotationChanged(AnnotationChangeEvent event) { 1215 fireChangeEvent(); 1216 } 1217 1218 /** 1219 * Receives notification of a change to one of the plot's axes. 1220 * 1221 * @param event information about the event (not used here). 1222 */ 1223 @Override 1224 public void axisChanged(AxisChangeEvent event) { 1225 fireChangeEvent(); 1226 } 1227 1228 /** 1229 * Receives notification of a change to the plot's dataset. 1230 * <P> 1231 * The plot reacts by passing on a plot change event to all registered 1232 * listeners. 1233 * 1234 * @param event information about the event (not used here). 1235 */ 1236 @Override 1237 public void datasetChanged(DatasetChangeEvent event) { 1238 PlotChangeEvent newEvent = new PlotChangeEvent(this); 1239 newEvent.setType(ChartChangeEventType.DATASET_UPDATED); 1240 notifyListeners(newEvent); 1241 } 1242 1243 /** 1244 * Receives notification of a change to a marker that is assigned to the 1245 * plot. 1246 * 1247 * @param event the event. 1248 * 1249 * @since 1.0.3 1250 */ 1251 @Override 1252 public void markerChanged(MarkerChangeEvent event) { 1253 fireChangeEvent(); 1254 } 1255 1256 /** 1257 * Adjusts the supplied x-value. 1258 * 1259 * @param x the x-value. 1260 * @param w1 width 1. 1261 * @param w2 width 2. 1262 * @param edge the edge (left or right). 1263 * 1264 * @return The adjusted x-value. 1265 */ 1266 protected double getRectX(double x, double w1, double w2, 1267 RectangleEdge edge) { 1268 1269 double result = x; 1270 if (edge == RectangleEdge.LEFT) { 1271 result = result + w1; 1272 } 1273 else if (edge == RectangleEdge.RIGHT) { 1274 result = result + w2; 1275 } 1276 return result; 1277 1278 } 1279 1280 /** 1281 * Adjusts the supplied y-value. 1282 * 1283 * @param y the x-value. 1284 * @param h1 height 1. 1285 * @param h2 height 2. 1286 * @param edge the edge (top or bottom). 1287 * 1288 * @return The adjusted y-value. 1289 */ 1290 protected double getRectY(double y, double h1, double h2, 1291 RectangleEdge edge) { 1292 1293 double result = y; 1294 if (edge == RectangleEdge.TOP) { 1295 result = result + h1; 1296 } 1297 else if (edge == RectangleEdge.BOTTOM) { 1298 result = result + h2; 1299 } 1300 return result; 1301 1302 } 1303 1304 /** 1305 * Tests this plot for equality with another object. 1306 * 1307 * @param obj the object (<code>null</code> permitted). 1308 * 1309 * @return <code>true</code> or <code>false</code>. 1310 */ 1311 @Override 1312 public boolean equals(Object obj) { 1313 if (obj == this) { 1314 return true; 1315 } 1316 if (!(obj instanceof Plot)) { 1317 return false; 1318 } 1319 Plot that = (Plot) obj; 1320 if (!ObjectUtilities.equal(this.noDataMessage, that.noDataMessage)) { 1321 return false; 1322 } 1323 if (!ObjectUtilities.equal( 1324 this.noDataMessageFont, that.noDataMessageFont 1325 )) { 1326 return false; 1327 } 1328 if (!PaintUtilities.equal(this.noDataMessagePaint, 1329 that.noDataMessagePaint)) { 1330 return false; 1331 } 1332 if (!ObjectUtilities.equal(this.insets, that.insets)) { 1333 return false; 1334 } 1335 if (this.outlineVisible != that.outlineVisible) { 1336 return false; 1337 } 1338 if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) { 1339 return false; 1340 } 1341 if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) { 1342 return false; 1343 } 1344 if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) { 1345 return false; 1346 } 1347 if (!ObjectUtilities.equal(this.backgroundImage, 1348 that.backgroundImage)) { 1349 return false; 1350 } 1351 if (this.backgroundImageAlignment != that.backgroundImageAlignment) { 1352 return false; 1353 } 1354 if (this.backgroundImageAlpha != that.backgroundImageAlpha) { 1355 return false; 1356 } 1357 if (this.foregroundAlpha != that.foregroundAlpha) { 1358 return false; 1359 } 1360 if (this.backgroundAlpha != that.backgroundAlpha) { 1361 return false; 1362 } 1363 if (!this.drawingSupplier.equals(that.drawingSupplier)) { 1364 return false; 1365 } 1366 if (this.notify != that.notify) { 1367 return false; 1368 } 1369 return true; 1370 } 1371 1372 /** 1373 * Creates a clone of the plot. 1374 * 1375 * @return A clone. 1376 * 1377 * @throws CloneNotSupportedException if some component of the plot does not 1378 * support cloning. 1379 */ 1380 @Override 1381 public Object clone() throws CloneNotSupportedException { 1382 1383 Plot clone = (Plot) super.clone(); 1384 // private Plot parent <-- don't clone the parent plot, but take care 1385 // childs in combined plots instead 1386 if (this.datasetGroup != null) { 1387 clone.datasetGroup 1388 = (DatasetGroup) ObjectUtilities.clone(this.datasetGroup); 1389 } 1390 clone.drawingSupplier 1391 = (DrawingSupplier) ObjectUtilities.clone(this.drawingSupplier); 1392 clone.listenerList = new EventListenerList(); 1393 return clone; 1394 1395 } 1396 1397 /** 1398 * Provides serialization support. 1399 * 1400 * @param stream the output stream. 1401 * 1402 * @throws IOException if there is an I/O error. 1403 */ 1404 private void writeObject(ObjectOutputStream stream) throws IOException { 1405 stream.defaultWriteObject(); 1406 SerialUtilities.writePaint(this.noDataMessagePaint, stream); 1407 SerialUtilities.writeStroke(this.outlineStroke, stream); 1408 SerialUtilities.writePaint(this.outlinePaint, stream); 1409 // backgroundImage 1410 SerialUtilities.writePaint(this.backgroundPaint, stream); 1411 } 1412 1413 /** 1414 * Provides serialization support. 1415 * 1416 * @param stream the input stream. 1417 * 1418 * @throws IOException if there is an I/O error. 1419 * @throws ClassNotFoundException if there is a classpath problem. 1420 */ 1421 private void readObject(ObjectInputStream stream) 1422 throws IOException, ClassNotFoundException { 1423 stream.defaultReadObject(); 1424 this.noDataMessagePaint = SerialUtilities.readPaint(stream); 1425 this.outlineStroke = SerialUtilities.readStroke(stream); 1426 this.outlinePaint = SerialUtilities.readPaint(stream); 1427 // backgroundImage 1428 this.backgroundPaint = SerialUtilities.readPaint(stream); 1429 1430 this.listenerList = new EventListenerList(); 1431 1432 } 1433 1434 /** 1435 * Resolves a domain axis location for a given plot orientation. 1436 * 1437 * @param location the location (<code>null</code> not permitted). 1438 * @param orientation the orientation (<code>null</code> not permitted). 1439 * 1440 * @return The edge (never <code>null</code>). 1441 */ 1442 public static RectangleEdge resolveDomainAxisLocation( 1443 AxisLocation location, PlotOrientation orientation) { 1444 1445 ParamChecks.nullNotPermitted(location, "location"); 1446 ParamChecks.nullNotPermitted(orientation, "orientation"); 1447 1448 RectangleEdge result = null; 1449 if (location == AxisLocation.TOP_OR_RIGHT) { 1450 if (orientation == PlotOrientation.HORIZONTAL) { 1451 result = RectangleEdge.RIGHT; 1452 } 1453 else if (orientation == PlotOrientation.VERTICAL) { 1454 result = RectangleEdge.TOP; 1455 } 1456 } 1457 else if (location == AxisLocation.TOP_OR_LEFT) { 1458 if (orientation == PlotOrientation.HORIZONTAL) { 1459 result = RectangleEdge.LEFT; 1460 } 1461 else if (orientation == PlotOrientation.VERTICAL) { 1462 result = RectangleEdge.TOP; 1463 } 1464 } 1465 else if (location == AxisLocation.BOTTOM_OR_RIGHT) { 1466 if (orientation == PlotOrientation.HORIZONTAL) { 1467 result = RectangleEdge.RIGHT; 1468 } 1469 else if (orientation == PlotOrientation.VERTICAL) { 1470 result = RectangleEdge.BOTTOM; 1471 } 1472 } 1473 else if (location == AxisLocation.BOTTOM_OR_LEFT) { 1474 if (orientation == PlotOrientation.HORIZONTAL) { 1475 result = RectangleEdge.LEFT; 1476 } 1477 else if (orientation == PlotOrientation.VERTICAL) { 1478 result = RectangleEdge.BOTTOM; 1479 } 1480 } 1481 // the above should cover all the options... 1482 if (result == null) { 1483 throw new IllegalStateException("resolveDomainAxisLocation()"); 1484 } 1485 return result; 1486 1487 } 1488 1489 /** 1490 * Resolves a range axis location for a given plot orientation. 1491 * 1492 * @param location the location (<code>null</code> not permitted). 1493 * @param orientation the orientation (<code>null</code> not permitted). 1494 * 1495 * @return The edge (never <code>null</code>). 1496 */ 1497 public static RectangleEdge resolveRangeAxisLocation( 1498 AxisLocation location, PlotOrientation orientation) { 1499 1500 ParamChecks.nullNotPermitted(location, "location"); 1501 ParamChecks.nullNotPermitted(orientation, "orientation"); 1502 1503 RectangleEdge result = null; 1504 if (location == AxisLocation.TOP_OR_RIGHT) { 1505 if (orientation == PlotOrientation.HORIZONTAL) { 1506 result = RectangleEdge.TOP; 1507 } 1508 else if (orientation == PlotOrientation.VERTICAL) { 1509 result = RectangleEdge.RIGHT; 1510 } 1511 } 1512 else if (location == AxisLocation.TOP_OR_LEFT) { 1513 if (orientation == PlotOrientation.HORIZONTAL) { 1514 result = RectangleEdge.TOP; 1515 } 1516 else if (orientation == PlotOrientation.VERTICAL) { 1517 result = RectangleEdge.LEFT; 1518 } 1519 } 1520 else if (location == AxisLocation.BOTTOM_OR_RIGHT) { 1521 if (orientation == PlotOrientation.HORIZONTAL) { 1522 result = RectangleEdge.BOTTOM; 1523 } 1524 else if (orientation == PlotOrientation.VERTICAL) { 1525 result = RectangleEdge.RIGHT; 1526 } 1527 } 1528 else if (location == AxisLocation.BOTTOM_OR_LEFT) { 1529 if (orientation == PlotOrientation.HORIZONTAL) { 1530 result = RectangleEdge.BOTTOM; 1531 } 1532 else if (orientation == PlotOrientation.VERTICAL) { 1533 result = RectangleEdge.LEFT; 1534 } 1535 } 1536 1537 // the above should cover all the options... 1538 if (result == null) { 1539 throw new IllegalStateException("resolveRangeAxisLocation()"); 1540 } 1541 return result; 1542 1543 } 1544 1545}