001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2014, 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-2014, 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 * 21-Jul-2014 : Override isDomainPannable() and setDomainPannable() - motivated 098 * by patch #304 by Ulrich Voigt (DG); 099 * 100 */ 101 102package org.jfree.chart.plot; 103 104import java.awt.Graphics2D; 105import java.awt.geom.Point2D; 106import java.awt.geom.Rectangle2D; 107import java.util.Collections; 108import java.util.Iterator; 109import java.util.List; 110 111import org.jfree.chart.LegendItemCollection; 112import org.jfree.chart.axis.AxisSpace; 113import org.jfree.chart.axis.AxisState; 114import org.jfree.chart.axis.NumberAxis; 115import org.jfree.chart.axis.ValueAxis; 116import org.jfree.chart.event.PlotChangeEvent; 117import org.jfree.chart.event.PlotChangeListener; 118import org.jfree.chart.renderer.xy.XYItemRenderer; 119import org.jfree.chart.util.ParamChecks; 120import org.jfree.chart.util.ShadowGenerator; 121import org.jfree.data.Range; 122import org.jfree.ui.RectangleEdge; 123import org.jfree.ui.RectangleInsets; 124import org.jfree.util.ObjectUtilities; 125 126/** 127 * An extension of {@link XYPlot} that contains multiple subplots that share a 128 * common range axis. 129 */ 130public class CombinedRangeXYPlot extends XYPlot 131 implements PlotChangeListener { 132 133 /** For serialization. */ 134 private static final long serialVersionUID = -5177814085082031168L; 135 136 /** Storage for the subplot references. */ 137 private List<XYPlot> subplots; 138 139 /** The gap between subplots. */ 140 private double gap = 5.0; 141 142 /** Temporary storage for the subplot areas. */ 143 private transient Rectangle2D[] subplotAreas; 144 145 /** 146 * Default constructor. 147 */ 148 public CombinedRangeXYPlot() { 149 this(new NumberAxis()); 150 } 151 152 /** 153 * Creates a new plot. 154 * 155 * @param rangeAxis the shared axis. 156 */ 157 public CombinedRangeXYPlot(ValueAxis rangeAxis) { 158 super(null, // no data in the parent plot 159 null, 160 rangeAxis, 161 null); 162 this.subplots = new java.util.ArrayList<XYPlot>(); 163 } 164 165 /** 166 * Returns a string describing the type of plot. 167 * 168 * @return The type of plot. 169 */ 170 @Override 171 public String getPlotType() { 172 return localizationResources.getString("Combined_Range_XYPlot"); 173 } 174 175 /** 176 * Returns the space between subplots. 177 * 178 * @return The gap. 179 * 180 * @see #setGap(double) 181 */ 182 public double getGap() { 183 return this.gap; 184 } 185 186 /** 187 * Sets the amount of space between subplots. 188 * 189 * @param gap the gap between subplots. 190 * 191 * @see #getGap() 192 */ 193 public void setGap(double gap) { 194 this.gap = gap; 195 } 196 197 /** 198 * Returns {@code true} if the domain is pannable for at least one subplot, 199 * and {@code false} otherwise. 200 * 201 * @return A boolean. 202 */ 203 @Override 204 public boolean isDomainPannable() { 205 for (XYPlot subplot : this.subplots) { 206 if (subplot.isDomainPannable()) { 207 return true; 208 } 209 } 210 return false; 211 } 212 213 /** 214 * Sets the flag, on each of the subplots, that controls whether or not the 215 * domain is pannable. 216 * 217 * @param pannable the new flag value. 218 */ 219 @Override 220 public void setDomainPannable(boolean pannable) { 221 for (XYPlot subplot : this.subplots) { 222 subplot.setDomainPannable(pannable); 223 } 224 } 225 226 /** 227 * Adds a subplot, with a default 'weight' of 1. 228 * <br><br> 229 * You must ensure that the subplot has a non-null domain axis. The range 230 * axis for the subplot will be set to <code>null</code>. 231 * 232 * @param subplot the subplot. 233 */ 234 public void add(XYPlot subplot) { 235 add(subplot, 1); 236 } 237 238 /** 239 * Adds a subplot with a particular weight (greater than or equal to one). 240 * The weight determines how much space is allocated to the subplot 241 * relative to all the other subplots. 242 * <br><br> 243 * You must ensure that the subplot has a non-null domain axis. The range 244 * axis for the subplot will be set to <code>null</code>. 245 * 246 * @param subplot the subplot (<code>null</code> not permitted). 247 * @param weight the weight (must be 1 or greater). 248 */ 249 public void add(XYPlot subplot, int weight) { 250 ParamChecks.nullNotPermitted(subplot, "subplot"); 251 if (weight <= 0) { 252 String msg = "The 'weight' must be positive."; 253 throw new IllegalArgumentException(msg); 254 } 255 256 // store the plot and its weight 257 subplot.setParent(this); 258 subplot.setWeight(weight); 259 subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0)); 260 subplot.setRangeAxis(null); 261 subplot.addChangeListener(this); 262 this.subplots.add(subplot); 263 configureRangeAxes(); 264 fireChangeEvent(); 265 266 } 267 268 /** 269 * Removes a subplot from the combined chart. 270 * 271 * @param subplot the subplot (<code>null</code> not permitted). 272 */ 273 public void remove(XYPlot subplot) { 274 ParamChecks.nullNotPermitted(subplot, "subplot"); 275 int position = -1; 276 int size = this.subplots.size(); 277 int i = 0; 278 while (position == -1 && i < size) { 279 if (this.subplots.get(i) == subplot) { 280 position = i; 281 } 282 i++; 283 } 284 if (position != -1) { 285 this.subplots.remove(position); 286 subplot.setParent(null); 287 subplot.removeChangeListener(this); 288 configureRangeAxes(); 289 fireChangeEvent(); 290 } 291 } 292 293 /** 294 * Returns the list of subplots. The returned list may be empty, but is 295 * never <code>null</code>. 296 * 297 * @return An unmodifiable list of subplots. 298 */ 299 public List getSubplots() { 300 if (this.subplots != null) { 301 return Collections.unmodifiableList(this.subplots); 302 } 303 else { 304 return Collections.EMPTY_LIST; 305 } 306 } 307 308 /** 309 * Calculates the space required for the axes. 310 * 311 * @param g2 the graphics device. 312 * @param plotArea the plot area. 313 * 314 * @return The space required for the axes. 315 */ 316 @Override 317 protected AxisSpace calculateAxisSpace(Graphics2D g2, 318 Rectangle2D plotArea) { 319 320 AxisSpace space = new AxisSpace(); 321 PlotOrientation orientation = getOrientation(); 322 323 // work out the space required by the domain axis... 324 AxisSpace fixed = getFixedRangeAxisSpace(); 325 if (fixed != null) { 326 if (orientation == PlotOrientation.VERTICAL) { 327 space.setLeft(fixed.getLeft()); 328 space.setRight(fixed.getRight()); 329 } 330 else if (orientation == PlotOrientation.HORIZONTAL) { 331 space.setTop(fixed.getTop()); 332 space.setBottom(fixed.getBottom()); 333 } 334 } 335 else { 336 ValueAxis valueAxis = getRangeAxis(); 337 RectangleEdge valueEdge = Plot.resolveRangeAxisLocation( 338 getRangeAxisLocation(), orientation 339 ); 340 if (valueAxis != null) { 341 space = valueAxis.reserveSpace(g2, this, plotArea, valueEdge, 342 space); 343 } 344 } 345 346 Rectangle2D adjustedPlotArea = space.shrink(plotArea, null); 347 // work out the maximum height or width of the non-shared axes... 348 int n = this.subplots.size(); 349 int totalWeight = 0; 350 for (int i = 0; i < n; i++) { 351 XYPlot sub = (XYPlot) this.subplots.get(i); 352 totalWeight += sub.getWeight(); 353 } 354 355 // calculate plotAreas of all sub-plots, maximum vertical/horizontal 356 // axis width/height 357 this.subplotAreas = new Rectangle2D[n]; 358 double x = adjustedPlotArea.getX(); 359 double y = adjustedPlotArea.getY(); 360 double usableSize = 0.0; 361 if (orientation == PlotOrientation.VERTICAL) { 362 usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1); 363 } 364 else if (orientation == PlotOrientation.HORIZONTAL) { 365 usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1); 366 } 367 368 for (int i = 0; i < n; i++) { 369 XYPlot plot = (XYPlot) this.subplots.get(i); 370 371 // calculate sub-plot area 372 if (orientation == PlotOrientation.VERTICAL) { 373 double w = usableSize * plot.getWeight() / totalWeight; 374 this.subplotAreas[i] = new Rectangle2D.Double(x, y, w, 375 adjustedPlotArea.getHeight()); 376 x = x + w + this.gap; 377 } 378 else if (orientation == PlotOrientation.HORIZONTAL) { 379 double h = usableSize * plot.getWeight() / totalWeight; 380 this.subplotAreas[i] = new Rectangle2D.Double(x, y, 381 adjustedPlotArea.getWidth(), h); 382 y = y + h + this.gap; 383 } 384 385 AxisSpace subSpace = plot.calculateDomainAxisSpace(g2, 386 this.subplotAreas[i], null); 387 space.ensureAtLeast(subSpace); 388 389 } 390 391 return space; 392 } 393 394 /** 395 * Draws the plot within the specified area on a graphics device. 396 * 397 * @param g2 the graphics device. 398 * @param area the plot area (in Java2D space). 399 * @param anchor an anchor point in Java2D space (<code>null</code> 400 * permitted). 401 * @param parentState the state from the parent plot, if there is one 402 * (<code>null</code> permitted). 403 * @param info collects chart drawing information (<code>null</code> 404 * permitted). 405 */ 406 @Override 407 public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, 408 PlotState parentState, PlotRenderingInfo info) { 409 410 // set up info collection... 411 if (info != null) { 412 info.setPlotArea(area); 413 } 414 415 // adjust the drawing area for plot insets (if any)... 416 RectangleInsets insets = getInsets(); 417 insets.trim(area); 418 419 AxisSpace space = calculateAxisSpace(g2, area); 420 Rectangle2D dataArea = space.shrink(area, null); 421 //this.axisOffset.trim(dataArea); 422 423 // set the width and height of non-shared axis of all sub-plots 424 setFixedDomainAxisSpaceForSubplots(space); 425 426 // draw the shared axis 427 ValueAxis axis = getRangeAxis(); 428 RectangleEdge edge = getRangeAxisEdge(); 429 double cursor = RectangleEdge.coordinate(dataArea, edge); 430 AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info); 431 432 if (parentState == null) { 433 parentState = new PlotState(); 434 } 435 parentState.getSharedAxisStates().put(axis, axisState); 436 437 // draw all the charts 438 for (int i = 0; i < this.subplots.size(); i++) { 439 XYPlot plot = (XYPlot) this.subplots.get(i); 440 PlotRenderingInfo subplotInfo = null; 441 if (info != null) { 442 subplotInfo = new PlotRenderingInfo(info.getOwner()); 443 info.addSubplotInfo(subplotInfo); 444 } 445 plot.draw(g2, this.subplotAreas[i], anchor, parentState, 446 subplotInfo); 447 } 448 449 if (info != null) { 450 info.setDataArea(dataArea); 451 } 452 453 } 454 455 /** 456 * Returns a collection of legend items for the plot. 457 * 458 * @return The legend items. 459 */ 460 @Override 461 public LegendItemCollection getLegendItems() { 462 LegendItemCollection result = getFixedLegendItems(); 463 if (result == null) { 464 result = new LegendItemCollection(); 465 466 if (this.subplots != null) { 467 Iterator iterator = this.subplots.iterator(); 468 while (iterator.hasNext()) { 469 XYPlot plot = (XYPlot) iterator.next(); 470 LegendItemCollection more = plot.getLegendItems(); 471 result.addAll(more); 472 } 473 } 474 } 475 return result; 476 } 477 478 /** 479 * Multiplies the range on the domain axis/axes by the specified factor. 480 * 481 * @param factor the zoom factor. 482 * @param info the plot rendering info (<code>null</code> not permitted). 483 * @param source the source point (<code>null</code> not permitted). 484 */ 485 @Override 486 public void zoomDomainAxes(double factor, PlotRenderingInfo info, 487 Point2D source) { 488 zoomDomainAxes(factor, info, source, false); 489 } 490 491 /** 492 * Multiplies the range on the domain axis/axes by the specified factor. 493 * 494 * @param factor the zoom factor. 495 * @param info the plot rendering info (<code>null</code> not permitted). 496 * @param source the source point (<code>null</code> not permitted). 497 * @param useAnchor zoom about the anchor point? 498 */ 499 @Override 500 public void zoomDomainAxes(double factor, PlotRenderingInfo info, 501 Point2D source, boolean useAnchor) { 502 // delegate 'info' and 'source' argument checks... 503 XYPlot subplot = findSubplot(info, source); 504 if (subplot != null) { 505 subplot.zoomDomainAxes(factor, info, source, useAnchor); 506 } 507 else { 508 // if the source point doesn't fall within a subplot, we do the 509 // zoom on all subplots... 510 Iterator iterator = getSubplots().iterator(); 511 while (iterator.hasNext()) { 512 subplot = (XYPlot) iterator.next(); 513 subplot.zoomDomainAxes(factor, info, source, useAnchor); 514 } 515 } 516 } 517 518 /** 519 * Zooms in on the domain axes. 520 * 521 * @param lowerPercent the lower bound. 522 * @param upperPercent the upper bound. 523 * @param info the plot rendering info (<code>null</code> not permitted). 524 * @param source the source point (<code>null</code> not permitted). 525 */ 526 @Override 527 public void zoomDomainAxes(double lowerPercent, double upperPercent, 528 PlotRenderingInfo info, Point2D source) { 529 // delegate 'info' and 'source' argument checks... 530 XYPlot subplot = findSubplot(info, source); 531 if (subplot != null) { 532 subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source); 533 } 534 else { 535 // if the source point doesn't fall within a subplot, we do the 536 // zoom on all subplots... 537 Iterator iterator = getSubplots().iterator(); 538 while (iterator.hasNext()) { 539 subplot = (XYPlot) iterator.next(); 540 subplot.zoomDomainAxes(lowerPercent, upperPercent, info, 541 source); 542 } 543 } 544 } 545 546 /** 547 * Pans all domain axes by the specified percentage. 548 * 549 * @param panRange the distance to pan (as a percentage of the axis length). 550 * @param info the plot info 551 * @param source the source point where the pan action started. 552 * 553 * @since 1.0.15 554 */ 555 @Override 556 public void panDomainAxes(double panRange, PlotRenderingInfo info, 557 Point2D source) { 558 559 XYPlot subplot = findSubplot(info, source); 560 if (subplot == null) { 561 return; 562 } 563 if (!subplot.isDomainPannable()) { 564 return; 565 } 566 PlotRenderingInfo subplotInfo = info.getSubplotInfo( 567 info.getSubplotIndex(source)); 568 if (subplotInfo == null) { 569 return; 570 } 571 572 for (int i = 0; i < subplot.getDomainAxisCount(); i++) { 573 ValueAxis domainAxis = subplot.getDomainAxis(i); 574 if (domainAxis != null) { 575 domainAxis.pan(panRange); 576 } 577 } 578 } 579 580 /** 581 * Returns the subplot (if any) that contains the (x, y) point (specified 582 * in Java2D space). 583 * 584 * @param info the chart rendering info (<code>null</code> not permitted). 585 * @param source the source point (<code>null</code> not permitted). 586 * 587 * @return A subplot (possibly <code>null</code>). 588 */ 589 public XYPlot findSubplot(PlotRenderingInfo info, Point2D source) { 590 ParamChecks.nullNotPermitted(info, "info"); 591 ParamChecks.nullNotPermitted(source, "source"); 592 XYPlot result = null; 593 int subplotIndex = info.getSubplotIndex(source); 594 if (subplotIndex >= 0) { 595 result = (XYPlot) this.subplots.get(subplotIndex); 596 } 597 return result; 598 } 599 600 /** 601 * Sets the item renderer FOR ALL SUBPLOTS. Registered listeners are 602 * notified that the plot has been modified. 603 * <P> 604 * Note: usually you will want to set the renderer independently for each 605 * subplot, which is NOT what this method does. 606 * 607 * @param renderer the new renderer. 608 */ 609 @Override 610 public void setRenderer(XYItemRenderer renderer) { 611 super.setRenderer(renderer); // not strictly necessary, since the 612 // renderer set for the 613 // parent plot is not used 614 Iterator iterator = this.subplots.iterator(); 615 while (iterator.hasNext()) { 616 XYPlot plot = (XYPlot) iterator.next(); 617 plot.setRenderer(renderer); 618 } 619 } 620 621 /** 622 * Sets the orientation for the plot (and all its subplots). 623 * 624 * @param orientation the orientation. 625 */ 626 @Override 627 public void setOrientation(PlotOrientation orientation) { 628 super.setOrientation(orientation); 629 Iterator iterator = this.subplots.iterator(); 630 while (iterator.hasNext()) { 631 XYPlot plot = (XYPlot) iterator.next(); 632 plot.setOrientation(orientation); 633 } 634 } 635 636 /** 637 * Sets the shadow generator for the plot (and all subplots) and sends 638 * a {@link PlotChangeEvent} to all registered listeners. 639 * 640 * @param generator the new generator (<code>null</code> permitted). 641 */ 642 @Override 643 public void setShadowGenerator(ShadowGenerator generator) { 644 setNotify(false); 645 super.setShadowGenerator(generator); 646 Iterator iterator = this.subplots.iterator(); 647 while (iterator.hasNext()) { 648 XYPlot plot = (XYPlot) iterator.next(); 649 plot.setShadowGenerator(generator); 650 } 651 setNotify(true); 652 } 653 654 /** 655 * Returns a range representing the extent of the data values in this plot 656 * (obtained from the subplots) that will be rendered against the specified 657 * axis. NOTE: This method is intended for internal JFreeChart use, and 658 * is public only so that code in the axis classes can call it. Since 659 * only the range axis is shared between subplots, the JFreeChart code 660 * will only call this method for the range values (although this is not 661 * checked/enforced). 662 * 663 * @param axis the axis. 664 * 665 * @return The range. 666 */ 667 @Override 668 public Range getDataRange(ValueAxis axis) { 669 Range result = null; 670 if (this.subplots != null) { 671 Iterator iterator = this.subplots.iterator(); 672 while (iterator.hasNext()) { 673 XYPlot subplot = (XYPlot) iterator.next(); 674 result = Range.combine(result, subplot.getDataRange(axis)); 675 } 676 } 677 return result; 678 } 679 680 /** 681 * Sets the space (width or height, depending on the orientation of the 682 * plot) for the domain axis of each subplot. 683 * 684 * @param space the space. 685 */ 686 protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) { 687 Iterator iterator = this.subplots.iterator(); 688 while (iterator.hasNext()) { 689 XYPlot plot = (XYPlot) iterator.next(); 690 plot.setFixedDomainAxisSpace(space, false); 691 } 692 } 693 694 /** 695 * Handles a 'click' on the plot by updating the anchor values... 696 * 697 * @param x x-coordinate, where the click occured. 698 * @param y y-coordinate, where the click occured. 699 * @param info object containing information about the plot dimensions. 700 */ 701 @Override 702 public void handleClick(int x, int y, PlotRenderingInfo info) { 703 Rectangle2D dataArea = info.getDataArea(); 704 if (dataArea.contains(x, y)) { 705 for (int i = 0; i < this.subplots.size(); i++) { 706 XYPlot subplot = (XYPlot) this.subplots.get(i); 707 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i); 708 subplot.handleClick(x, y, subplotInfo); 709 } 710 } 711 } 712 713 /** 714 * Receives a {@link PlotChangeEvent} and responds by notifying all 715 * listeners. 716 * 717 * @param event the event. 718 */ 719 @Override 720 public void plotChanged(PlotChangeEvent event) { 721 notifyListeners(event); 722 } 723 724 /** 725 * Tests this plot for equality with another object. 726 * 727 * @param obj the other object. 728 * 729 * @return <code>true</code> or <code>false</code>. 730 */ 731 @Override 732 public boolean equals(Object obj) { 733 if (obj == this) { 734 return true; 735 } 736 if (!(obj instanceof CombinedRangeXYPlot)) { 737 return false; 738 } 739 CombinedRangeXYPlot that = (CombinedRangeXYPlot) obj; 740 if (this.gap != that.gap) { 741 return false; 742 } 743 if (!ObjectUtilities.equal(this.subplots, that.subplots)) { 744 return false; 745 } 746 return super.equals(obj); 747 } 748 749 /** 750 * Returns a clone of the plot. 751 * 752 * @return A clone. 753 * 754 * @throws CloneNotSupportedException this class will not throw this 755 * exception, but subclasses (if any) might. 756 */ 757 @Override 758 public Object clone() throws CloneNotSupportedException { 759 760 CombinedRangeXYPlot result = (CombinedRangeXYPlot) super.clone(); 761 result.subplots = (List) ObjectUtilities.deepClone(this.subplots); 762 for (Iterator it = result.subplots.iterator(); it.hasNext();) { 763 Plot child = (Plot) it.next(); 764 child.setParent(result); 765 } 766 767 // after setting up all the subplots, the shared range axis may need 768 // reconfiguring 769 ValueAxis rangeAxis = result.getRangeAxis(); 770 if (rangeAxis != null) { 771 rangeAxis.configure(); 772 } 773 774 return result; 775 } 776 777}