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 * CombinedRangeXYPlot.java 029 * ------------------------ 030 * (C) Copyright 2001-2012, by Bill Kelemen and Contributors. 031 * 032 * Original Author: Bill Kelemen; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Anthony Boulestreau; 035 * David Basten; 036 * Kevin Frechette (for ISTI); 037 * Arnaud Lelievre; 038 * Nicolas Brodu; 039 * Petr Kubanek (bug 1606205); 040 * 041 * Changes: 042 * -------- 043 * 06-Dec-2001 : Version 1 (BK); 044 * 12-Dec-2001 : Removed unnecessary 'throws' clause from constructor (DG); 045 * 18-Dec-2001 : Added plotArea attribute and get/set methods (BK); 046 * 22-Dec-2001 : Fixed bug in chartChanged with multiple combinations of 047 * CombinedPlots (BK); 048 * 08-Jan-2002 : Moved to new package com.jrefinery.chart.combination (DG); 049 * 25-Feb-2002 : Updated import statements (DG); 050 * 28-Feb-2002 : Readded "this.plotArea = plotArea" that was deleted from 051 * draw() method (BK); 052 * 26-Mar-2002 : Added an empty zoom method (this method needs to be written 053 * so that combined plots will support zooming (DG); 054 * 29-Mar-2002 : Changed the method createCombinedAxis adding the creation of 055 * OverlaidSymbolicAxis and CombinedSymbolicAxis(AB); 056 * 23-Apr-2002 : Renamed CombinedPlot-->MultiXYPlot, and simplified the 057 * structure (DG); 058 * 23-May-2002 : Renamed (again) MultiXYPlot-->CombinedXYPlot (DG); 059 * 19-Jun-2002 : Added get/setGap() methods suggested by David Basten (DG); 060 * 25-Jun-2002 : Removed redundant imports (DG); 061 * 16-Jul-2002 : Draws shared axis after subplots (to fix missing gridlines), 062 * added overrides of 'setSeriesPaint()' and 'setXYItemRenderer()' 063 * that pass changes down to subplots (KF); 064 * 09-Oct-2002 : Added add(XYPlot) method (DG); 065 * 26-Mar-2003 : Implemented Serializable (DG); 066 * 16-May-2003 : Renamed CombinedXYPlot --> CombinedRangeXYPlot (DG); 067 * 26-Jun-2003 : Fixed bug 755547 (DG); 068 * 16-Jul-2003 : Removed getSubPlots() method (duplicate of getSubplots()) (DG); 069 * 08-Aug-2003 : Adjusted totalWeight in remove() method (DG); 070 * 21-Aug-2003 : Implemented Cloneable (DG); 071 * 08-Sep-2003 : Added internationalization via use of properties 072 * resourceBundle (RFE 690236) (AL); 073 * 11-Sep-2003 : Fix cloning support (subplots) (NB); 074 * 15-Sep-2003 : Fixed error in cloning (DG); 075 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG); 076 * 17-Sep-2003 : Updated handling of 'clicks' (DG); 077 * 12-Nov-2004 : Implements the new Zoomable interface (DG); 078 * 25-Nov-2004 : Small update to clone() implementation (DG); 079 * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend 080 * items if set (DG); 081 * 05-May-2005 : Removed unused draw() method (DG); 082 * ------------- JFREECHART 1.0.x --------------------------------------------- 083 * 13-Sep-2006 : Updated API docs (DG); 084 * 06-Feb-2007 : Fixed bug 1606205, draw shared axis after subplots (DG); 085 * 23-Mar-2007 : Reverted previous patch (DG); 086 * 17-Apr-2007 : Added null argument checks to findSubplot() (DG); 087 * 18-Jul-2007 : Fixed bug in removeSubplot (DG); 088 * 27-Nov-2007 : Modified setFixedDomainAxisSpaceForSubplots() so as not to 089 * trigger change events in subplots (DG); 090 * 27-Mar-2008 : Add documentation for getDataRange() method (DG); 091 * 31-Mar-2008 : Updated getSubplots() to return EMPTY_LIST for null 092 * subplots, as suggested by Richard West (DG); 093 * 28-Apr-2008 : Fixed zooming problem (see bug 1950037) (DG); 094 * 11-Aug-2008 : Don't store totalWeight of subplots, calculate it as 095 * required (DG); 096 * 21-Dec-2011 : Apply patch 3447161 by Ulrich Voigt and Martin Hoeller (MH); 097 * 098 */ 099 100package org.jfree.chart.plot; 101 102import java.awt.Graphics2D; 103import java.awt.geom.Point2D; 104import java.awt.geom.Rectangle2D; 105import java.util.Collections; 106import java.util.Iterator; 107import java.util.List; 108 109import org.jfree.chart.LegendItemCollection; 110import org.jfree.chart.axis.AxisSpace; 111import org.jfree.chart.axis.AxisState; 112import org.jfree.chart.axis.NumberAxis; 113import org.jfree.chart.axis.ValueAxis; 114import org.jfree.chart.event.PlotChangeEvent; 115import org.jfree.chart.event.PlotChangeListener; 116import org.jfree.chart.renderer.xy.XYItemRenderer; 117import org.jfree.chart.util.ParamChecks; 118import org.jfree.chart.util.ShadowGenerator; 119import org.jfree.data.Range; 120import org.jfree.ui.RectangleEdge; 121import org.jfree.ui.RectangleInsets; 122import org.jfree.util.ObjectUtilities; 123 124/** 125 * An extension of {@link XYPlot} that contains multiple subplots that share a 126 * common range axis. 127 */ 128public class CombinedRangeXYPlot extends XYPlot 129 implements PlotChangeListener { 130 131 /** For serialization. */ 132 private static final long serialVersionUID = -5177814085082031168L; 133 134 /** Storage for the subplot references. */ 135 private List subplots; 136 137 /** The gap between subplots. */ 138 private double gap = 5.0; 139 140 /** Temporary storage for the subplot areas. */ 141 private transient Rectangle2D[] subplotAreas; 142 143 /** 144 * Default constructor. 145 */ 146 public CombinedRangeXYPlot() { 147 this(new NumberAxis()); 148 } 149 150 /** 151 * Creates a new plot. 152 * 153 * @param rangeAxis the shared axis. 154 */ 155 public CombinedRangeXYPlot(ValueAxis rangeAxis) { 156 157 super(null, // no data in the parent plot 158 null, 159 rangeAxis, 160 null); 161 162 this.subplots = new java.util.ArrayList(); 163 164 } 165 166 /** 167 * Returns a string describing the type of plot. 168 * 169 * @return The type of plot. 170 */ 171 @Override 172 public String getPlotType() { 173 return localizationResources.getString("Combined_Range_XYPlot"); 174 } 175 176 /** 177 * Returns the space between subplots. 178 * 179 * @return The gap. 180 * 181 * @see #setGap(double) 182 */ 183 public double getGap() { 184 return this.gap; 185 } 186 187 /** 188 * Sets the amount of space between subplots. 189 * 190 * @param gap the gap between subplots. 191 * 192 * @see #getGap() 193 */ 194 public void setGap(double gap) { 195 this.gap = gap; 196 } 197 198 /** 199 * Adds a subplot, with a default 'weight' of 1. 200 * <br><br> 201 * You must ensure that the subplot has a non-null domain axis. The range 202 * axis for the subplot will be set to <code>null</code>. 203 * 204 * @param subplot the subplot. 205 */ 206 public void add(XYPlot subplot) { 207 add(subplot, 1); 208 } 209 210 /** 211 * Adds a subplot with a particular weight (greater than or equal to one). 212 * The weight determines how much space is allocated to the subplot 213 * relative to all the other subplots. 214 * <br><br> 215 * You must ensure that the subplot has a non-null domain axis. The range 216 * axis for the subplot will be set to <code>null</code>. 217 * 218 * @param subplot the subplot (<code>null</code> not permitted). 219 * @param weight the weight (must be 1 or greater). 220 */ 221 public void add(XYPlot subplot, int weight) { 222 ParamChecks.nullNotPermitted(subplot, "subplot"); 223 if (weight <= 0) { 224 String msg = "The 'weight' must be positive."; 225 throw new IllegalArgumentException(msg); 226 } 227 228 // store the plot and its weight 229 subplot.setParent(this); 230 subplot.setWeight(weight); 231 subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0)); 232 subplot.setRangeAxis(null); 233 subplot.addChangeListener(this); 234 this.subplots.add(subplot); 235 configureRangeAxes(); 236 fireChangeEvent(); 237 238 } 239 240 /** 241 * Removes a subplot from the combined chart. 242 * 243 * @param subplot the subplot (<code>null</code> not permitted). 244 */ 245 public void remove(XYPlot subplot) { 246 ParamChecks.nullNotPermitted(subplot, "subplot"); 247 int position = -1; 248 int size = this.subplots.size(); 249 int i = 0; 250 while (position == -1 && i < size) { 251 if (this.subplots.get(i) == subplot) { 252 position = i; 253 } 254 i++; 255 } 256 if (position != -1) { 257 this.subplots.remove(position); 258 subplot.setParent(null); 259 subplot.removeChangeListener(this); 260 configureRangeAxes(); 261 fireChangeEvent(); 262 } 263 } 264 265 /** 266 * Returns the list of subplots. The returned list may be empty, but is 267 * never <code>null</code>. 268 * 269 * @return An unmodifiable list of subplots. 270 */ 271 public List getSubplots() { 272 if (this.subplots != null) { 273 return Collections.unmodifiableList(this.subplots); 274 } 275 else { 276 return Collections.EMPTY_LIST; 277 } 278 } 279 280 /** 281 * Calculates the space required for the axes. 282 * 283 * @param g2 the graphics device. 284 * @param plotArea the plot area. 285 * 286 * @return The space required for the axes. 287 */ 288 @Override 289 protected AxisSpace calculateAxisSpace(Graphics2D g2, 290 Rectangle2D plotArea) { 291 292 AxisSpace space = new AxisSpace(); 293 PlotOrientation orientation = getOrientation(); 294 295 // work out the space required by the domain axis... 296 AxisSpace fixed = getFixedRangeAxisSpace(); 297 if (fixed != null) { 298 if (orientation == PlotOrientation.VERTICAL) { 299 space.setLeft(fixed.getLeft()); 300 space.setRight(fixed.getRight()); 301 } 302 else if (orientation == PlotOrientation.HORIZONTAL) { 303 space.setTop(fixed.getTop()); 304 space.setBottom(fixed.getBottom()); 305 } 306 } 307 else { 308 ValueAxis valueAxis = getRangeAxis(); 309 RectangleEdge valueEdge = Plot.resolveRangeAxisLocation( 310 getRangeAxisLocation(), orientation 311 ); 312 if (valueAxis != null) { 313 space = valueAxis.reserveSpace(g2, this, plotArea, valueEdge, 314 space); 315 } 316 } 317 318 Rectangle2D adjustedPlotArea = space.shrink(plotArea, null); 319 // work out the maximum height or width of the non-shared axes... 320 int n = this.subplots.size(); 321 int totalWeight = 0; 322 for (int i = 0; i < n; i++) { 323 XYPlot sub = (XYPlot) this.subplots.get(i); 324 totalWeight += sub.getWeight(); 325 } 326 327 // calculate plotAreas of all sub-plots, maximum vertical/horizontal 328 // axis width/height 329 this.subplotAreas = new Rectangle2D[n]; 330 double x = adjustedPlotArea.getX(); 331 double y = adjustedPlotArea.getY(); 332 double usableSize = 0.0; 333 if (orientation == PlotOrientation.VERTICAL) { 334 usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1); 335 } 336 else if (orientation == PlotOrientation.HORIZONTAL) { 337 usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1); 338 } 339 340 for (int i = 0; i < n; i++) { 341 XYPlot plot = (XYPlot) this.subplots.get(i); 342 343 // calculate sub-plot area 344 if (orientation == PlotOrientation.VERTICAL) { 345 double w = usableSize * plot.getWeight() / totalWeight; 346 this.subplotAreas[i] = new Rectangle2D.Double(x, y, w, 347 adjustedPlotArea.getHeight()); 348 x = x + w + this.gap; 349 } 350 else if (orientation == PlotOrientation.HORIZONTAL) { 351 double h = usableSize * plot.getWeight() / totalWeight; 352 this.subplotAreas[i] = new Rectangle2D.Double(x, y, 353 adjustedPlotArea.getWidth(), h); 354 y = y + h + this.gap; 355 } 356 357 AxisSpace subSpace = plot.calculateDomainAxisSpace(g2, 358 this.subplotAreas[i], null); 359 space.ensureAtLeast(subSpace); 360 361 } 362 363 return space; 364 } 365 366 /** 367 * Draws the plot within the specified area on a graphics device. 368 * 369 * @param g2 the graphics device. 370 * @param area the plot area (in Java2D space). 371 * @param anchor an anchor point in Java2D space (<code>null</code> 372 * permitted). 373 * @param parentState the state from the parent plot, if there is one 374 * (<code>null</code> permitted). 375 * @param info collects chart drawing information (<code>null</code> 376 * permitted). 377 */ 378 @Override 379 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 380 PlotState parentState, PlotRenderingInfo info) { 381 382 // set up info collection... 383 if (info != null) { 384 info.setPlotArea(area); 385 } 386 387 // adjust the drawing area for plot insets (if any)... 388 RectangleInsets insets = getInsets(); 389 insets.trim(area); 390 391 AxisSpace space = calculateAxisSpace(g2, area); 392 Rectangle2D dataArea = space.shrink(area, null); 393 //this.axisOffset.trim(dataArea); 394 395 // set the width and height of non-shared axis of all sub-plots 396 setFixedDomainAxisSpaceForSubplots(space); 397 398 // draw the shared axis 399 ValueAxis axis = getRangeAxis(); 400 RectangleEdge edge = getRangeAxisEdge(); 401 double cursor = RectangleEdge.coordinate(dataArea, edge); 402 AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info); 403 404 if (parentState == null) { 405 parentState = new PlotState(); 406 } 407 parentState.getSharedAxisStates().put(axis, axisState); 408 409 // draw all the charts 410 for (int i = 0; i < this.subplots.size(); i++) { 411 XYPlot plot = (XYPlot) this.subplots.get(i); 412 PlotRenderingInfo subplotInfo = null; 413 if (info != null) { 414 subplotInfo = new PlotRenderingInfo(info.getOwner()); 415 info.addSubplotInfo(subplotInfo); 416 } 417 plot.draw(g2, this.subplotAreas[i], anchor, parentState, 418 subplotInfo); 419 } 420 421 if (info != null) { 422 info.setDataArea(dataArea); 423 } 424 425 } 426 427 /** 428 * Returns a collection of legend items for the plot. 429 * 430 * @return The legend items. 431 */ 432 @Override 433 public LegendItemCollection getLegendItems() { 434 LegendItemCollection result = getFixedLegendItems(); 435 if (result == null) { 436 result = new LegendItemCollection(); 437 438 if (this.subplots != null) { 439 Iterator iterator = this.subplots.iterator(); 440 while (iterator.hasNext()) { 441 XYPlot plot = (XYPlot) iterator.next(); 442 LegendItemCollection more = plot.getLegendItems(); 443 result.addAll(more); 444 } 445 } 446 } 447 return result; 448 } 449 450 /** 451 * Multiplies the range on the domain axis/axes by the specified factor. 452 * 453 * @param factor the zoom factor. 454 * @param info the plot rendering info (<code>null</code> not permitted). 455 * @param source the source point (<code>null</code> not permitted). 456 */ 457 @Override 458 public void zoomDomainAxes(double factor, PlotRenderingInfo info, 459 Point2D source) { 460 zoomDomainAxes(factor, info, source, false); 461 } 462 463 /** 464 * Multiplies the range on the domain axis/axes by the specified factor. 465 * 466 * @param factor the zoom factor. 467 * @param info the plot rendering info (<code>null</code> not permitted). 468 * @param source the source point (<code>null</code> not permitted). 469 * @param useAnchor zoom about the anchor point? 470 */ 471 @Override 472 public void zoomDomainAxes(double factor, PlotRenderingInfo info, 473 Point2D source, boolean useAnchor) { 474 // delegate 'info' and 'source' argument checks... 475 XYPlot subplot = findSubplot(info, source); 476 if (subplot != null) { 477 subplot.zoomDomainAxes(factor, info, source, useAnchor); 478 } 479 else { 480 // if the source point doesn't fall within a subplot, we do the 481 // zoom on all subplots... 482 Iterator iterator = getSubplots().iterator(); 483 while (iterator.hasNext()) { 484 subplot = (XYPlot) iterator.next(); 485 subplot.zoomDomainAxes(factor, info, source, useAnchor); 486 } 487 } 488 } 489 490 /** 491 * Zooms in on the domain axes. 492 * 493 * @param lowerPercent the lower bound. 494 * @param upperPercent the upper bound. 495 * @param info the plot rendering info (<code>null</code> not permitted). 496 * @param source the source point (<code>null</code> not permitted). 497 */ 498 @Override 499 public void zoomDomainAxes(double lowerPercent, double upperPercent, 500 PlotRenderingInfo info, Point2D source) { 501 // delegate 'info' and 'source' argument checks... 502 XYPlot subplot = findSubplot(info, source); 503 if (subplot != null) { 504 subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source); 505 } 506 else { 507 // if the source point doesn't fall within a subplot, we do the 508 // zoom on all subplots... 509 Iterator iterator = getSubplots().iterator(); 510 while (iterator.hasNext()) { 511 subplot = (XYPlot) iterator.next(); 512 subplot.zoomDomainAxes(lowerPercent, upperPercent, info, 513 source); 514 } 515 } 516 } 517 518 /** 519 * Pans all domain axes by the specified percentage. 520 * 521 * @param panRange the distance to pan (as a percentage of the axis length). 522 * @param info the plot info 523 * @param source the source point where the pan action started. 524 * 525 * @since 1.0.15 526 */ 527 @Override 528 public void panDomainAxes(double panRange, PlotRenderingInfo info, 529 Point2D source) { 530 531 XYPlot subplot = findSubplot(info, source); 532 if (subplot != null) { 533 PlotRenderingInfo subplotInfo = info.getSubplotInfo( 534 info.getSubplotIndex(source)); 535 if (subplotInfo == null) { 536 return; 537 } 538 539 for (int i = 0; i < subplot.getDomainAxisCount(); i++) { 540 ValueAxis domainAxis = subplot.getDomainAxis(i); 541 domainAxis.pan(panRange); 542 } 543 } 544 } 545 546 /** 547 * Returns the subplot (if any) that contains the (x, y) point (specified 548 * in Java2D space). 549 * 550 * @param info the chart rendering info (<code>null</code> not permitted). 551 * @param source the source point (<code>null</code> not permitted). 552 * 553 * @return A subplot (possibly <code>null</code>). 554 */ 555 public XYPlot findSubplot(PlotRenderingInfo info, Point2D source) { 556 ParamChecks.nullNotPermitted(info, "info"); 557 ParamChecks.nullNotPermitted(source, "source"); 558 XYPlot result = null; 559 int subplotIndex = info.getSubplotIndex(source); 560 if (subplotIndex >= 0) { 561 result = (XYPlot) this.subplots.get(subplotIndex); 562 } 563 return result; 564 } 565 566 /** 567 * Sets the item renderer FOR ALL SUBPLOTS. Registered listeners are 568 * notified that the plot has been modified. 569 * <P> 570 * Note: usually you will want to set the renderer independently for each 571 * subplot, which is NOT what this method does. 572 * 573 * @param renderer the new renderer. 574 */ 575 @Override 576 public void setRenderer(XYItemRenderer renderer) { 577 super.setRenderer(renderer); // not strictly necessary, since the 578 // renderer set for the 579 // parent plot is not used 580 Iterator iterator = this.subplots.iterator(); 581 while (iterator.hasNext()) { 582 XYPlot plot = (XYPlot) iterator.next(); 583 plot.setRenderer(renderer); 584 } 585 } 586 587 /** 588 * Sets the orientation for the plot (and all its subplots). 589 * 590 * @param orientation the orientation. 591 */ 592 @Override 593 public void setOrientation(PlotOrientation orientation) { 594 super.setOrientation(orientation); 595 Iterator iterator = this.subplots.iterator(); 596 while (iterator.hasNext()) { 597 XYPlot plot = (XYPlot) iterator.next(); 598 plot.setOrientation(orientation); 599 } 600 } 601 602 /** 603 * Sets the shadow generator for the plot (and all subplots) and sends 604 * a {@link PlotChangeEvent} to all registered listeners. 605 * 606 * @param generator the new generator (<code>null</code> permitted). 607 */ 608 @Override 609 public void setShadowGenerator(ShadowGenerator generator) { 610 setNotify(false); 611 super.setShadowGenerator(generator); 612 Iterator iterator = this.subplots.iterator(); 613 while (iterator.hasNext()) { 614 XYPlot plot = (XYPlot) iterator.next(); 615 plot.setShadowGenerator(generator); 616 } 617 setNotify(true); 618 } 619 620 /** 621 * Returns a range representing the extent of the data values in this plot 622 * (obtained from the subplots) that will be rendered against the specified 623 * axis. NOTE: This method is intended for internal JFreeChart use, and 624 * is public only so that code in the axis classes can call it. Since 625 * only the range axis is shared between subplots, the JFreeChart code 626 * will only call this method for the range values (although this is not 627 * checked/enforced). 628 * 629 * @param axis the axis. 630 * 631 * @return The range. 632 */ 633 @Override 634 public Range getDataRange(ValueAxis axis) { 635 Range result = null; 636 if (this.subplots != null) { 637 Iterator iterator = this.subplots.iterator(); 638 while (iterator.hasNext()) { 639 XYPlot subplot = (XYPlot) iterator.next(); 640 result = Range.combine(result, subplot.getDataRange(axis)); 641 } 642 } 643 return result; 644 } 645 646 /** 647 * Sets the space (width or height, depending on the orientation of the 648 * plot) for the domain axis of each subplot. 649 * 650 * @param space the space. 651 */ 652 protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) { 653 Iterator iterator = this.subplots.iterator(); 654 while (iterator.hasNext()) { 655 XYPlot plot = (XYPlot) iterator.next(); 656 plot.setFixedDomainAxisSpace(space, false); 657 } 658 } 659 660 /** 661 * Handles a 'click' on the plot by updating the anchor values... 662 * 663 * @param x x-coordinate, where the click occured. 664 * @param y y-coordinate, where the click occured. 665 * @param info object containing information about the plot dimensions. 666 */ 667 @Override 668 public void handleClick(int x, int y, PlotRenderingInfo info) { 669 Rectangle2D dataArea = info.getDataArea(); 670 if (dataArea.contains(x, y)) { 671 for (int i = 0; i < this.subplots.size(); i++) { 672 XYPlot subplot = (XYPlot) this.subplots.get(i); 673 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i); 674 subplot.handleClick(x, y, subplotInfo); 675 } 676 } 677 } 678 679 /** 680 * Receives a {@link PlotChangeEvent} and responds by notifying all 681 * listeners. 682 * 683 * @param event the event. 684 */ 685 @Override 686 public void plotChanged(PlotChangeEvent event) { 687 notifyListeners(event); 688 } 689 690 /** 691 * Tests this plot for equality with another object. 692 * 693 * @param obj the other object. 694 * 695 * @return <code>true</code> or <code>false</code>. 696 */ 697 @Override 698 public boolean equals(Object obj) { 699 if (obj == this) { 700 return true; 701 } 702 if (!(obj instanceof CombinedRangeXYPlot)) { 703 return false; 704 } 705 CombinedRangeXYPlot that = (CombinedRangeXYPlot) obj; 706 if (this.gap != that.gap) { 707 return false; 708 } 709 if (!ObjectUtilities.equal(this.subplots, that.subplots)) { 710 return false; 711 } 712 return super.equals(obj); 713 } 714 715 /** 716 * Returns a clone of the plot. 717 * 718 * @return A clone. 719 * 720 * @throws CloneNotSupportedException this class will not throw this 721 * exception, but subclasses (if any) might. 722 */ 723 @Override 724 public Object clone() throws CloneNotSupportedException { 725 726 CombinedRangeXYPlot result = (CombinedRangeXYPlot) super.clone(); 727 result.subplots = (List) ObjectUtilities.deepClone(this.subplots); 728 for (Iterator it = result.subplots.iterator(); it.hasNext();) { 729 Plot child = (Plot) it.next(); 730 child.setParent(result); 731 } 732 733 // after setting up all the subplots, the shared range axis may need 734 // reconfiguring 735 ValueAxis rangeAxis = result.getRangeAxis(); 736 if (rangeAxis != null) { 737 rangeAxis.configure(); 738 } 739 740 return result; 741 } 742 743}