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