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