Source for org.jfree.chart.plot.CombinedDomainXYPlot

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