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