Source for org.jfree.chart.plot.CombinedRangeXYPlot

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it
  10:  * under the terms of the GNU Lesser General Public License as published by
  11:  * the Free Software Foundation; either version 2.1 of the License, or
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
  22:  * USA.
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  25:  * in the United States and other countries.]
  26:  *
  27:  * ------------------------
  28:  * CombinedRangeXYPlot.java
  29:  * ------------------------
  30:  * (C) Copyright 2001-2008, by Bill Kelemen and Contributors.
  31:  *
  32:  * Original Author:  Bill Kelemen;
  33:  * Contributor(s):   David Gilbert (for Object Refinery Limited);
  34:  *                   Anthony Boulestreau;
  35:  *                   David Basten;
  36:  *                   Kevin Frechette (for ISTI);
  37:  *                   Arnaud Lelievre;
  38:  *                   Nicolas Brodu;
  39:  *                   Petr Kubanek (bug 1606205);
  40:  *
  41:  * Changes:
  42:  * --------
  43:  * 06-Dec-2001 : Version 1 (BK);
  44:  * 12-Dec-2001 : Removed unnecessary 'throws' clause from constructor (DG);
  45:  * 18-Dec-2001 : Added plotArea attribute and get/set methods (BK);
  46:  * 22-Dec-2001 : Fixed bug in chartChanged with multiple combinations of
  47:  *               CombinedPlots (BK);
  48:  * 08-Jan-2002 : Moved to new package com.jrefinery.chart.combination (DG);
  49:  * 25-Feb-2002 : Updated import statements (DG);
  50:  * 28-Feb-2002 : Readded "this.plotArea = plotArea" that was deleted from
  51:  *               draw() method (BK);
  52:  * 26-Mar-2002 : Added an empty zoom method (this method needs to be written
  53:  *               so that combined plots will support zooming (DG);
  54:  * 29-Mar-2002 : Changed the method createCombinedAxis adding the creation of
  55:  *               OverlaidSymbolicAxis and CombinedSymbolicAxis(AB);
  56:  * 23-Apr-2002 : Renamed CombinedPlot-->MultiXYPlot, and simplified the
  57:  *               structure (DG);
  58:  * 23-May-2002 : Renamed (again) MultiXYPlot-->CombinedXYPlot (DG);
  59:  * 19-Jun-2002 : Added get/setGap() methods suggested by David Basten (DG);
  60:  * 25-Jun-2002 : Removed redundant imports (DG);
  61:  * 16-Jul-2002 : Draws shared axis after subplots (to fix missing gridlines),
  62:  *               added overrides of 'setSeriesPaint()' and 'setXYItemRenderer()'
  63:  *               that pass changes down to subplots (KF);
  64:  * 09-Oct-2002 : Added add(XYPlot) method (DG);
  65:  * 26-Mar-2003 : Implemented Serializable (DG);
  66:  * 16-May-2003 : Renamed CombinedXYPlot --> CombinedRangeXYPlot (DG);
  67:  * 26-Jun-2003 : Fixed bug 755547 (DG);
  68:  * 16-Jul-2003 : Removed getSubPlots() method (duplicate of getSubplots()) (DG);
  69:  * 08-Aug-2003 : Adjusted totalWeight in remove() method (DG);
  70:  * 21-Aug-2003 : Implemented Cloneable (DG);
  71:  * 08-Sep-2003 : Added internationalization via use of properties
  72:  *               resourceBundle (RFE 690236) (AL);
  73:  * 11-Sep-2003 : Fix cloning support (subplots) (NB);
  74:  * 15-Sep-2003 : Fixed error in cloning (DG);
  75:  * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
  76:  * 17-Sep-2003 : Updated handling of 'clicks' (DG);
  77:  * 12-Nov-2004 : Implements the new Zoomable interface (DG);
  78:  * 25-Nov-2004 : Small update to clone() implementation (DG);
  79:  * 21-Feb-2005 : The getLegendItems() method now returns the fixed legend
  80:  *               items if set (DG);
  81:  * 05-May-2005 : Removed unused draw() method (DG);
  82:  * ------------- JFREECHART 1.0.x ---------------------------------------------
  83:  * 13-Sep-2006 : Updated API docs (DG);
  84:  * 06-Feb-2007 : Fixed bug 1606205, draw shared axis after subplots (DG);
  85:  * 23-Mar-2007 : Reverted previous patch (DG);
  86:  * 17-Apr-2007 : Added null argument checks to findSubplot() (DG);
  87:  * 18-Jul-2007 : Fixed bug in removeSubplot (DG);
  88:  * 27-Nov-2007 : Modified setFixedDomainAxisSpaceForSubplots() so as not to
  89:  *               trigger change events in subplots (DG);
  90:  * 27-Mar-2008 : Add documentation for getDataRange() method (DG);
  91:  * 31-Mar-2008 : Updated getSubplots() to return EMPTY_LIST for null
  92:  *               subplots, as suggested by Richard West (DG);
  93:  * 28-Apr-2008 : Fixed zooming problem (see bug 1950037) (DG);
  94:  *
  95:  */
  96: 
  97: package org.jfree.chart.plot;
  98: 
  99: import java.awt.Graphics2D;
 100: import java.awt.geom.Point2D;
 101: import java.awt.geom.Rectangle2D;
 102: import java.util.Collections;
 103: import java.util.Iterator;
 104: import java.util.List;
 105: 
 106: import org.jfree.chart.LegendItemCollection;
 107: import org.jfree.chart.axis.AxisSpace;
 108: import org.jfree.chart.axis.AxisState;
 109: import org.jfree.chart.axis.NumberAxis;
 110: import org.jfree.chart.axis.ValueAxis;
 111: import org.jfree.chart.event.PlotChangeEvent;
 112: import org.jfree.chart.event.PlotChangeListener;
 113: import org.jfree.chart.renderer.xy.XYItemRenderer;
 114: import org.jfree.data.Range;
 115: import org.jfree.ui.RectangleEdge;
 116: import org.jfree.ui.RectangleInsets;
 117: import org.jfree.util.ObjectUtilities;
 118: 
 119: /**
 120:  * An extension of {@link XYPlot} that contains multiple subplots that share a
 121:  * common range axis.
 122:  */
 123: public class CombinedRangeXYPlot extends XYPlot
 124:         implements PlotChangeListener {
 125: 
 126:     /** For serialization. */
 127:     private static final long serialVersionUID = -5177814085082031168L;
 128: 
 129:     /** Storage for the subplot references. */
 130:     private List subplots;
 131: 
 132:     /** Total weight of all charts. */
 133:     private int totalWeight = 0;
 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: 
 141:     /**
 142:      * Default constructor.
 143:      */
 144:     public CombinedRangeXYPlot() {
 145:         this(new NumberAxis());
 146:     }
 147: 
 148:     /**
 149:      * Creates a new plot.
 150:      *
 151:      * @param rangeAxis  the shared axis.
 152:      */
 153:     public CombinedRangeXYPlot(ValueAxis rangeAxis) {
 154: 
 155:         super(null, // no data in the parent plot
 156:               null,
 157:               rangeAxis,
 158:               null);
 159: 
 160:         this.subplots = new java.util.ArrayList();
 161: 
 162:     }
 163: 
 164:     /**
 165:      * Returns a string describing the type of plot.
 166:      *
 167:      * @return The type of plot.
 168:      */
 169:     public String getPlotType() {
 170:         return localizationResources.getString("Combined_Range_XYPlot");
 171:     }
 172: 
 173:     /**
 174:      * Returns the space between subplots.
 175:      *
 176:      * @return The gap
 177:      */
 178:     public double getGap() {
 179:         return this.gap;
 180:     }
 181: 
 182:     /**
 183:      * Sets the amount of space between subplots.
 184:      *
 185:      * @param gap  the gap between subplots
 186:      */
 187:     public void setGap(double gap) {
 188:         this.gap = gap;
 189:     }
 190: 
 191:     /**
 192:      * Adds a subplot, with a default 'weight' of 1.
 193:      * <br><br>
 194:      * You must ensure that the subplot has a non-null domain axis.  The range
 195:      * axis for the subplot will be set to <code>null</code>.
 196:      *
 197:      * @param subplot  the subplot.
 198:      */
 199:     public void add(XYPlot subplot) {
 200:         add(subplot, 1);
 201:     }
 202: 
 203:     /**
 204:      * Adds a subplot with a particular weight (greater than or equal to one).
 205:      * The weight determines how much space is allocated to the subplot
 206:      * relative to all the other subplots.
 207:      * <br><br>
 208:      * You must ensure that the subplot has a non-null domain axis.  The range
 209:      * axis for the subplot will be set to <code>null</code>.
 210:      *
 211:      * @param subplot  the subplot.
 212:      * @param weight  the weight (must be 1 or greater).
 213:      */
 214:     public void add(XYPlot subplot, int weight) {
 215: 
 216:         // verify valid weight
 217:         if (weight <= 0) {
 218:             String msg = "The 'weight' must be positive.";
 219:             throw new IllegalArgumentException(msg);
 220:         }
 221: 
 222:         // store the plot and its weight
 223:         subplot.setParent(this);
 224:         subplot.setWeight(weight);
 225:         subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0));
 226:         subplot.setRangeAxis(null);
 227:         subplot.addChangeListener(this);
 228:         this.subplots.add(subplot);
 229: 
 230:         // keep track of total weights
 231:         this.totalWeight += weight;
 232:         configureRangeAxes();
 233:         fireChangeEvent();
 234: 
 235:     }
 236: 
 237:     /**
 238:      * Removes a subplot from the combined chart.
 239:      *
 240:      * @param subplot  the subplot (<code>null</code> not permitted).
 241:      */
 242:     public void remove(XYPlot subplot) {
 243:         if (subplot == null) {
 244:             throw new IllegalArgumentException(" Null 'subplot' argument.");
 245:         }
 246:         int position = -1;
 247:         int size = this.subplots.size();
 248:         int i = 0;
 249:         while (position == -1 && i < size) {
 250:             if (this.subplots.get(i) == subplot) {
 251:                 position = i;
 252:             }
 253:             i++;
 254:         }
 255:         if (position != -1) {
 256:             this.subplots.remove(position);
 257:             subplot.setParent(null);
 258:             subplot.removeChangeListener(this);
 259:             this.totalWeight -= subplot.getWeight();
 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:     protected AxisSpace calculateAxisSpace(Graphics2D g2,
 289:                                            Rectangle2D plotArea) {
 290: 
 291:         AxisSpace space = new AxisSpace();
 292:         PlotOrientation orientation = getOrientation();
 293: 
 294:         // work out the space required by the domain axis...
 295:         AxisSpace fixed = getFixedRangeAxisSpace();
 296:         if (fixed != null) {
 297:             if (orientation == PlotOrientation.VERTICAL) {
 298:                 space.setLeft(fixed.getLeft());
 299:                 space.setRight(fixed.getRight());
 300:             }
 301:             else if (orientation == PlotOrientation.HORIZONTAL) {
 302:                 space.setTop(fixed.getTop());
 303:                 space.setBottom(fixed.getBottom());
 304:             }
 305:         }
 306:         else {
 307:             ValueAxis valueAxis = getRangeAxis();
 308:             RectangleEdge valueEdge = Plot.resolveRangeAxisLocation(
 309:                 getRangeAxisLocation(), orientation
 310:             );
 311:             if (valueAxis != null) {
 312:                 space = valueAxis.reserveSpace(g2, this, plotArea, valueEdge,
 313:                         space);
 314:             }
 315:         }
 316: 
 317:         Rectangle2D adjustedPlotArea = space.shrink(plotArea, null);
 318:         // work out the maximum height or width of the non-shared axes...
 319:         int n = this.subplots.size();
 320: 
 321:         // calculate plotAreas of all sub-plots, maximum vertical/horizontal
 322:         // axis width/height
 323:         this.subplotAreas = new Rectangle2D[n];
 324:         double x = adjustedPlotArea.getX();
 325:         double y = adjustedPlotArea.getY();
 326:         double usableSize = 0.0;
 327:         if (orientation == PlotOrientation.VERTICAL) {
 328:             usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1);
 329:         }
 330:         else if (orientation == PlotOrientation.HORIZONTAL) {
 331:             usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1);
 332:         }
 333: 
 334:         for (int i = 0; i < n; i++) {
 335:             XYPlot plot = (XYPlot) this.subplots.get(i);
 336: 
 337:             // calculate sub-plot area
 338:             if (orientation == PlotOrientation.VERTICAL) {
 339:                 double w = usableSize * plot.getWeight() / this.totalWeight;
 340:                 this.subplotAreas[i] = new Rectangle2D.Double(x, y, w,
 341:                         adjustedPlotArea.getHeight());
 342:                 x = x + w + this.gap;
 343:             }
 344:             else if (orientation == PlotOrientation.HORIZONTAL) {
 345:                 double h = usableSize * plot.getWeight() / this.totalWeight;
 346:                 this.subplotAreas[i] = new Rectangle2D.Double(x, y,
 347:                         adjustedPlotArea.getWidth(), h);
 348:                 y = y + h + this.gap;
 349:             }
 350: 
 351:             AxisSpace subSpace = plot.calculateDomainAxisSpace(g2,
 352:                     this.subplotAreas[i], null);
 353:             space.ensureAtLeast(subSpace);
 354: 
 355:         }
 356: 
 357:         return space;
 358:     }
 359: 
 360:     /**
 361:      * Draws the plot within the specified area on a graphics device.
 362:      *
 363:      * @param g2  the graphics device.
 364:      * @param area  the plot area (in Java2D space).
 365:      * @param anchor  an anchor point in Java2D space (<code>null</code>
 366:      *                permitted).
 367:      * @param parentState  the state from the parent plot, if there is one
 368:      *                     (<code>null</code> permitted).
 369:      * @param info  collects chart drawing information (<code>null</code>
 370:      *              permitted).
 371:      */
 372:     public void draw(Graphics2D g2,
 373:                      Rectangle2D area,
 374:                      Point2D anchor,
 375:                      PlotState parentState,
 376:                      PlotRenderingInfo info) {
 377: 
 378:         // set up info collection...
 379:         if (info != null) {
 380:             info.setPlotArea(area);
 381:         }
 382: 
 383:         // adjust the drawing area for plot insets (if any)...
 384:         RectangleInsets insets = getInsets();
 385:         insets.trim(area);
 386: 
 387:         AxisSpace space = calculateAxisSpace(g2, area);
 388:         Rectangle2D dataArea = space.shrink(area, null);
 389:         //this.axisOffset.trim(dataArea);
 390: 
 391:         // set the width and height of non-shared axis of all sub-plots
 392:         setFixedDomainAxisSpaceForSubplots(space);
 393: 
 394:         // draw the shared axis
 395:         ValueAxis axis = getRangeAxis();
 396:         RectangleEdge edge = getRangeAxisEdge();
 397:         double cursor = RectangleEdge.coordinate(dataArea, edge);
 398:         AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info);
 399: 
 400:         if (parentState == null) {
 401:             parentState = new PlotState();
 402:         }
 403:         parentState.getSharedAxisStates().put(axis, axisState);
 404: 
 405:         // draw all the charts
 406:         for (int i = 0; i < this.subplots.size(); i++) {
 407:             XYPlot plot = (XYPlot) this.subplots.get(i);
 408:             PlotRenderingInfo subplotInfo = null;
 409:             if (info != null) {
 410:                 subplotInfo = new PlotRenderingInfo(info.getOwner());
 411:                 info.addSubplotInfo(subplotInfo);
 412:             }
 413:             plot.draw(g2, this.subplotAreas[i], anchor, parentState,
 414:                     subplotInfo);
 415:         }
 416: 
 417:         if (info != null) {
 418:             info.setDataArea(dataArea);
 419:         }
 420: 
 421:     }
 422: 
 423:     /**
 424:      * Returns a collection of legend items for the plot.
 425:      *
 426:      * @return The legend items.
 427:      */
 428:     public LegendItemCollection getLegendItems() {
 429:         LegendItemCollection result = getFixedLegendItems();
 430:         if (result == null) {
 431:             result = new LegendItemCollection();
 432: 
 433:             if (this.subplots != null) {
 434:                 Iterator iterator = this.subplots.iterator();
 435:                 while (iterator.hasNext()) {
 436:                     XYPlot plot = (XYPlot) iterator.next();
 437:                     LegendItemCollection more = plot.getLegendItems();
 438:                     result.addAll(more);
 439:                 }
 440:             }
 441:         }
 442:         return result;
 443:     }
 444: 
 445:     /**
 446:      * Multiplies the range on the domain axis/axes by the specified factor.
 447:      *
 448:      * @param factor  the zoom factor.
 449:      * @param info  the plot rendering info (<code>null</code> not permitted).
 450:      * @param source  the source point (<code>null</code> not permitted).
 451:      */
 452:     public void zoomDomainAxes(double factor, PlotRenderingInfo info,
 453:                                Point2D source) {
 454:         zoomDomainAxes(factor, info, source, false);
 455:     }
 456: 
 457:     /**
 458:      * Multiplies the range on the domain axis/axes by the specified factor.
 459:      *
 460:      * @param factor  the zoom factor.
 461:      * @param info  the plot rendering info (<code>null</code> not permitted).
 462:      * @param source  the source point (<code>null</code> not permitted).
 463:      * @param useAnchor  zoom about the anchor point?
 464:      */
 465:     public void zoomDomainAxes(double factor, PlotRenderingInfo info,
 466:                                Point2D source, boolean useAnchor) {
 467:         // delegate 'info' and 'source' argument checks...
 468:         XYPlot subplot = findSubplot(info, source);
 469:         if (subplot != null) {
 470:             subplot.zoomDomainAxes(factor, info, source, useAnchor);
 471:         }
 472:         else {
 473:             // if the source point doesn't fall within a subplot, we do the
 474:             // zoom on all subplots...
 475:             Iterator iterator = getSubplots().iterator();
 476:             while (iterator.hasNext()) {
 477:                 subplot = (XYPlot) iterator.next();
 478:                 subplot.zoomDomainAxes(factor, info, source, useAnchor);
 479:             }
 480:         }
 481:     }
 482: 
 483:     /**
 484:      * Zooms in on the domain axes.
 485:      *
 486:      * @param lowerPercent  the lower bound.
 487:      * @param upperPercent  the upper bound.
 488:      * @param info  the plot rendering info (<code>null</code> not permitted).
 489:      * @param source  the source point (<code>null</code> not permitted).
 490:      */
 491:     public void zoomDomainAxes(double lowerPercent, double upperPercent,
 492:                                PlotRenderingInfo info, Point2D source) {
 493:         // delegate 'info' and 'source' argument checks...
 494:         XYPlot subplot = findSubplot(info, source);
 495:         if (subplot != null) {
 496:             subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source);
 497:         }
 498:         else {
 499:             // if the source point doesn't fall within a subplot, we do the
 500:             // zoom on all subplots...
 501:             Iterator iterator = getSubplots().iterator();
 502:             while (iterator.hasNext()) {
 503:                 subplot = (XYPlot) iterator.next();
 504:                 subplot.zoomDomainAxes(lowerPercent, upperPercent, info,
 505:                         source);
 506:             }
 507:         }
 508:     }
 509: 
 510:     /**
 511:      * Returns the subplot (if any) that contains the (x, y) point (specified
 512:      * in Java2D space).
 513:      *
 514:      * @param info  the chart rendering info (<code>null</code> not permitted).
 515:      * @param source  the source point (<code>null</code> not permitted).
 516:      *
 517:      * @return A subplot (possibly <code>null</code>).
 518:      */
 519:     public XYPlot findSubplot(PlotRenderingInfo info, Point2D source) {
 520:         if (info == null) {
 521:             throw new IllegalArgumentException("Null 'info' argument.");
 522:         }
 523:         if (source == null) {
 524:             throw new IllegalArgumentException("Null 'source' argument.");
 525:         }
 526:         XYPlot result = null;
 527:         int subplotIndex = info.getSubplotIndex(source);
 528:         if (subplotIndex >= 0) {
 529:             result =  (XYPlot) this.subplots.get(subplotIndex);
 530:         }
 531:         return result;
 532:     }
 533: 
 534:     /**
 535:      * Sets the item renderer FOR ALL SUBPLOTS.  Registered listeners are
 536:      * notified that the plot has been modified.
 537:      * <P>
 538:      * Note: usually you will want to set the renderer independently for each
 539:      * subplot, which is NOT what this method does.
 540:      *
 541:      * @param renderer the new renderer.
 542:      */
 543:     public void setRenderer(XYItemRenderer renderer) {
 544: 
 545:         super.setRenderer(renderer);  // not strictly necessary, since the
 546:                                       // renderer set for the
 547:                                       // parent plot is not used
 548: 
 549:         Iterator iterator = this.subplots.iterator();
 550:         while (iterator.hasNext()) {
 551:             XYPlot plot = (XYPlot) iterator.next();
 552:             plot.setRenderer(renderer);
 553:         }
 554: 
 555:     }
 556: 
 557:     /**
 558:      * Sets the orientation for the plot (and all its subplots).
 559:      *
 560:      * @param orientation  the orientation.
 561:      */
 562:     public void setOrientation(PlotOrientation orientation) {
 563: 
 564:         super.setOrientation(orientation);
 565: 
 566:         Iterator iterator = this.subplots.iterator();
 567:         while (iterator.hasNext()) {
 568:             XYPlot plot = (XYPlot) iterator.next();
 569:             plot.setOrientation(orientation);
 570:         }
 571: 
 572:     }
 573: 
 574:     /**
 575:      * Returns a range representing the extent of the data values in this plot
 576:      * (obtained from the subplots) that will be rendered against the specified
 577:      * axis.  NOTE: This method is intended for internal JFreeChart use, and
 578:      * is public only so that code in the axis classes can call it.  Since
 579:      * only the range axis is shared between subplots, the JFreeChart code
 580:      * will only call this method for the range values (although this is not
 581:      * checked/enforced).
 582:      *
 583:      * @param axis  the axis.
 584:      *
 585:      * @return The range.
 586:      */
 587:     public Range getDataRange(ValueAxis axis) {
 588:         Range result = null;
 589:         if (this.subplots != null) {
 590:             Iterator iterator = this.subplots.iterator();
 591:             while (iterator.hasNext()) {
 592:                 XYPlot subplot = (XYPlot) iterator.next();
 593:                 result = Range.combine(result, subplot.getDataRange(axis));
 594:             }
 595:         }
 596:         return result;
 597:     }
 598: 
 599:     /**
 600:      * Sets the space (width or height, depending on the orientation of the
 601:      * plot) for the domain axis of each subplot.
 602:      *
 603:      * @param space  the space.
 604:      */
 605:     protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) {
 606:         Iterator iterator = this.subplots.iterator();
 607:         while (iterator.hasNext()) {
 608:             XYPlot plot = (XYPlot) iterator.next();
 609:             plot.setFixedDomainAxisSpace(space, false);
 610:         }
 611:     }
 612: 
 613:     /**
 614:      * Handles a 'click' on the plot by updating the anchor values...
 615:      *
 616:      * @param x  x-coordinate, where the click occured.
 617:      * @param y  y-coordinate, where the click occured.
 618:      * @param info  object containing information about the plot dimensions.
 619:      */
 620:     public void handleClick(int x, int y, PlotRenderingInfo info) {
 621: 
 622:         Rectangle2D dataArea = info.getDataArea();
 623:         if (dataArea.contains(x, y)) {
 624:             for (int i = 0; i < this.subplots.size(); i++) {
 625:                 XYPlot subplot = (XYPlot) this.subplots.get(i);
 626:                 PlotRenderingInfo subplotInfo = info.getSubplotInfo(i);
 627:                 subplot.handleClick(x, y, subplotInfo);
 628:             }
 629:         }
 630: 
 631:     }
 632: 
 633:     /**
 634:      * Receives a {@link PlotChangeEvent} and responds by notifying all
 635:      * listeners.
 636:      *
 637:      * @param event  the event.
 638:      */
 639:     public void plotChanged(PlotChangeEvent event) {
 640:         notifyListeners(event);
 641:     }
 642: 
 643:     /**
 644:      * Tests this plot for equality with another object.
 645:      *
 646:      * @param obj  the other object.
 647:      *
 648:      * @return <code>true</code> or <code>false</code>.
 649:      */
 650:     public boolean equals(Object obj) {
 651: 
 652:         if (obj == this) {
 653:             return true;
 654:         }
 655: 
 656:         if (!(obj instanceof CombinedRangeXYPlot)) {
 657:             return false;
 658:         }
 659:         if (!super.equals(obj)) {
 660:             return false;
 661:         }
 662:         CombinedRangeXYPlot that = (CombinedRangeXYPlot) obj;
 663:         if (!ObjectUtilities.equal(this.subplots, that.subplots)) {
 664:             return false;
 665:         }
 666:         if (this.totalWeight != that.totalWeight) {
 667:             return false;
 668:         }
 669:         if (this.gap != that.gap) {
 670:             return false;
 671:         }
 672:         return true;
 673:     }
 674: 
 675:     /**
 676:      * Returns a clone of the plot.
 677:      *
 678:      * @return A clone.
 679:      *
 680:      * @throws CloneNotSupportedException  this class will not throw this
 681:      *         exception, but subclasses (if any) might.
 682:      */
 683:     public Object clone() throws CloneNotSupportedException {
 684: 
 685:         CombinedRangeXYPlot result = (CombinedRangeXYPlot) super.clone();
 686:         result.subplots = (List) ObjectUtilities.deepClone(this.subplots);
 687:         for (Iterator it = result.subplots.iterator(); it.hasNext();) {
 688:             Plot child = (Plot) it.next();
 689:             child.setParent(result);
 690:         }
 691: 
 692:         // after setting up all the subplots, the shared range axis may need
 693:         // reconfiguring
 694:         ValueAxis rangeAxis = result.getRangeAxis();
 695:         if (rangeAxis != null) {
 696:             rangeAxis.configure();
 697:         }
 698: 
 699:         return result;
 700:     }
 701: 
 702: }