0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * -----------
0028: * XYPlot.java
0029: * -----------
0030: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Craig MacFarlane;
0034: * Mark Watson (www.markwatson.com);
0035: * Jonathan Nash;
0036: * Gideon Krause;
0037: * Klaus Rheinwald;
0038: * Xavier Poinsard;
0039: * Richard Atkinson;
0040: * Arnaud Lelievre;
0041: * Nicolas Brodu;
0042: * Eduardo Ramalho;
0043: * Sergei Ivanov;
0044: *
0045: * $Id: XYPlot.java,v 1.44.2.29 2007/06/07 12:49:36 mungady Exp $
0046: *
0047: * Changes (from 21-Jun-2001)
0048: * --------------------------
0049: * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
0050: * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
0051: * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
0052: * 19-Oct-2001 : Removed the code for drawing the visual representation of each
0053: * data point into a separate class StandardXYItemRenderer.
0054: * This will make it easier to add variations to the way the
0055: * charts are drawn. Based on code contributed by Mark
0056: * Watson (DG);
0057: * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
0058: * 20-Nov-2001 : Fixed clipping bug that shows up when chart is displayed
0059: * inside JScrollPane (DG);
0060: * 12-Dec-2001 : Removed unnecessary 'throws' clauses from constructor (DG);
0061: * 13-Dec-2001 : Added skeleton code for tooltips. Added new constructor. (DG);
0062: * 16-Jan-2002 : Renamed the tooltips class (DG);
0063: * 22-Jan-2002 : Added DrawInfo class, incorporating tooltips and crosshairs.
0064: * Crosshairs based on code by Jonathan Nash (DG);
0065: * 05-Feb-2002 : Added alpha-transparency setting based on code by Sylvain
0066: * Vieujot (DG);
0067: * 26-Feb-2002 : Updated getMinimumXXX() and getMaximumXXX() methods to handle
0068: * special case when chart is null (DG);
0069: * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG);
0070: * 28-Mar-2002 : The plot now registers with the renderer as a property change
0071: * listener. Also added a new constructor (DG);
0072: * 09-Apr-2002 : Removed the transRangeZero from the renderer.drawItem()
0073: * method. Moved the tooltip generator into the renderer (DG);
0074: * 23-Apr-2002 : Fixed bug in methods for drawing horizontal and vertical
0075: * lines (DG);
0076: * 13-May-2002 : Small change to the draw() method so that it works for
0077: * OverlaidXYPlot also (DG);
0078: * 25-Jun-2002 : Removed redundant import (DG);
0079: * 20-Aug-2002 : Renamed getItemRenderer() --> getRenderer(), and
0080: * setXYItemRenderer() --> setRenderer() (DG);
0081: * 28-Aug-2002 : Added mechanism for (optional) plot annotations (DG);
0082: * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
0083: * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
0084: * these were set in the axes) (DG);
0085: * 09-Jan-2003 : Further additions to the grid settings, plus integrated plot
0086: * border bug fix contributed by Gideon Krause (DG);
0087: * 22-Jan-2003 : Removed monolithic constructor (DG);
0088: * 04-Mar-2003 : Added 'no data' message, see bug report 691634. Added
0089: * secondary range markers using code contributed by Klaus
0090: * Rheinwald (DG);
0091: * 26-Mar-2003 : Implemented Serializable (DG);
0092: * 03-Apr-2003 : Added setDomainAxisLocation() method (DG);
0093: * 30-Apr-2003 : Moved annotation drawing into a separate method (DG);
0094: * 01-May-2003 : Added multi-pass mechanism for renderers (DG);
0095: * 02-May-2003 : Changed axis locations from int to AxisLocation (DG);
0096: * 15-May-2003 : Added an orientation attribute (DG);
0097: * 02-Jun-2003 : Removed range axis compatibility test (DG);
0098: * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
0099: * Services Ltd) (DG);
0100: * 26-Jun-2003 : Fixed bug (757303) in getDataRange() method (DG);
0101: * 02-Jul-2003 : Added patch from bug report 698646 (secondary axes for
0102: * overlaid plots) (DG);
0103: * 23-Jul-2003 : Added support for multiple secondary datasets, axes and
0104: * renderers (DG);
0105: * 27-Jul-2003 : Added support for stacked XY area charts (RA);
0106: * 19-Aug-2003 : Implemented Cloneable (DG);
0107: * 01-Sep-2003 : Fixed bug where change to secondary datasets didn't generate
0108: * change event (797466) (DG)
0109: * 08-Sep-2003 : Added internationalization via use of properties
0110: * resourceBundle (RFE 690236) (AL);
0111: * 08-Sep-2003 : Changed ValueAxis API (DG);
0112: * 08-Sep-2003 : Fixes for serialization (NB);
0113: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
0114: * 17-Sep-2003 : Fixed zooming to include secondary domain axes (DG);
0115: * 18-Sep-2003 : Added getSecondaryDomainAxisCount() and
0116: * getSecondaryRangeAxisCount() methods suggested by Eduardo
0117: * Ramalho (RFE 808548) (DG);
0118: * 23-Sep-2003 : Split domain and range markers into foreground and
0119: * background (DG);
0120: * 06-Oct-2003 : Fixed bug in clearDomainMarkers() and clearRangeMarkers()
0121: * methods. Fixed bug (815876) in addSecondaryRangeMarker()
0122: * method. Added new addSecondaryDomainMarker methods (see bug
0123: * id 815869) (DG);
0124: * 10-Nov-2003 : Added getSecondaryDomain/RangeAxisMappedToDataset() methods
0125: * requested by Eduardo Ramalho (DG);
0126: * 24-Nov-2003 : Removed unnecessary notification when updating axis anchor
0127: * values (DG);
0128: * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
0129: * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
0130: * 12-Mar-2004 : Fixed bug where primary renderer is always used to determine
0131: * range type (DG);
0132: * 22-Mar-2004 : Fixed cloning bug (DG);
0133: * 23-Mar-2004 : Fixed more cloning bugs (DG);
0134: * 07-Apr-2004 : Fixed problem with axis range when the secondary renderer is
0135: * stacked, see this post in the forum:
0136: * http://www.jfree.org/phpBB2/viewtopic.php?t=8204 (DG);
0137: * 07-Apr-2004 : Added get/setDatasetRenderingOrder() methods (DG);
0138: * 26-Apr-2004 : Added option to fill quadrant areas in the background of the
0139: * plot (DG);
0140: * 27-Apr-2004 : Removed major distinction between primary and secondary
0141: * datasets, renderers and axes (DG);
0142: * 30-Apr-2004 : Modified to make use of the new getRangeExtent() method in the
0143: * renderer interface (DG);
0144: * 13-May-2004 : Added optional fixedLegendItems attribute (DG);
0145: * 19-May-2004 : Added indexOf() method (DG);
0146: * 03-Jun-2004 : Fixed zooming bug (DG);
0147: * 18-Aug-2004 : Added removedAnnotation() method (by tkram01) (DG);
0148: * 05-Oct-2004 : Modified storage type for dataset-to-axis maps (DG);
0149: * 06-Oct-2004 : Modified getDataRange() method to use renderer to determine
0150: * the x-value range (now matches behaviour for y-values). Added
0151: * getDomainAxisIndex() method (DG);
0152: * 12-Nov-2004 : Implemented new Zoomable interface (DG);
0153: * 25-Nov-2004 : Small update to clone() implementation (DG);
0154: * 22-Feb-2005 : Changed axis offsets from Spacer --> RectangleInsets (DG);
0155: * 24-Feb-2005 : Added indexOf(XYItemRenderer) method (DG);
0156: * 21-Mar-2005 : Register plot as change listener in setRenderer() method (DG);
0157: * 21-Apr-2005 : Added get/setSeriesRenderingOrder() methods (ET);
0158: * 26-Apr-2005 : Removed LOGGER (DG);
0159: * 04-May-2005 : Fixed serialization of domain and range markers (DG);
0160: * 05-May-2005 : Removed unused draw() method (DG);
0161: * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
0162: * RFE 1183100 (DG);
0163: * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
0164: * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
0165: * 01-Jun-2005 : Added clearDomainMarkers(int) method to match
0166: * clearRangeMarkers(int) (DG);
0167: * 06-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
0168: * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
0169: * 06-Jul-2005 : Fixed crosshair bug (id = 1233336) (DG);
0170: * ------------- JFREECHART 1.0.x ---------------------------------------------
0171: * 26-Jan-2006 : Added getAnnotations() method (DG);
0172: * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
0173: * 13-Oct-2006 : Fixed initialisation of CrosshairState - see bug report
0174: * 1565168 (DG);
0175: * 22-Nov-2006 : Fixed equals() and cloning() for quadrant attributes, plus
0176: * API doc updates (DG);
0177: * 29-Nov-2006 : Added argument checks (DG);
0178: * 15-Jan-2007 : Fixed bug in drawRangeMarkers() (DG);
0179: * 07-Feb-2007 : Fixed bug 1654215, renderer with no dataset (DG);
0180: * 26-Feb-2007 : Added missing setDomainAxisLocation() and
0181: * setRangeAxisLocation() methods (DG);
0182: * 02-Mar-2007 : Fix for crosshair positioning with horizontal orientation
0183: * (see patch 1671648 by Sergei Ivanov) (DG);
0184: * 13-Mar-2007 : Added null argument checks for crosshair attributes (DG);
0185: * 23-Mar-2007 : Added domain zero base line facility (DG);
0186: * 04-May-2007 : Render only visible data items if possible (DG);
0187: * 24-May-2007 : Fixed bug in render method for an empty series (DG);
0188: * 07-Jun-2007 : Modified drawBackground() to pass orientation to
0189: * fillBackground() for handling GradientPaint (DG);
0190: *
0191: */
0192:
0193: package org.jfree.chart.plot;
0194:
0195: import java.awt.AlphaComposite;
0196: import java.awt.BasicStroke;
0197: import java.awt.Color;
0198: import java.awt.Composite;
0199: import java.awt.Graphics2D;
0200: import java.awt.Paint;
0201: import java.awt.Shape;
0202: import java.awt.Stroke;
0203: import java.awt.geom.Line2D;
0204: import java.awt.geom.Point2D;
0205: import java.awt.geom.Rectangle2D;
0206: import java.io.IOException;
0207: import java.io.ObjectInputStream;
0208: import java.io.ObjectOutputStream;
0209: import java.io.Serializable;
0210: import java.util.ArrayList;
0211: import java.util.Collection;
0212: import java.util.Collections;
0213: import java.util.HashMap;
0214: import java.util.Iterator;
0215: import java.util.List;
0216: import java.util.Map;
0217: import java.util.ResourceBundle;
0218: import java.util.Set;
0219: import java.util.TreeMap;
0220:
0221: import org.jfree.chart.LegendItem;
0222: import org.jfree.chart.LegendItemCollection;
0223: import org.jfree.chart.annotations.XYAnnotation;
0224: import org.jfree.chart.axis.Axis;
0225: import org.jfree.chart.axis.AxisCollection;
0226: import org.jfree.chart.axis.AxisLocation;
0227: import org.jfree.chart.axis.AxisSpace;
0228: import org.jfree.chart.axis.AxisState;
0229: import org.jfree.chart.axis.ValueAxis;
0230: import org.jfree.chart.axis.ValueTick;
0231: import org.jfree.chart.event.ChartChangeEventType;
0232: import org.jfree.chart.event.PlotChangeEvent;
0233: import org.jfree.chart.event.RendererChangeEvent;
0234: import org.jfree.chart.event.RendererChangeListener;
0235: import org.jfree.chart.renderer.RendererUtilities;
0236: import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
0237: import org.jfree.chart.renderer.xy.XYItemRenderer;
0238: import org.jfree.chart.renderer.xy.XYItemRendererState;
0239: import org.jfree.data.Range;
0240: import org.jfree.data.general.Dataset;
0241: import org.jfree.data.general.DatasetChangeEvent;
0242: import org.jfree.data.general.DatasetUtilities;
0243: import org.jfree.data.xy.XYDataset;
0244: import org.jfree.io.SerialUtilities;
0245: import org.jfree.ui.Layer;
0246: import org.jfree.ui.RectangleEdge;
0247: import org.jfree.ui.RectangleInsets;
0248: import org.jfree.util.ObjectList;
0249: import org.jfree.util.ObjectUtilities;
0250: import org.jfree.util.PaintUtilities;
0251: import org.jfree.util.PublicCloneable;
0252:
0253: /**
0254: * A general class for plotting data in the form of (x, y) pairs. This plot can
0255: * use data from any class that implements the {@link XYDataset} interface.
0256: * <P>
0257: * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
0258: * on the plot. By using different renderers, various chart types can be
0259: * produced.
0260: * <p>
0261: * The {@link org.jfree.chart.ChartFactory} class contains static methods for
0262: * creating pre-configured charts.
0263: */
0264: public class XYPlot extends Plot implements ValueAxisPlot, Zoomable,
0265: RendererChangeListener, Cloneable, PublicCloneable,
0266: Serializable {
0267:
0268: /** For serialization. */
0269: private static final long serialVersionUID = 7044148245716569264L;
0270:
0271: /** The default grid line stroke. */
0272: public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(
0273: 0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f,
0274: new float[] { 2.0f, 2.0f }, 0.0f);
0275:
0276: /** The default grid line paint. */
0277: public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
0278:
0279: /** The default crosshair visibility. */
0280: public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
0281:
0282: /** The default crosshair stroke. */
0283: public static final Stroke DEFAULT_CROSSHAIR_STROKE = DEFAULT_GRIDLINE_STROKE;
0284:
0285: /** The default crosshair paint. */
0286: public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
0287:
0288: /** The resourceBundle for the localization. */
0289: protected static ResourceBundle localizationResources = ResourceBundle
0290: .getBundle("org.jfree.chart.plot.LocalizationBundle");
0291:
0292: /** The plot orientation. */
0293: private PlotOrientation orientation;
0294:
0295: /** The offset between the data area and the axes. */
0296: private RectangleInsets axisOffset;
0297:
0298: /** The domain axis / axes (used for the x-values). */
0299: private ObjectList domainAxes;
0300:
0301: /** The domain axis locations. */
0302: private ObjectList domainAxisLocations;
0303:
0304: /** The range axis (used for the y-values). */
0305: private ObjectList rangeAxes;
0306:
0307: /** The range axis location. */
0308: private ObjectList rangeAxisLocations;
0309:
0310: /** Storage for the datasets. */
0311: private ObjectList datasets;
0312:
0313: /** Storage for the renderers. */
0314: private ObjectList renderers;
0315:
0316: /**
0317: * Storage for keys that map datasets/renderers to domain axes. If the
0318: * map contains no entry for a dataset, it is assumed to map to the
0319: * primary domain axis (index = 0).
0320: */
0321: private Map datasetToDomainAxisMap;
0322:
0323: /**
0324: * Storage for keys that map datasets/renderers to range axes. If the
0325: * map contains no entry for a dataset, it is assumed to map to the
0326: * primary domain axis (index = 0).
0327: */
0328: private Map datasetToRangeAxisMap;
0329:
0330: /** The origin point for the quadrants (if drawn). */
0331: private transient Point2D quadrantOrigin = new Point2D.Double(0.0,
0332: 0.0);
0333:
0334: /** The paint used for each quadrant. */
0335: private transient Paint[] quadrantPaint = new Paint[] { null, null,
0336: null, null };
0337:
0338: /** A flag that controls whether the domain grid-lines are visible. */
0339: private boolean domainGridlinesVisible;
0340:
0341: /** The stroke used to draw the domain grid-lines. */
0342: private transient Stroke domainGridlineStroke;
0343:
0344: /** The paint used to draw the domain grid-lines. */
0345: private transient Paint domainGridlinePaint;
0346:
0347: /** A flag that controls whether the range grid-lines are visible. */
0348: private boolean rangeGridlinesVisible;
0349:
0350: /** The stroke used to draw the range grid-lines. */
0351: private transient Stroke rangeGridlineStroke;
0352:
0353: /** The paint used to draw the range grid-lines. */
0354: private transient Paint rangeGridlinePaint;
0355:
0356: /**
0357: * A flag that controls whether or not the zero baseline against the domain
0358: * axis is visible.
0359: *
0360: * @since 1.0.5
0361: */
0362: private boolean domainZeroBaselineVisible;
0363:
0364: /**
0365: * The stroke used for the zero baseline against the domain axis.
0366: *
0367: * @since 1.0.5
0368: */
0369: private transient Stroke domainZeroBaselineStroke;
0370:
0371: /**
0372: * The paint used for the zero baseline against the domain axis.
0373: *
0374: * @since 1.0.5
0375: */
0376: private transient Paint domainZeroBaselinePaint;
0377:
0378: /**
0379: * A flag that controls whether or not the zero baseline against the range
0380: * axis is visible.
0381: */
0382: private boolean rangeZeroBaselineVisible;
0383:
0384: /** The stroke used for the zero baseline against the range axis. */
0385: private transient Stroke rangeZeroBaselineStroke;
0386:
0387: /** The paint used for the zero baseline against the range axis. */
0388: private transient Paint rangeZeroBaselinePaint;
0389:
0390: /** A flag that controls whether or not a domain crosshair is drawn..*/
0391: private boolean domainCrosshairVisible;
0392:
0393: /** The domain crosshair value. */
0394: private double domainCrosshairValue;
0395:
0396: /** The pen/brush used to draw the crosshair (if any). */
0397: private transient Stroke domainCrosshairStroke;
0398:
0399: /** The color used to draw the crosshair (if any). */
0400: private transient Paint domainCrosshairPaint;
0401:
0402: /**
0403: * A flag that controls whether or not the crosshair locks onto actual
0404: * data points.
0405: */
0406: private boolean domainCrosshairLockedOnData = true;
0407:
0408: /** A flag that controls whether or not a range crosshair is drawn..*/
0409: private boolean rangeCrosshairVisible;
0410:
0411: /** The range crosshair value. */
0412: private double rangeCrosshairValue;
0413:
0414: /** The pen/brush used to draw the crosshair (if any). */
0415: private transient Stroke rangeCrosshairStroke;
0416:
0417: /** The color used to draw the crosshair (if any). */
0418: private transient Paint rangeCrosshairPaint;
0419:
0420: /**
0421: * A flag that controls whether or not the crosshair locks onto actual
0422: * data points.
0423: */
0424: private boolean rangeCrosshairLockedOnData = true;
0425:
0426: /** A map of lists of foreground markers (optional) for the domain axes. */
0427: private Map foregroundDomainMarkers;
0428:
0429: /** A map of lists of background markers (optional) for the domain axes. */
0430: private Map backgroundDomainMarkers;
0431:
0432: /** A map of lists of foreground markers (optional) for the range axes. */
0433: private Map foregroundRangeMarkers;
0434:
0435: /** A map of lists of background markers (optional) for the range axes. */
0436: private Map backgroundRangeMarkers;
0437:
0438: /**
0439: * A (possibly empty) list of annotations for the plot. The list should
0440: * be initialised in the constructor and never allowed to be
0441: * <code>null</code>.
0442: */
0443: private List annotations;
0444:
0445: /** The paint used for the domain tick bands (if any). */
0446: private transient Paint domainTickBandPaint;
0447:
0448: /** The paint used for the range tick bands (if any). */
0449: private transient Paint rangeTickBandPaint;
0450:
0451: /** The fixed domain axis space. */
0452: private AxisSpace fixedDomainAxisSpace;
0453:
0454: /** The fixed range axis space. */
0455: private AxisSpace fixedRangeAxisSpace;
0456:
0457: /**
0458: * The order of the dataset rendering (REVERSE draws the primary dataset
0459: * last so that it appears to be on top).
0460: */
0461: private DatasetRenderingOrder datasetRenderingOrder = DatasetRenderingOrder.REVERSE;
0462:
0463: /**
0464: * The order of the series rendering (REVERSE draws the primary series
0465: * last so that it appears to be on top).
0466: */
0467: private SeriesRenderingOrder seriesRenderingOrder = SeriesRenderingOrder.REVERSE;
0468:
0469: /**
0470: * The weight for this plot (only relevant if this is a subplot in a
0471: * combined plot).
0472: */
0473: private int weight;
0474:
0475: /**
0476: * An optional collection of legend items that can be returned by the
0477: * getLegendItems() method.
0478: */
0479: private LegendItemCollection fixedLegendItems;
0480:
0481: /**
0482: * Creates a new <code>XYPlot</code> instance with no dataset, no axes and
0483: * no renderer. You should specify these items before using the plot.
0484: */
0485: public XYPlot() {
0486: this (null, null, null, null);
0487: }
0488:
0489: /**
0490: * Creates a new plot with the specified dataset, axes and renderer. Any
0491: * of the arguments can be <code>null</code>, but in that case you should
0492: * take care to specify the value before using the plot (otherwise a
0493: * <code>NullPointerException</code> may be thrown).
0494: *
0495: * @param dataset the dataset (<code>null</code> permitted).
0496: * @param domainAxis the domain axis (<code>null</code> permitted).
0497: * @param rangeAxis the range axis (<code>null</code> permitted).
0498: * @param renderer the renderer (<code>null</code> permitted).
0499: */
0500: public XYPlot(XYDataset dataset, ValueAxis domainAxis,
0501: ValueAxis rangeAxis, XYItemRenderer renderer) {
0502:
0503: super ();
0504:
0505: this .orientation = PlotOrientation.VERTICAL;
0506: this .weight = 1; // only relevant when this is a subplot
0507: this .axisOffset = RectangleInsets.ZERO_INSETS;
0508:
0509: // allocate storage for datasets, axes and renderers (all optional)
0510: this .domainAxes = new ObjectList();
0511: this .domainAxisLocations = new ObjectList();
0512: this .foregroundDomainMarkers = new HashMap();
0513: this .backgroundDomainMarkers = new HashMap();
0514:
0515: this .rangeAxes = new ObjectList();
0516: this .rangeAxisLocations = new ObjectList();
0517: this .foregroundRangeMarkers = new HashMap();
0518: this .backgroundRangeMarkers = new HashMap();
0519:
0520: this .datasets = new ObjectList();
0521: this .renderers = new ObjectList();
0522:
0523: this .datasetToDomainAxisMap = new TreeMap();
0524: this .datasetToRangeAxisMap = new TreeMap();
0525:
0526: this .datasets.set(0, dataset);
0527: if (dataset != null) {
0528: dataset.addChangeListener(this );
0529: }
0530:
0531: this .renderers.set(0, renderer);
0532: if (renderer != null) {
0533: renderer.setPlot(this );
0534: renderer.addChangeListener(this );
0535: }
0536:
0537: this .domainAxes.set(0, domainAxis);
0538: this .mapDatasetToDomainAxis(0, 0);
0539: if (domainAxis != null) {
0540: domainAxis.setPlot(this );
0541: domainAxis.addChangeListener(this );
0542: }
0543: this .domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
0544:
0545: this .rangeAxes.set(0, rangeAxis);
0546: this .mapDatasetToRangeAxis(0, 0);
0547: if (rangeAxis != null) {
0548: rangeAxis.setPlot(this );
0549: rangeAxis.addChangeListener(this );
0550: }
0551: this .rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
0552:
0553: configureDomainAxes();
0554: configureRangeAxes();
0555:
0556: this .domainGridlinesVisible = true;
0557: this .domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
0558: this .domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
0559:
0560: this .domainZeroBaselineVisible = false;
0561: this .domainZeroBaselinePaint = Color.black;
0562: this .domainZeroBaselineStroke = new BasicStroke(0.5f);
0563:
0564: this .rangeGridlinesVisible = true;
0565: this .rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
0566: this .rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
0567:
0568: this .rangeZeroBaselineVisible = false;
0569: this .rangeZeroBaselinePaint = Color.black;
0570: this .rangeZeroBaselineStroke = new BasicStroke(0.5f);
0571:
0572: this .domainCrosshairVisible = false;
0573: this .domainCrosshairValue = 0.0;
0574: this .domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
0575: this .domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
0576:
0577: this .rangeCrosshairVisible = false;
0578: this .rangeCrosshairValue = 0.0;
0579: this .rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
0580: this .rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
0581:
0582: this .annotations = new java.util.ArrayList();
0583:
0584: }
0585:
0586: /**
0587: * Returns the plot type as a string.
0588: *
0589: * @return A short string describing the type of plot.
0590: */
0591: public String getPlotType() {
0592: return localizationResources.getString("XY_Plot");
0593: }
0594:
0595: /**
0596: * Returns the orientation of the plot.
0597: *
0598: * @return The orientation (never <code>null</code>).
0599: *
0600: * @see #setOrientation(PlotOrientation)
0601: */
0602: public PlotOrientation getOrientation() {
0603: return this .orientation;
0604: }
0605:
0606: /**
0607: * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
0608: * all registered listeners.
0609: *
0610: * @param orientation the orientation (<code>null</code> not allowed).
0611: *
0612: * @see #getOrientation()
0613: */
0614: public void setOrientation(PlotOrientation orientation) {
0615: if (orientation == null) {
0616: throw new IllegalArgumentException(
0617: "Null 'orientation' argument.");
0618: }
0619: if (orientation != this .orientation) {
0620: this .orientation = orientation;
0621: notifyListeners(new PlotChangeEvent(this ));
0622: }
0623: }
0624:
0625: /**
0626: * Returns the axis offset.
0627: *
0628: * @return The axis offset (never <code>null</code>).
0629: *
0630: * @see #setAxisOffset(RectangleInsets)
0631: */
0632: public RectangleInsets getAxisOffset() {
0633: return this .axisOffset;
0634: }
0635:
0636: /**
0637: * Sets the axis offsets (gap between the data area and the axes) and sends
0638: * a {@link PlotChangeEvent} to all registered listeners.
0639: *
0640: * @param offset the offset (<code>null</code> not permitted).
0641: *
0642: * @see #getAxisOffset()
0643: */
0644: public void setAxisOffset(RectangleInsets offset) {
0645: if (offset == null) {
0646: throw new IllegalArgumentException(
0647: "Null 'offset' argument.");
0648: }
0649: this .axisOffset = offset;
0650: notifyListeners(new PlotChangeEvent(this ));
0651: }
0652:
0653: /**
0654: * Returns the domain axis with index 0. If the domain axis for this plot
0655: * is <code>null</code>, then the method will return the parent plot's
0656: * domain axis (if there is a parent plot).
0657: *
0658: * @return The domain axis (possibly <code>null</code>).
0659: *
0660: * @see #getDomainAxis(int)
0661: * @see #setDomainAxis(ValueAxis)
0662: */
0663: public ValueAxis getDomainAxis() {
0664: return getDomainAxis(0);
0665: }
0666:
0667: /**
0668: * Returns the domain axis with the specified index, or <code>null</code>.
0669: *
0670: * @param index the axis index.
0671: *
0672: * @return The axis (<code>null</code> possible).
0673: *
0674: * @see #setDomainAxis(int, ValueAxis)
0675: */
0676: public ValueAxis getDomainAxis(int index) {
0677: ValueAxis result = null;
0678: if (index < this .domainAxes.size()) {
0679: result = (ValueAxis) this .domainAxes.get(index);
0680: }
0681: if (result == null) {
0682: Plot parent = getParent();
0683: if (parent instanceof XYPlot) {
0684: XYPlot xy = (XYPlot) parent;
0685: result = xy.getDomainAxis(index);
0686: }
0687: }
0688: return result;
0689: }
0690:
0691: /**
0692: * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
0693: * to all registered listeners.
0694: *
0695: * @param axis the new axis (<code>null</code> permitted).
0696: *
0697: * @see #getDomainAxis()
0698: * @see #setDomainAxis(int, ValueAxis)
0699: */
0700: public void setDomainAxis(ValueAxis axis) {
0701: setDomainAxis(0, axis);
0702: }
0703:
0704: /**
0705: * Sets a domain axis and sends a {@link PlotChangeEvent} to all
0706: * registered listeners.
0707: *
0708: * @param index the axis index.
0709: * @param axis the axis (<code>null</code> permitted).
0710: *
0711: * @see #getDomainAxis(int)
0712: * @see #setRangeAxis(int, ValueAxis)
0713: */
0714: public void setDomainAxis(int index, ValueAxis axis) {
0715: setDomainAxis(index, axis, true);
0716: }
0717:
0718: /**
0719: * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
0720: * all registered listeners.
0721: *
0722: * @param index the axis index.
0723: * @param axis the axis.
0724: * @param notify notify listeners?
0725: *
0726: * @see #getDomainAxis(int)
0727: */
0728: public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
0729: ValueAxis existing = getDomainAxis(index);
0730: if (existing != null) {
0731: existing.removeChangeListener(this );
0732: }
0733: if (axis != null) {
0734: axis.setPlot(this );
0735: }
0736: this .domainAxes.set(index, axis);
0737: if (axis != null) {
0738: axis.configure();
0739: axis.addChangeListener(this );
0740: }
0741: if (notify) {
0742: notifyListeners(new PlotChangeEvent(this ));
0743: }
0744: }
0745:
0746: /**
0747: * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
0748: * to all registered listeners.
0749: *
0750: * @param axes the axes (<code>null</code> not permitted).
0751: *
0752: * @see #setRangeAxes(ValueAxis[])
0753: */
0754: public void setDomainAxes(ValueAxis[] axes) {
0755: for (int i = 0; i < axes.length; i++) {
0756: setDomainAxis(i, axes[i], false);
0757: }
0758: notifyListeners(new PlotChangeEvent(this ));
0759: }
0760:
0761: /**
0762: * Returns the location of the primary domain axis.
0763: *
0764: * @return The location (never <code>null</code>).
0765: *
0766: * @see #setDomainAxisLocation(AxisLocation)
0767: */
0768: public AxisLocation getDomainAxisLocation() {
0769: return (AxisLocation) this .domainAxisLocations.get(0);
0770: }
0771:
0772: /**
0773: * Sets the location of the primary domain axis and sends a
0774: * {@link PlotChangeEvent} to all registered listeners.
0775: *
0776: * @param location the location (<code>null</code> not permitted).
0777: *
0778: * @see #getDomainAxisLocation()
0779: */
0780: public void setDomainAxisLocation(AxisLocation location) {
0781: // delegate...
0782: setDomainAxisLocation(0, location, true);
0783: }
0784:
0785: /**
0786: * Sets the location of the domain axis and, if requested, sends a
0787: * {@link PlotChangeEvent} to all registered listeners.
0788: *
0789: * @param location the location (<code>null</code> not permitted).
0790: * @param notify notify listeners?
0791: *
0792: * @see #getDomainAxisLocation()
0793: */
0794: public void setDomainAxisLocation(AxisLocation location,
0795: boolean notify) {
0796: // delegate...
0797: setDomainAxisLocation(0, location, notify);
0798: }
0799:
0800: /**
0801: * Returns the edge for the primary domain axis (taking into account the
0802: * plot's orientation).
0803: *
0804: * @return The edge.
0805: *
0806: * @see #getDomainAxisLocation()
0807: * @see #getOrientation()
0808: */
0809: public RectangleEdge getDomainAxisEdge() {
0810: return Plot.resolveDomainAxisLocation(getDomainAxisLocation(),
0811: this .orientation);
0812: }
0813:
0814: /**
0815: * Returns the number of domain axes.
0816: *
0817: * @return The axis count.
0818: *
0819: * @see #getRangeAxisCount()
0820: */
0821: public int getDomainAxisCount() {
0822: return this .domainAxes.size();
0823: }
0824:
0825: /**
0826: * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
0827: * to all registered listeners.
0828: *
0829: * @see #clearRangeAxes()
0830: */
0831: public void clearDomainAxes() {
0832: for (int i = 0; i < this .domainAxes.size(); i++) {
0833: ValueAxis axis = (ValueAxis) this .domainAxes.get(i);
0834: if (axis != null) {
0835: axis.removeChangeListener(this );
0836: }
0837: }
0838: this .domainAxes.clear();
0839: notifyListeners(new PlotChangeEvent(this ));
0840: }
0841:
0842: /**
0843: * Configures the domain axes.
0844: */
0845: public void configureDomainAxes() {
0846: for (int i = 0; i < this .domainAxes.size(); i++) {
0847: ValueAxis axis = (ValueAxis) this .domainAxes.get(i);
0848: if (axis != null) {
0849: axis.configure();
0850: }
0851: }
0852: }
0853:
0854: /**
0855: * Returns the location for a domain axis. If this hasn't been set
0856: * explicitly, the method returns the location that is opposite to the
0857: * primary domain axis location.
0858: *
0859: * @param index the axis index.
0860: *
0861: * @return The location (never <code>null</code>).
0862: *
0863: * @see #setDomainAxisLocation(int, AxisLocation)
0864: */
0865: public AxisLocation getDomainAxisLocation(int index) {
0866: AxisLocation result = null;
0867: if (index < this .domainAxisLocations.size()) {
0868: result = (AxisLocation) this .domainAxisLocations.get(index);
0869: }
0870: if (result == null) {
0871: result = AxisLocation.getOpposite(getDomainAxisLocation());
0872: }
0873: return result;
0874: }
0875:
0876: /**
0877: * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
0878: * to all registered listeners.
0879: *
0880: * @param index the axis index.
0881: * @param location the location (<code>null</code> not permitted for index
0882: * 0).
0883: *
0884: * @see #getDomainAxisLocation(int)
0885: */
0886: public void setDomainAxisLocation(int index, AxisLocation location) {
0887: // delegate...
0888: setDomainAxisLocation(index, location, true);
0889: }
0890:
0891: /**
0892: * Sets the axis location for a domain axis and, if requested, sends a
0893: * {@link PlotChangeEvent} to all registered listeners.
0894: *
0895: * @param index the axis index.
0896: * @param location the location (<code>null</code> not permitted for
0897: * index 0).
0898: * @param notify notify listeners?
0899: *
0900: * @since 1.0.5
0901: *
0902: * @see #getDomainAxisLocation(int)
0903: * @see #setRangeAxisLocation(int, AxisLocation, boolean)
0904: */
0905: public void setDomainAxisLocation(int index, AxisLocation location,
0906: boolean notify) {
0907:
0908: if (index == 0 && location == null) {
0909: throw new IllegalArgumentException(
0910: "Null 'location' for index 0 not permitted.");
0911: }
0912: this .domainAxisLocations.set(index, location);
0913: if (notify) {
0914: notifyListeners(new PlotChangeEvent(this ));
0915: }
0916: }
0917:
0918: /**
0919: * Returns the edge for a domain axis.
0920: *
0921: * @param index the axis index.
0922: *
0923: * @return The edge.
0924: *
0925: * @see #getRangeAxisEdge(int)
0926: */
0927: public RectangleEdge getDomainAxisEdge(int index) {
0928: AxisLocation location = getDomainAxisLocation(index);
0929: RectangleEdge result = Plot.resolveDomainAxisLocation(location,
0930: this .orientation);
0931: if (result == null) {
0932: result = RectangleEdge.opposite(getDomainAxisEdge());
0933: }
0934: return result;
0935: }
0936:
0937: /**
0938: * Returns the range axis for the plot. If the range axis for this plot is
0939: * <code>null</code>, then the method will return the parent plot's range
0940: * axis (if there is a parent plot).
0941: *
0942: * @return The range axis.
0943: *
0944: * @see #getRangeAxis(int)
0945: * @see #setRangeAxis(ValueAxis)
0946: */
0947: public ValueAxis getRangeAxis() {
0948: return getRangeAxis(0);
0949: }
0950:
0951: /**
0952: * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
0953: * all registered listeners.
0954: *
0955: * @param axis the axis (<code>null</code> permitted).
0956: *
0957: * @see #getRangeAxis()
0958: * @see #setRangeAxis(int, ValueAxis)
0959: */
0960: public void setRangeAxis(ValueAxis axis) {
0961:
0962: if (axis != null) {
0963: axis.setPlot(this );
0964: }
0965:
0966: // plot is likely registered as a listener with the existing axis...
0967: ValueAxis existing = getRangeAxis();
0968: if (existing != null) {
0969: existing.removeChangeListener(this );
0970: }
0971:
0972: this .rangeAxes.set(0, axis);
0973: if (axis != null) {
0974: axis.configure();
0975: axis.addChangeListener(this );
0976: }
0977: notifyListeners(new PlotChangeEvent(this ));
0978:
0979: }
0980:
0981: /**
0982: * Returns the location of the primary range axis.
0983: *
0984: * @return The location (never <code>null</code>).
0985: *
0986: * @see #setRangeAxisLocation(AxisLocation)
0987: */
0988: public AxisLocation getRangeAxisLocation() {
0989: return (AxisLocation) this .rangeAxisLocations.get(0);
0990: }
0991:
0992: /**
0993: * Sets the location of the primary range axis and sends a
0994: * {@link PlotChangeEvent} to all registered listeners.
0995: *
0996: * @param location the location (<code>null</code> not permitted).
0997: *
0998: * @see #getRangeAxisLocation()
0999: */
1000: public void setRangeAxisLocation(AxisLocation location) {
1001: // delegate...
1002: setRangeAxisLocation(0, location, true);
1003: }
1004:
1005: /**
1006: * Sets the location of the primary range axis and, if requested, sends a
1007: * {@link PlotChangeEvent} to all registered listeners.
1008: *
1009: * @param location the location (<code>null</code> not permitted).
1010: * @param notify notify listeners?
1011: *
1012: * @see #getRangeAxisLocation()
1013: */
1014: public void setRangeAxisLocation(AxisLocation location,
1015: boolean notify) {
1016: // delegate...
1017: setRangeAxisLocation(0, location, notify);
1018: }
1019:
1020: /**
1021: * Returns the edge for the primary range axis.
1022: *
1023: * @return The range axis edge.
1024: *
1025: * @see #getRangeAxisLocation()
1026: * @see #getOrientation()
1027: */
1028: public RectangleEdge getRangeAxisEdge() {
1029: return Plot.resolveRangeAxisLocation(getRangeAxisLocation(),
1030: this .orientation);
1031: }
1032:
1033: /**
1034: * Returns a range axis.
1035: *
1036: * @param index the axis index.
1037: *
1038: * @return The axis (<code>null</code> possible).
1039: *
1040: * @see #setRangeAxis(int, ValueAxis)
1041: */
1042: public ValueAxis getRangeAxis(int index) {
1043: ValueAxis result = null;
1044: if (index < this .rangeAxes.size()) {
1045: result = (ValueAxis) this .rangeAxes.get(index);
1046: }
1047: if (result == null) {
1048: Plot parent = getParent();
1049: if (parent instanceof XYPlot) {
1050: XYPlot xy = (XYPlot) parent;
1051: result = xy.getRangeAxis(index);
1052: }
1053: }
1054: return result;
1055: }
1056:
1057: /**
1058: * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
1059: * listeners.
1060: *
1061: * @param index the axis index.
1062: * @param axis the axis (<code>null</code> permitted).
1063: *
1064: * @see #getRangeAxis(int)
1065: */
1066: public void setRangeAxis(int index, ValueAxis axis) {
1067: setRangeAxis(index, axis, true);
1068: }
1069:
1070: /**
1071: * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
1072: * all registered listeners.
1073: *
1074: * @param index the axis index.
1075: * @param axis the axis (<code>null</code> permitted).
1076: * @param notify notify listeners?
1077: *
1078: * @see #getRangeAxis(int)
1079: */
1080: public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
1081: ValueAxis existing = getRangeAxis(index);
1082: if (existing != null) {
1083: existing.removeChangeListener(this );
1084: }
1085: if (axis != null) {
1086: axis.setPlot(this );
1087: }
1088: this .rangeAxes.set(index, axis);
1089: if (axis != null) {
1090: axis.configure();
1091: axis.addChangeListener(this );
1092: }
1093: if (notify) {
1094: notifyListeners(new PlotChangeEvent(this ));
1095: }
1096: }
1097:
1098: /**
1099: * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
1100: * to all registered listeners.
1101: *
1102: * @param axes the axes (<code>null</code> not permitted).
1103: *
1104: * @see #setDomainAxes(ValueAxis[])
1105: */
1106: public void setRangeAxes(ValueAxis[] axes) {
1107: for (int i = 0; i < axes.length; i++) {
1108: setRangeAxis(i, axes[i], false);
1109: }
1110: notifyListeners(new PlotChangeEvent(this ));
1111: }
1112:
1113: /**
1114: * Returns the number of range axes.
1115: *
1116: * @return The axis count.
1117: *
1118: * @see #getDomainAxisCount()
1119: */
1120: public int getRangeAxisCount() {
1121: return this .rangeAxes.size();
1122: }
1123:
1124: /**
1125: * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1126: * to all registered listeners.
1127: *
1128: * @see #clearDomainAxes()
1129: */
1130: public void clearRangeAxes() {
1131: for (int i = 0; i < this .rangeAxes.size(); i++) {
1132: ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
1133: if (axis != null) {
1134: axis.removeChangeListener(this );
1135: }
1136: }
1137: this .rangeAxes.clear();
1138: notifyListeners(new PlotChangeEvent(this ));
1139: }
1140:
1141: /**
1142: * Configures the range axes.
1143: *
1144: * @see #configureDomainAxes()
1145: */
1146: public void configureRangeAxes() {
1147: for (int i = 0; i < this .rangeAxes.size(); i++) {
1148: ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
1149: if (axis != null) {
1150: axis.configure();
1151: }
1152: }
1153: }
1154:
1155: /**
1156: * Returns the location for a range axis. If this hasn't been set
1157: * explicitly, the method returns the location that is opposite to the
1158: * primary range axis location.
1159: *
1160: * @param index the axis index.
1161: *
1162: * @return The location (never <code>null</code>).
1163: *
1164: * @see #setRangeAxisLocation(int, AxisLocation)
1165: */
1166: public AxisLocation getRangeAxisLocation(int index) {
1167: AxisLocation result = null;
1168: if (index < this .rangeAxisLocations.size()) {
1169: result = (AxisLocation) this .rangeAxisLocations.get(index);
1170: }
1171: if (result == null) {
1172: result = AxisLocation.getOpposite(getRangeAxisLocation());
1173: }
1174: return result;
1175: }
1176:
1177: /**
1178: * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1179: * to all registered listeners.
1180: *
1181: * @param index the axis index.
1182: * @param location the location (<code>null</code> permitted).
1183: *
1184: * @see #getRangeAxisLocation(int)
1185: */
1186: public void setRangeAxisLocation(int index, AxisLocation location) {
1187: // delegate...
1188: setRangeAxisLocation(index, location, true);
1189: }
1190:
1191: /**
1192: * Sets the axis location for a domain axis and, if requested, sends a
1193: * {@link PlotChangeEvent} to all registered listeners.
1194: *
1195: * @param index the axis index.
1196: * @param location the location (<code>null</code> not permitted for
1197: * index 0).
1198: * @param notify notify listeners?
1199: *
1200: * @since 1.0.5
1201: *
1202: * @see #getRangeAxisLocation(int)
1203: * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1204: */
1205: public void setRangeAxisLocation(int index, AxisLocation location,
1206: boolean notify) {
1207:
1208: if (index == 0 && location == null) {
1209: throw new IllegalArgumentException(
1210: "Null 'location' for index 0 not permitted.");
1211: }
1212: this .rangeAxisLocations.set(index, location);
1213: if (notify) {
1214: notifyListeners(new PlotChangeEvent(this ));
1215: }
1216: }
1217:
1218: /**
1219: * Returns the edge for a range axis.
1220: *
1221: * @param index the axis index.
1222: *
1223: * @return The edge.
1224: *
1225: * @see #getRangeAxisLocation(int)
1226: * @see #getOrientation()
1227: */
1228: public RectangleEdge getRangeAxisEdge(int index) {
1229: AxisLocation location = getRangeAxisLocation(index);
1230: RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1231: this .orientation);
1232: if (result == null) {
1233: result = RectangleEdge.opposite(getRangeAxisEdge());
1234: }
1235: return result;
1236: }
1237:
1238: /**
1239: * Returns the primary dataset for the plot.
1240: *
1241: * @return The primary dataset (possibly <code>null</code>).
1242: *
1243: * @see #getDataset(int)
1244: * @see #setDataset(XYDataset)
1245: */
1246: public XYDataset getDataset() {
1247: return getDataset(0);
1248: }
1249:
1250: /**
1251: * Returns a dataset.
1252: *
1253: * @param index the dataset index.
1254: *
1255: * @return The dataset (possibly <code>null</code>).
1256: *
1257: * @see #setDataset(int, XYDataset)
1258: */
1259: public XYDataset getDataset(int index) {
1260: XYDataset result = null;
1261: if (this .datasets.size() > index) {
1262: result = (XYDataset) this .datasets.get(index);
1263: }
1264: return result;
1265: }
1266:
1267: /**
1268: * Sets the primary dataset for the plot, replacing the existing dataset if
1269: * there is one.
1270: *
1271: * @param dataset the dataset (<code>null</code> permitted).
1272: *
1273: * @see #getDataset()
1274: * @see #setDataset(int, XYDataset)
1275: */
1276: public void setDataset(XYDataset dataset) {
1277: setDataset(0, dataset);
1278: }
1279:
1280: /**
1281: * Sets a dataset for the plot.
1282: *
1283: * @param index the dataset index.
1284: * @param dataset the dataset (<code>null</code> permitted).
1285: *
1286: * @see #getDataset(int)
1287: */
1288: public void setDataset(int index, XYDataset dataset) {
1289: XYDataset existing = getDataset(index);
1290: if (existing != null) {
1291: existing.removeChangeListener(this );
1292: }
1293: this .datasets.set(index, dataset);
1294: if (dataset != null) {
1295: dataset.addChangeListener(this );
1296: }
1297:
1298: // send a dataset change event to self...
1299: DatasetChangeEvent event = new DatasetChangeEvent(this , dataset);
1300: datasetChanged(event);
1301: }
1302:
1303: /**
1304: * Returns the number of datasets.
1305: *
1306: * @return The number of datasets.
1307: */
1308: public int getDatasetCount() {
1309: return this .datasets.size();
1310: }
1311:
1312: /**
1313: * Returns the index of the specified dataset, or <code>-1</code> if the
1314: * dataset does not belong to the plot.
1315: *
1316: * @param dataset the dataset (<code>null</code> not permitted).
1317: *
1318: * @return The index.
1319: */
1320: public int indexOf(XYDataset dataset) {
1321: int result = -1;
1322: for (int i = 0; i < this .datasets.size(); i++) {
1323: if (dataset == this .datasets.get(i)) {
1324: result = i;
1325: break;
1326: }
1327: }
1328: return result;
1329: }
1330:
1331: /**
1332: * Maps a dataset to a particular domain axis. All data will be plotted
1333: * against axis zero by default, no mapping is required for this case.
1334: *
1335: * @param index the dataset index (zero-based).
1336: * @param axisIndex the axis index.
1337: *
1338: * @see #mapDatasetToRangeAxis(int, int)
1339: */
1340: public void mapDatasetToDomainAxis(int index, int axisIndex) {
1341: this .datasetToDomainAxisMap.put(new Integer(index),
1342: new Integer(axisIndex));
1343: // fake a dataset change event to update axes...
1344: datasetChanged(new DatasetChangeEvent(this , getDataset(index)));
1345: }
1346:
1347: /**
1348: * Maps a dataset to a particular range axis. All data will be plotted
1349: * against axis zero by default, no mapping is required for this case.
1350: *
1351: * @param index the dataset index (zero-based).
1352: * @param axisIndex the axis index.
1353: *
1354: * @see #mapDatasetToDomainAxis(int, int)
1355: */
1356: public void mapDatasetToRangeAxis(int index, int axisIndex) {
1357: this .datasetToRangeAxisMap.put(new Integer(index), new Integer(
1358: axisIndex));
1359: // fake a dataset change event to update axes...
1360: datasetChanged(new DatasetChangeEvent(this , getDataset(index)));
1361: }
1362:
1363: /**
1364: * Returns the renderer for the primary dataset.
1365: *
1366: * @return The item renderer (possibly <code>null</code>).
1367: *
1368: * @see #setRenderer(XYItemRenderer)
1369: */
1370: public XYItemRenderer getRenderer() {
1371: return getRenderer(0);
1372: }
1373:
1374: /**
1375: * Returns the renderer for a dataset, or <code>null</code>.
1376: *
1377: * @param index the renderer index.
1378: *
1379: * @return The renderer (possibly <code>null</code>).
1380: *
1381: * @see #setRenderer(int, XYItemRenderer)
1382: */
1383: public XYItemRenderer getRenderer(int index) {
1384: XYItemRenderer result = null;
1385: if (this .renderers.size() > index) {
1386: result = (XYItemRenderer) this .renderers.get(index);
1387: }
1388: return result;
1389:
1390: }
1391:
1392: /**
1393: * Sets the renderer for the primary dataset and sends a
1394: * {@link PlotChangeEvent} to all registered listeners. If the renderer
1395: * is set to <code>null</code>, no data will be displayed.
1396: *
1397: * @param renderer the renderer (<code>null</code> permitted).
1398: *
1399: * @see #getRenderer()
1400: */
1401: public void setRenderer(XYItemRenderer renderer) {
1402: setRenderer(0, renderer);
1403: }
1404:
1405: /**
1406: * Sets a renderer and sends a {@link PlotChangeEvent} to all
1407: * registered listeners.
1408: *
1409: * @param index the index.
1410: * @param renderer the renderer.
1411: *
1412: * @see #getRenderer(int)
1413: */
1414: public void setRenderer(int index, XYItemRenderer renderer) {
1415: setRenderer(index, renderer, true);
1416: }
1417:
1418: /**
1419: * Sets a renderer and sends a {@link PlotChangeEvent} to all
1420: * registered listeners.
1421: *
1422: * @param index the index.
1423: * @param renderer the renderer.
1424: * @param notify notify listeners?
1425: *
1426: * @see #getRenderer(int)
1427: */
1428: public void setRenderer(int index, XYItemRenderer renderer,
1429: boolean notify) {
1430: XYItemRenderer existing = getRenderer(index);
1431: if (existing != null) {
1432: existing.removeChangeListener(this );
1433: }
1434: this .renderers.set(index, renderer);
1435: if (renderer != null) {
1436: renderer.setPlot(this );
1437: renderer.addChangeListener(this );
1438: }
1439: configureDomainAxes();
1440: configureRangeAxes();
1441: if (notify) {
1442: notifyListeners(new PlotChangeEvent(this ));
1443: }
1444: }
1445:
1446: /**
1447: * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1448: * to all registered listeners.
1449: *
1450: * @param renderers the renderers (<code>null</code> not permitted).
1451: */
1452: public void setRenderers(XYItemRenderer[] renderers) {
1453: for (int i = 0; i < renderers.length; i++) {
1454: setRenderer(i, renderers[i], false);
1455: }
1456: notifyListeners(new PlotChangeEvent(this ));
1457: }
1458:
1459: /**
1460: * Returns the dataset rendering order.
1461: *
1462: * @return The order (never <code>null</code>).
1463: *
1464: * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
1465: */
1466: public DatasetRenderingOrder getDatasetRenderingOrder() {
1467: return this .datasetRenderingOrder;
1468: }
1469:
1470: /**
1471: * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1472: * registered listeners. By default, the plot renders the primary dataset
1473: * last (so that the primary dataset overlays the secondary datasets).
1474: * You can reverse this if you want to.
1475: *
1476: * @param order the rendering order (<code>null</code> not permitted).
1477: *
1478: * @see #getDatasetRenderingOrder()
1479: */
1480: public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1481: if (order == null) {
1482: throw new IllegalArgumentException("Null 'order' argument.");
1483: }
1484: this .datasetRenderingOrder = order;
1485: notifyListeners(new PlotChangeEvent(this ));
1486: }
1487:
1488: /**
1489: * Returns the series rendering order.
1490: *
1491: * @return the order (never <code>null</code>).
1492: *
1493: * @see #setSeriesRenderingOrder(SeriesRenderingOrder)
1494: */
1495: public SeriesRenderingOrder getSeriesRenderingOrder() {
1496: return this .seriesRenderingOrder;
1497: }
1498:
1499: /**
1500: * Sets the series order and sends a {@link PlotChangeEvent} to all
1501: * registered listeners. By default, the plot renders the primary series
1502: * last (so that the primary series appears to be on top).
1503: * You can reverse this if you want to.
1504: *
1505: * @param order the rendering order (<code>null</code> not permitted).
1506: *
1507: * @see #getSeriesRenderingOrder()
1508: */
1509: public void setSeriesRenderingOrder(SeriesRenderingOrder order) {
1510: if (order == null) {
1511: throw new IllegalArgumentException("Null 'order' argument.");
1512: }
1513: this .seriesRenderingOrder = order;
1514: notifyListeners(new PlotChangeEvent(this ));
1515: }
1516:
1517: /**
1518: * Returns the index of the specified renderer, or <code>-1</code> if the
1519: * renderer is not assigned to this plot.
1520: *
1521: * @param renderer the renderer (<code>null</code> permitted).
1522: *
1523: * @return The renderer index.
1524: */
1525: public int getIndexOf(XYItemRenderer renderer) {
1526: return this .renderers.indexOf(renderer);
1527: }
1528:
1529: /**
1530: * Returns the renderer for the specified dataset. The code first
1531: * determines the index of the dataset, then checks if there is a
1532: * renderer with the same index (if not, the method returns renderer(0).
1533: *
1534: * @param dataset the dataset (<code>null</code> permitted).
1535: *
1536: * @return The renderer (possibly <code>null</code>).
1537: */
1538: public XYItemRenderer getRendererForDataset(XYDataset dataset) {
1539: XYItemRenderer result = null;
1540: for (int i = 0; i < this .datasets.size(); i++) {
1541: if (this .datasets.get(i) == dataset) {
1542: result = (XYItemRenderer) this .renderers.get(i);
1543: if (result == null) {
1544: result = getRenderer();
1545: }
1546: break;
1547: }
1548: }
1549: return result;
1550: }
1551:
1552: /**
1553: * Returns the weight for this plot when it is used as a subplot within a
1554: * combined plot.
1555: *
1556: * @return The weight.
1557: *
1558: * @see #setWeight(int)
1559: */
1560: public int getWeight() {
1561: return this .weight;
1562: }
1563:
1564: /**
1565: * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all
1566: * registered listeners.
1567: *
1568: * @param weight the weight.
1569: *
1570: * @see #getWeight()
1571: */
1572: public void setWeight(int weight) {
1573: this .weight = weight;
1574: notifyListeners(new PlotChangeEvent(this ));
1575: }
1576:
1577: /**
1578: * Returns <code>true</code> if the domain gridlines are visible, and
1579: * <code>false<code> otherwise.
1580: *
1581: * @return <code>true</code> or <code>false</code>.
1582: *
1583: * @see #setDomainGridlinesVisible(boolean)
1584: */
1585: public boolean isDomainGridlinesVisible() {
1586: return this .domainGridlinesVisible;
1587: }
1588:
1589: /**
1590: * Sets the flag that controls whether or not the domain grid-lines are
1591: * visible.
1592: * <p>
1593: * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1594: * registered listeners.
1595: *
1596: * @param visible the new value of the flag.
1597: *
1598: * @see #isDomainGridlinesVisible()
1599: */
1600: public void setDomainGridlinesVisible(boolean visible) {
1601: if (this .domainGridlinesVisible != visible) {
1602: this .domainGridlinesVisible = visible;
1603: notifyListeners(new PlotChangeEvent(this ));
1604: }
1605: }
1606:
1607: /**
1608: * Returns the stroke for the grid-lines (if any) plotted against the
1609: * domain axis.
1610: *
1611: * @return The stroke (never <code>null</code>).
1612: *
1613: * @see #setDomainGridlineStroke(Stroke)
1614: */
1615: public Stroke getDomainGridlineStroke() {
1616: return this .domainGridlineStroke;
1617: }
1618:
1619: /**
1620: * Sets the stroke for the grid lines plotted against the domain axis, and
1621: * sends a {@link PlotChangeEvent} to all registered listeners.
1622: * <p>
1623: * If you set this to <code>null</code>, no grid lines will be drawn.
1624: *
1625: * @param stroke the stroke (<code>null</code> not permitted).
1626: *
1627: * @throws IllegalArgumentException if <code>stroke</code> is
1628: * <code>null</code>.
1629: *
1630: * @see #getDomainGridlineStroke()
1631: */
1632: public void setDomainGridlineStroke(Stroke stroke) {
1633: if (stroke == null) {
1634: throw new IllegalArgumentException(
1635: "Null 'stroke' argument.");
1636: }
1637: this .domainGridlineStroke = stroke;
1638: notifyListeners(new PlotChangeEvent(this ));
1639: }
1640:
1641: /**
1642: * Returns the paint for the grid lines (if any) plotted against the domain
1643: * axis.
1644: *
1645: * @return The paint (never <code>null</code>).
1646: *
1647: * @see #setDomainGridlinePaint(Paint)
1648: */
1649: public Paint getDomainGridlinePaint() {
1650: return this .domainGridlinePaint;
1651: }
1652:
1653: /**
1654: * Sets the paint for the grid lines plotted against the domain axis, and
1655: * sends a {@link PlotChangeEvent} to all registered listeners.
1656: *
1657: * @param paint the paint (<code>null</code> not permitted).
1658: *
1659: * @throws IllegalArgumentException if <code>paint</code> is
1660: * <code>null</code>.
1661: *
1662: * @see #getDomainGridlinePaint()
1663: */
1664: public void setDomainGridlinePaint(Paint paint) {
1665: if (paint == null) {
1666: throw new IllegalArgumentException("Null 'paint' argument.");
1667: }
1668: this .domainGridlinePaint = paint;
1669: notifyListeners(new PlotChangeEvent(this ));
1670: }
1671:
1672: /**
1673: * Returns <code>true</code> if the range axis grid is visible, and
1674: * <code>false<code> otherwise.
1675: *
1676: * @return A boolean.
1677: *
1678: * @see #setRangeGridlinesVisible(boolean)
1679: */
1680: public boolean isRangeGridlinesVisible() {
1681: return this .rangeGridlinesVisible;
1682: }
1683:
1684: /**
1685: * Sets the flag that controls whether or not the range axis grid lines
1686: * are visible.
1687: * <p>
1688: * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1689: * registered listeners.
1690: *
1691: * @param visible the new value of the flag.
1692: *
1693: * @see #isRangeGridlinesVisible()
1694: */
1695: public void setRangeGridlinesVisible(boolean visible) {
1696: if (this .rangeGridlinesVisible != visible) {
1697: this .rangeGridlinesVisible = visible;
1698: notifyListeners(new PlotChangeEvent(this ));
1699: }
1700: }
1701:
1702: /**
1703: * Returns the stroke for the grid lines (if any) plotted against the
1704: * range axis.
1705: *
1706: * @return The stroke (never <code>null</code>).
1707: *
1708: * @see #setRangeGridlineStroke(Stroke)
1709: */
1710: public Stroke getRangeGridlineStroke() {
1711: return this .rangeGridlineStroke;
1712: }
1713:
1714: /**
1715: * Sets the stroke for the grid lines plotted against the range axis,
1716: * and sends a {@link PlotChangeEvent} to all registered listeners.
1717: *
1718: * @param stroke the stroke (<code>null</code> not permitted).
1719: *
1720: * @see #getRangeGridlineStroke()
1721: */
1722: public void setRangeGridlineStroke(Stroke stroke) {
1723: if (stroke == null) {
1724: throw new IllegalArgumentException(
1725: "Null 'stroke' argument.");
1726: }
1727: this .rangeGridlineStroke = stroke;
1728: notifyListeners(new PlotChangeEvent(this ));
1729: }
1730:
1731: /**
1732: * Returns the paint for the grid lines (if any) plotted against the range
1733: * axis.
1734: *
1735: * @return The paint (never <code>null</code>).
1736: *
1737: * @see #setRangeGridlinePaint(Paint)
1738: */
1739: public Paint getRangeGridlinePaint() {
1740: return this .rangeGridlinePaint;
1741: }
1742:
1743: /**
1744: * Sets the paint for the grid lines plotted against the range axis and
1745: * sends a {@link PlotChangeEvent} to all registered listeners.
1746: *
1747: * @param paint the paint (<code>null</code> not permitted).
1748: *
1749: * @see #getRangeGridlinePaint()
1750: */
1751: public void setRangeGridlinePaint(Paint paint) {
1752: if (paint == null) {
1753: throw new IllegalArgumentException("Null 'paint' argument.");
1754: }
1755: this .rangeGridlinePaint = paint;
1756: notifyListeners(new PlotChangeEvent(this ));
1757: }
1758:
1759: /**
1760: * Returns a flag that controls whether or not a zero baseline is
1761: * displayed for the domain axis.
1762: *
1763: * @return A boolean.
1764: *
1765: * @since 1.0.5
1766: *
1767: * @see #setDomainZeroBaselineVisible(boolean)
1768: */
1769: public boolean isDomainZeroBaselineVisible() {
1770: return this .domainZeroBaselineVisible;
1771: }
1772:
1773: /**
1774: * Sets the flag that controls whether or not the zero baseline is
1775: * displayed for the domain axis, and sends a {@link PlotChangeEvent} to
1776: * all registered listeners.
1777: *
1778: * @param visible the flag.
1779: *
1780: * @since 1.0.5
1781: *
1782: * @see #isDomainZeroBaselineVisible()
1783: */
1784: public void setDomainZeroBaselineVisible(boolean visible) {
1785: this .domainZeroBaselineVisible = visible;
1786: notifyListeners(new PlotChangeEvent(this ));
1787: }
1788:
1789: /**
1790: * Returns the stroke used for the zero baseline against the domain axis.
1791: *
1792: * @return The stroke (never <code>null</code>).
1793: *
1794: * @since 1.0.5
1795: *
1796: * @see #setDomainZeroBaselineStroke(Stroke)
1797: */
1798: public Stroke getDomainZeroBaselineStroke() {
1799: return this .domainZeroBaselineStroke;
1800: }
1801:
1802: /**
1803: * Sets the stroke for the zero baseline for the domain axis,
1804: * and sends a {@link PlotChangeEvent} to all registered listeners.
1805: *
1806: * @param stroke the stroke (<code>null</code> not permitted).
1807: *
1808: * @since 1.0.5
1809: *
1810: * @see #getRangeZeroBaselineStroke()
1811: */
1812: public void setDomainZeroBaselineStroke(Stroke stroke) {
1813: if (stroke == null) {
1814: throw new IllegalArgumentException(
1815: "Null 'stroke' argument.");
1816: }
1817: this .domainZeroBaselineStroke = stroke;
1818: notifyListeners(new PlotChangeEvent(this ));
1819: }
1820:
1821: /**
1822: * Returns the paint for the zero baseline (if any) plotted against the
1823: * domain axis.
1824: *
1825: * @since 1.0.5
1826: *
1827: * @return The paint (never <code>null</code>).
1828: *
1829: * @see #setDomainZeroBaselinePaint(Paint)
1830: */
1831: public Paint getDomainZeroBaselinePaint() {
1832: return this .domainZeroBaselinePaint;
1833: }
1834:
1835: /**
1836: * Sets the paint for the zero baseline plotted against the domain axis and
1837: * sends a {@link PlotChangeEvent} to all registered listeners.
1838: *
1839: * @param paint the paint (<code>null</code> not permitted).
1840: *
1841: * @since 1.0.5
1842: *
1843: * @see #getDomainZeroBaselinePaint()
1844: */
1845: public void setDomainZeroBaselinePaint(Paint paint) {
1846: if (paint == null) {
1847: throw new IllegalArgumentException("Null 'paint' argument.");
1848: }
1849: this .domainZeroBaselinePaint = paint;
1850: notifyListeners(new PlotChangeEvent(this ));
1851: }
1852:
1853: /**
1854: * Returns a flag that controls whether or not a zero baseline is
1855: * displayed for the range axis.
1856: *
1857: * @return A boolean.
1858: *
1859: * @see #setRangeZeroBaselineVisible(boolean)
1860: */
1861: public boolean isRangeZeroBaselineVisible() {
1862: return this .rangeZeroBaselineVisible;
1863: }
1864:
1865: /**
1866: * Sets the flag that controls whether or not the zero baseline is
1867: * displayed for the range axis, and sends a {@link PlotChangeEvent} to
1868: * all registered listeners.
1869: *
1870: * @param visible the flag.
1871: *
1872: * @see #isRangeZeroBaselineVisible()
1873: */
1874: public void setRangeZeroBaselineVisible(boolean visible) {
1875: this .rangeZeroBaselineVisible = visible;
1876: notifyListeners(new PlotChangeEvent(this ));
1877: }
1878:
1879: /**
1880: * Returns the stroke used for the zero baseline against the range axis.
1881: *
1882: * @return The stroke (never <code>null</code>).
1883: *
1884: * @see #setRangeZeroBaselineStroke(Stroke)
1885: */
1886: public Stroke getRangeZeroBaselineStroke() {
1887: return this .rangeZeroBaselineStroke;
1888: }
1889:
1890: /**
1891: * Sets the stroke for the zero baseline for the range axis,
1892: * and sends a {@link PlotChangeEvent} to all registered listeners.
1893: *
1894: * @param stroke the stroke (<code>null</code> not permitted).
1895: *
1896: * @see #getRangeZeroBaselineStroke()
1897: */
1898: public void setRangeZeroBaselineStroke(Stroke stroke) {
1899: if (stroke == null) {
1900: throw new IllegalArgumentException(
1901: "Null 'stroke' argument.");
1902: }
1903: this .rangeZeroBaselineStroke = stroke;
1904: notifyListeners(new PlotChangeEvent(this ));
1905: }
1906:
1907: /**
1908: * Returns the paint for the zero baseline (if any) plotted against the
1909: * range axis.
1910: *
1911: * @return The paint (never <code>null</code>).
1912: *
1913: * @see #setRangeZeroBaselinePaint(Paint)
1914: */
1915: public Paint getRangeZeroBaselinePaint() {
1916: return this .rangeZeroBaselinePaint;
1917: }
1918:
1919: /**
1920: * Sets the paint for the zero baseline plotted against the range axis and
1921: * sends a {@link PlotChangeEvent} to all registered listeners.
1922: *
1923: * @param paint the paint (<code>null</code> not permitted).
1924: *
1925: * @see #getRangeZeroBaselinePaint()
1926: */
1927: public void setRangeZeroBaselinePaint(Paint paint) {
1928: if (paint == null) {
1929: throw new IllegalArgumentException("Null 'paint' argument.");
1930: }
1931: this .rangeZeroBaselinePaint = paint;
1932: notifyListeners(new PlotChangeEvent(this ));
1933: }
1934:
1935: /**
1936: * Returns the paint used for the domain tick bands. If this is
1937: * <code>null</code>, no tick bands will be drawn.
1938: *
1939: * @return The paint (possibly <code>null</code>).
1940: *
1941: * @see #setDomainTickBandPaint(Paint)
1942: */
1943: public Paint getDomainTickBandPaint() {
1944: return this .domainTickBandPaint;
1945: }
1946:
1947: /**
1948: * Sets the paint for the domain tick bands.
1949: *
1950: * @param paint the paint (<code>null</code> permitted).
1951: *
1952: * @see #getDomainTickBandPaint()
1953: */
1954: public void setDomainTickBandPaint(Paint paint) {
1955: this .domainTickBandPaint = paint;
1956: notifyListeners(new PlotChangeEvent(this ));
1957: }
1958:
1959: /**
1960: * Returns the paint used for the range tick bands. If this is
1961: * <code>null</code>, no tick bands will be drawn.
1962: *
1963: * @return The paint (possibly <code>null</code>).
1964: *
1965: * @see #setRangeTickBandPaint(Paint)
1966: */
1967: public Paint getRangeTickBandPaint() {
1968: return this .rangeTickBandPaint;
1969: }
1970:
1971: /**
1972: * Sets the paint for the range tick bands.
1973: *
1974: * @param paint the paint (<code>null</code> permitted).
1975: *
1976: * @see #getRangeTickBandPaint()
1977: */
1978: public void setRangeTickBandPaint(Paint paint) {
1979: this .rangeTickBandPaint = paint;
1980: notifyListeners(new PlotChangeEvent(this ));
1981: }
1982:
1983: /**
1984: * Returns the origin for the quadrants that can be displayed on the plot.
1985: * This defaults to (0, 0).
1986: *
1987: * @return The origin point (never <code>null</code>).
1988: *
1989: * @see #setQuadrantOrigin(Point2D)
1990: */
1991: public Point2D getQuadrantOrigin() {
1992: return this .quadrantOrigin;
1993: }
1994:
1995: /**
1996: * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all
1997: * registered listeners.
1998: *
1999: * @param origin the origin (<code>null</code> not permitted).
2000: *
2001: * @see #getQuadrantOrigin()
2002: */
2003: public void setQuadrantOrigin(Point2D origin) {
2004: if (origin == null) {
2005: throw new IllegalArgumentException(
2006: "Null 'origin' argument.");
2007: }
2008: this .quadrantOrigin = origin;
2009: notifyListeners(new PlotChangeEvent(this ));
2010: }
2011:
2012: /**
2013: * Returns the paint used for the specified quadrant.
2014: *
2015: * @param index the quadrant index (0-3).
2016: *
2017: * @return The paint (possibly <code>null</code>).
2018: *
2019: * @see #setQuadrantPaint(int, Paint)
2020: */
2021: public Paint getQuadrantPaint(int index) {
2022: if (index < 0 || index > 3) {
2023: throw new IllegalArgumentException(
2024: "The index should be in the range 0 to 3.");
2025: }
2026: return this .quadrantPaint[index];
2027: }
2028:
2029: /**
2030: * Sets the paint used for the specified quadrant and sends a
2031: * {@link PlotChangeEvent} to all registered listeners.
2032: *
2033: * @param index the quadrant index (0-3).
2034: * @param paint the paint (<code>null</code> permitted).
2035: *
2036: * @see #getQuadrantPaint(int)
2037: */
2038: public void setQuadrantPaint(int index, Paint paint) {
2039: if (index < 0 || index > 3) {
2040: throw new IllegalArgumentException(
2041: "The index should be in the range 0 to 3.");
2042: }
2043: this .quadrantPaint[index] = paint;
2044: notifyListeners(new PlotChangeEvent(this ));
2045: }
2046:
2047: /**
2048: * Adds a marker for the domain axis and sends a {@link PlotChangeEvent}
2049: * to all registered listeners.
2050: * <P>
2051: * Typically a marker will be drawn by the renderer as a line perpendicular
2052: * to the range axis, however this is entirely up to the renderer.
2053: *
2054: * @param marker the marker (<code>null</code> not permitted).
2055: *
2056: * @see #addDomainMarker(Marker, Layer)
2057: * @see #clearDomainMarkers()
2058: */
2059: public void addDomainMarker(Marker marker) {
2060: // defer argument checking...
2061: addDomainMarker(marker, Layer.FOREGROUND);
2062: }
2063:
2064: /**
2065: * Adds a marker for the domain axis in the specified layer and sends a
2066: * {@link PlotChangeEvent} to all registered listeners.
2067: * <P>
2068: * Typically a marker will be drawn by the renderer as a line perpendicular
2069: * to the range axis, however this is entirely up to the renderer.
2070: *
2071: * @param marker the marker (<code>null</code> not permitted).
2072: * @param layer the layer (foreground or background).
2073: *
2074: * @see #addDomainMarker(int, Marker, Layer)
2075: */
2076: public void addDomainMarker(Marker marker, Layer layer) {
2077: addDomainMarker(0, marker, layer);
2078: }
2079:
2080: /**
2081: * Clears all the (foreground and background) domain markers and sends a
2082: * {@link PlotChangeEvent} to all registered listeners.
2083: *
2084: * @see #addDomainMarker(int, Marker, Layer)
2085: */
2086: public void clearDomainMarkers() {
2087: if (this .backgroundDomainMarkers != null) {
2088: Set keys = this .backgroundDomainMarkers.keySet();
2089: Iterator iterator = keys.iterator();
2090: while (iterator.hasNext()) {
2091: Integer key = (Integer) iterator.next();
2092: clearDomainMarkers(key.intValue());
2093: }
2094: this .backgroundDomainMarkers.clear();
2095: }
2096: if (this .foregroundDomainMarkers != null) {
2097: Set keys = this .foregroundDomainMarkers.keySet();
2098: Iterator iterator = keys.iterator();
2099: while (iterator.hasNext()) {
2100: Integer key = (Integer) iterator.next();
2101: clearDomainMarkers(key.intValue());
2102: }
2103: this .foregroundDomainMarkers.clear();
2104: }
2105: notifyListeners(new PlotChangeEvent(this ));
2106: }
2107:
2108: /**
2109: * Clears the (foreground and background) domain markers for a particular
2110: * renderer.
2111: *
2112: * @param index the renderer index.
2113: *
2114: * @see #clearRangeMarkers(int)
2115: */
2116: public void clearDomainMarkers(int index) {
2117: Integer key = new Integer(index);
2118: if (this .backgroundDomainMarkers != null) {
2119: Collection markers = (Collection) this .backgroundDomainMarkers
2120: .get(key);
2121: if (markers != null) {
2122: Iterator iterator = markers.iterator();
2123: while (iterator.hasNext()) {
2124: Marker m = (Marker) iterator.next();
2125: m.removeChangeListener(this );
2126: }
2127: markers.clear();
2128: }
2129: }
2130: if (this .foregroundRangeMarkers != null) {
2131: Collection markers = (Collection) this .foregroundDomainMarkers
2132: .get(key);
2133: if (markers != null) {
2134: Iterator iterator = markers.iterator();
2135: while (iterator.hasNext()) {
2136: Marker m = (Marker) iterator.next();
2137: m.removeChangeListener(this );
2138: }
2139: markers.clear();
2140: }
2141: }
2142: notifyListeners(new PlotChangeEvent(this ));
2143: }
2144:
2145: /**
2146: * Adds a marker for a specific dataset/renderer and sends a
2147: * {@link PlotChangeEvent} to all registered listeners.
2148: * <P>
2149: * Typically a marker will be drawn by the renderer as a line perpendicular
2150: * to the domain axis (that the renderer is mapped to), however this is
2151: * entirely up to the renderer.
2152: *
2153: * @param index the dataset/renderer index.
2154: * @param marker the marker.
2155: * @param layer the layer (foreground or background).
2156: *
2157: * @see #clearDomainMarkers(int)
2158: * @see #addRangeMarker(int, Marker, Layer)
2159: */
2160: public void addDomainMarker(int index, Marker marker, Layer layer) {
2161: if (marker == null) {
2162: throw new IllegalArgumentException(
2163: "Null 'marker' not permitted.");
2164: }
2165: if (layer == null) {
2166: throw new IllegalArgumentException(
2167: "Null 'layer' not permitted.");
2168: }
2169: Collection markers;
2170: if (layer == Layer.FOREGROUND) {
2171: markers = (Collection) this .foregroundDomainMarkers
2172: .get(new Integer(index));
2173: if (markers == null) {
2174: markers = new java.util.ArrayList();
2175: this .foregroundDomainMarkers.put(new Integer(index),
2176: markers);
2177: }
2178: markers.add(marker);
2179: } else if (layer == Layer.BACKGROUND) {
2180: markers = (Collection) this .backgroundDomainMarkers
2181: .get(new Integer(index));
2182: if (markers == null) {
2183: markers = new java.util.ArrayList();
2184: this .backgroundDomainMarkers.put(new Integer(index),
2185: markers);
2186: }
2187: markers.add(marker);
2188: }
2189: marker.addChangeListener(this );
2190: notifyListeners(new PlotChangeEvent(this ));
2191: }
2192:
2193: /**
2194: * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to
2195: * all registered listeners.
2196: * <P>
2197: * Typically a marker will be drawn by the renderer as a line perpendicular
2198: * to the range axis, however this is entirely up to the renderer.
2199: *
2200: * @param marker the marker (<code>null</code> not permitted).
2201: *
2202: * @see #addRangeMarker(Marker, Layer)
2203: */
2204: public void addRangeMarker(Marker marker) {
2205: addRangeMarker(marker, Layer.FOREGROUND);
2206: }
2207:
2208: /**
2209: * Adds a marker for the range axis in the specified layer and sends a
2210: * {@link PlotChangeEvent} to all registered listeners.
2211: * <P>
2212: * Typically a marker will be drawn by the renderer as a line perpendicular
2213: * to the range axis, however this is entirely up to the renderer.
2214: *
2215: * @param marker the marker (<code>null</code> not permitted).
2216: * @param layer the layer (foreground or background).
2217: *
2218: * @see #addRangeMarker(int, Marker, Layer)
2219: */
2220: public void addRangeMarker(Marker marker, Layer layer) {
2221: addRangeMarker(0, marker, layer);
2222: }
2223:
2224: /**
2225: * Clears all the range markers and sends a {@link PlotChangeEvent} to all
2226: * registered listeners.
2227: *
2228: * @see #clearRangeMarkers()
2229: */
2230: public void clearRangeMarkers() {
2231: if (this .backgroundRangeMarkers != null) {
2232: Set keys = this .backgroundRangeMarkers.keySet();
2233: Iterator iterator = keys.iterator();
2234: while (iterator.hasNext()) {
2235: Integer key = (Integer) iterator.next();
2236: clearRangeMarkers(key.intValue());
2237: }
2238: this .backgroundRangeMarkers.clear();
2239: }
2240: if (this .foregroundRangeMarkers != null) {
2241: Set keys = this .foregroundRangeMarkers.keySet();
2242: Iterator iterator = keys.iterator();
2243: while (iterator.hasNext()) {
2244: Integer key = (Integer) iterator.next();
2245: clearRangeMarkers(key.intValue());
2246: }
2247: this .foregroundRangeMarkers.clear();
2248: }
2249: notifyListeners(new PlotChangeEvent(this ));
2250: }
2251:
2252: /**
2253: * Adds a marker for a specific dataset/renderer and sends a
2254: * {@link PlotChangeEvent} to all registered listeners.
2255: * <P>
2256: * Typically a marker will be drawn by the renderer as a line perpendicular
2257: * to the range axis, however this is entirely up to the renderer.
2258: *
2259: * @param index the dataset/renderer index.
2260: * @param marker the marker.
2261: * @param layer the layer (foreground or background).
2262: *
2263: * @see #clearRangeMarkers(int)
2264: * @see #addDomainMarker(int, Marker, Layer)
2265: */
2266: public void addRangeMarker(int index, Marker marker, Layer layer) {
2267: Collection markers;
2268: if (layer == Layer.FOREGROUND) {
2269: markers = (Collection) this .foregroundRangeMarkers
2270: .get(new Integer(index));
2271: if (markers == null) {
2272: markers = new java.util.ArrayList();
2273: this .foregroundRangeMarkers.put(new Integer(index),
2274: markers);
2275: }
2276: markers.add(marker);
2277: } else if (layer == Layer.BACKGROUND) {
2278: markers = (Collection) this .backgroundRangeMarkers
2279: .get(new Integer(index));
2280: if (markers == null) {
2281: markers = new java.util.ArrayList();
2282: this .backgroundRangeMarkers.put(new Integer(index),
2283: markers);
2284: }
2285: markers.add(marker);
2286: }
2287: marker.addChangeListener(this );
2288: notifyListeners(new PlotChangeEvent(this ));
2289: }
2290:
2291: /**
2292: * Clears the (foreground and background) range markers for a particular
2293: * renderer.
2294: *
2295: * @param index the renderer index.
2296: */
2297: public void clearRangeMarkers(int index) {
2298: Integer key = new Integer(index);
2299: if (this .backgroundRangeMarkers != null) {
2300: Collection markers = (Collection) this .backgroundRangeMarkers
2301: .get(key);
2302: if (markers != null) {
2303: Iterator iterator = markers.iterator();
2304: while (iterator.hasNext()) {
2305: Marker m = (Marker) iterator.next();
2306: m.removeChangeListener(this );
2307: }
2308: markers.clear();
2309: }
2310: }
2311: if (this .foregroundRangeMarkers != null) {
2312: Collection markers = (Collection) this .foregroundRangeMarkers
2313: .get(key);
2314: if (markers != null) {
2315: Iterator iterator = markers.iterator();
2316: while (iterator.hasNext()) {
2317: Marker m = (Marker) iterator.next();
2318: m.removeChangeListener(this );
2319: }
2320: markers.clear();
2321: }
2322: }
2323: notifyListeners(new PlotChangeEvent(this ));
2324: }
2325:
2326: /**
2327: * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to
2328: * all registered listeners.
2329: *
2330: * @param annotation the annotation (<code>null</code> not permitted).
2331: *
2332: * @see #getAnnotations()
2333: * @see #removeAnnotation(XYAnnotation)
2334: */
2335: public void addAnnotation(XYAnnotation annotation) {
2336: if (annotation == null) {
2337: throw new IllegalArgumentException(
2338: "Null 'annotation' argument.");
2339: }
2340: this .annotations.add(annotation);
2341: notifyListeners(new PlotChangeEvent(this ));
2342: }
2343:
2344: /**
2345: * Removes an annotation from the plot and sends a {@link PlotChangeEvent}
2346: * to all registered listeners.
2347: *
2348: * @param annotation the annotation (<code>null</code> not permitted).
2349: *
2350: * @return A boolean (indicates whether or not the annotation was removed).
2351: *
2352: * @see #addAnnotation(XYAnnotation)
2353: * @see #getAnnotations()
2354: */
2355: public boolean removeAnnotation(XYAnnotation annotation) {
2356: if (annotation == null) {
2357: throw new IllegalArgumentException(
2358: "Null 'annotation' argument.");
2359: }
2360: boolean removed = this .annotations.remove(annotation);
2361: if (removed) {
2362: notifyListeners(new PlotChangeEvent(this ));
2363: }
2364: return removed;
2365: }
2366:
2367: /**
2368: * Returns the list of annotations.
2369: *
2370: * @return The list of annotations.
2371: *
2372: * @since 1.0.1
2373: *
2374: * @see #addAnnotation(XYAnnotation)
2375: */
2376: public List getAnnotations() {
2377: return new ArrayList(this .annotations);
2378: }
2379:
2380: /**
2381: * Clears all the annotations and sends a {@link PlotChangeEvent} to all
2382: * registered listeners.
2383: *
2384: * @see #addAnnotation(XYAnnotation)
2385: */
2386: public void clearAnnotations() {
2387: this .annotations.clear();
2388: notifyListeners(new PlotChangeEvent(this ));
2389: }
2390:
2391: /**
2392: * Calculates the space required for all the axes in the plot.
2393: *
2394: * @param g2 the graphics device.
2395: * @param plotArea the plot area.
2396: *
2397: * @return The required space.
2398: */
2399: protected AxisSpace calculateAxisSpace(Graphics2D g2,
2400: Rectangle2D plotArea) {
2401: AxisSpace space = new AxisSpace();
2402: space = calculateDomainAxisSpace(g2, plotArea, space);
2403: space = calculateRangeAxisSpace(g2, plotArea, space);
2404: return space;
2405: }
2406:
2407: /**
2408: * Calculates the space required for the domain axis/axes.
2409: *
2410: * @param g2 the graphics device.
2411: * @param plotArea the plot area.
2412: * @param space a carrier for the result (<code>null</code> permitted).
2413: *
2414: * @return The required space.
2415: */
2416: protected AxisSpace calculateDomainAxisSpace(Graphics2D g2,
2417: Rectangle2D plotArea, AxisSpace space) {
2418:
2419: if (space == null) {
2420: space = new AxisSpace();
2421: }
2422:
2423: // reserve some space for the domain axis...
2424: if (this .fixedDomainAxisSpace != null) {
2425: if (this .orientation == PlotOrientation.HORIZONTAL) {
2426: space.ensureAtLeast(
2427: this .fixedDomainAxisSpace.getLeft(),
2428: RectangleEdge.LEFT);
2429: space.ensureAtLeast(this .fixedDomainAxisSpace
2430: .getRight(), RectangleEdge.RIGHT);
2431: } else if (this .orientation == PlotOrientation.VERTICAL) {
2432: space.ensureAtLeast(this .fixedDomainAxisSpace.getTop(),
2433: RectangleEdge.TOP);
2434: space.ensureAtLeast(this .fixedDomainAxisSpace
2435: .getBottom(), RectangleEdge.BOTTOM);
2436: }
2437: } else {
2438: // reserve space for the domain axes...
2439: for (int i = 0; i < this .domainAxes.size(); i++) {
2440: Axis axis = (Axis) this .domainAxes.get(i);
2441: if (axis != null) {
2442: RectangleEdge edge = getDomainAxisEdge(i);
2443: space = axis.reserveSpace(g2, this , plotArea, edge,
2444: space);
2445: }
2446: }
2447: }
2448:
2449: return space;
2450:
2451: }
2452:
2453: /**
2454: * Calculates the space required for the range axis/axes.
2455: *
2456: * @param g2 the graphics device.
2457: * @param plotArea the plot area.
2458: * @param space a carrier for the result (<code>null</code> permitted).
2459: *
2460: * @return The required space.
2461: */
2462: protected AxisSpace calculateRangeAxisSpace(Graphics2D g2,
2463: Rectangle2D plotArea, AxisSpace space) {
2464:
2465: if (space == null) {
2466: space = new AxisSpace();
2467: }
2468:
2469: // reserve some space for the range axis...
2470: if (this .fixedRangeAxisSpace != null) {
2471: if (this .orientation == PlotOrientation.HORIZONTAL) {
2472: space.ensureAtLeast(this .fixedRangeAxisSpace.getTop(),
2473: RectangleEdge.TOP);
2474: space.ensureAtLeast(this .fixedRangeAxisSpace
2475: .getBottom(), RectangleEdge.BOTTOM);
2476: } else if (this .orientation == PlotOrientation.VERTICAL) {
2477: space.ensureAtLeast(this .fixedRangeAxisSpace.getLeft(),
2478: RectangleEdge.LEFT);
2479: space.ensureAtLeast(
2480: this .fixedRangeAxisSpace.getRight(),
2481: RectangleEdge.RIGHT);
2482: }
2483: } else {
2484: // reserve space for the range axes...
2485: for (int i = 0; i < this .rangeAxes.size(); i++) {
2486: Axis axis = (Axis) this .rangeAxes.get(i);
2487: if (axis != null) {
2488: RectangleEdge edge = getRangeAxisEdge(i);
2489: space = axis.reserveSpace(g2, this , plotArea, edge,
2490: space);
2491: }
2492: }
2493: }
2494: return space;
2495:
2496: }
2497:
2498: /**
2499: * Draws the plot within the specified area on a graphics device.
2500: *
2501: * @param g2 the graphics device.
2502: * @param area the plot area (in Java2D space).
2503: * @param anchor an anchor point in Java2D space (<code>null</code>
2504: * permitted).
2505: * @param parentState the state from the parent plot, if there is one
2506: * (<code>null</code> permitted).
2507: * @param info collects chart drawing information (<code>null</code>
2508: * permitted).
2509: */
2510: public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor,
2511: PlotState parentState, PlotRenderingInfo info) {
2512:
2513: // if the plot area is too small, just return...
2514: boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW);
2515: boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW);
2516: if (b1 || b2) {
2517: return;
2518: }
2519:
2520: // record the plot area...
2521: if (info != null) {
2522: info.setPlotArea(area);
2523: }
2524:
2525: // adjust the drawing area for the plot insets (if any)...
2526: RectangleInsets insets = getInsets();
2527: insets.trim(area);
2528:
2529: AxisSpace space = calculateAxisSpace(g2, area);
2530: Rectangle2D dataArea = space.shrink(area, null);
2531: this .axisOffset.trim(dataArea);
2532:
2533: if (info != null) {
2534: info.setDataArea(dataArea);
2535: }
2536:
2537: // draw the plot background and axes...
2538: drawBackground(g2, dataArea);
2539: Map axisStateMap = drawAxes(g2, area, dataArea, info);
2540:
2541: PlotOrientation orient = getOrientation();
2542:
2543: // the anchor point is typically the point where the mouse last
2544: // clicked - the crosshairs will be driven off this point...
2545: if (anchor != null && !dataArea.contains(anchor)) {
2546: anchor = null;
2547: }
2548: CrosshairState crosshairState = new CrosshairState();
2549: crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY);
2550: crosshairState.setAnchor(anchor);
2551:
2552: crosshairState.setAnchorX(Double.NaN);
2553: crosshairState.setAnchorY(Double.NaN);
2554: if (anchor != null) {
2555: ValueAxis domainAxis = getDomainAxis();
2556: if (domainAxis != null) {
2557: double x;
2558: if (orient == PlotOrientation.VERTICAL) {
2559: x = domainAxis.java2DToValue(anchor.getX(),
2560: dataArea, getDomainAxisEdge());
2561: } else {
2562: x = domainAxis.java2DToValue(anchor.getY(),
2563: dataArea, getDomainAxisEdge());
2564: }
2565: crosshairState.setAnchorX(x);
2566: }
2567: ValueAxis rangeAxis = getRangeAxis();
2568: if (rangeAxis != null) {
2569: double y;
2570: if (orient == PlotOrientation.VERTICAL) {
2571: y = rangeAxis.java2DToValue(anchor.getY(),
2572: dataArea, getRangeAxisEdge());
2573: } else {
2574: y = rangeAxis.java2DToValue(anchor.getX(),
2575: dataArea, getRangeAxisEdge());
2576: }
2577: crosshairState.setAnchorY(y);
2578: }
2579: }
2580: crosshairState.setCrosshairX(getDomainCrosshairValue());
2581: crosshairState.setCrosshairY(getRangeCrosshairValue());
2582: Shape originalClip = g2.getClip();
2583: Composite originalComposite = g2.getComposite();
2584:
2585: g2.clip(dataArea);
2586: g2.setComposite(AlphaComposite.getInstance(
2587: AlphaComposite.SRC_OVER, getForegroundAlpha()));
2588:
2589: AxisState domainAxisState = (AxisState) axisStateMap
2590: .get(getDomainAxis());
2591: if (domainAxisState == null) {
2592: if (parentState != null) {
2593: domainAxisState = (AxisState) parentState
2594: .getSharedAxisStates().get(getDomainAxis());
2595: }
2596: }
2597:
2598: AxisState rangeAxisState = (AxisState) axisStateMap
2599: .get(getRangeAxis());
2600: if (rangeAxisState == null) {
2601: if (parentState != null) {
2602: rangeAxisState = (AxisState) parentState
2603: .getSharedAxisStates().get(getRangeAxis());
2604: }
2605: }
2606: if (domainAxisState != null) {
2607: drawDomainTickBands(g2, dataArea, domainAxisState
2608: .getTicks());
2609: }
2610: if (rangeAxisState != null) {
2611: drawRangeTickBands(g2, dataArea, rangeAxisState.getTicks());
2612: }
2613: if (domainAxisState != null) {
2614: drawDomainGridlines(g2, dataArea, domainAxisState
2615: .getTicks());
2616: drawZeroDomainBaseline(g2, dataArea);
2617: }
2618: if (rangeAxisState != null) {
2619: drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks());
2620: drawZeroRangeBaseline(g2, dataArea);
2621: }
2622:
2623: // draw the markers that are associated with a specific renderer...
2624: for (int i = 0; i < this .renderers.size(); i++) {
2625: drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND);
2626: }
2627: for (int i = 0; i < this .renderers.size(); i++) {
2628: drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND);
2629: }
2630:
2631: // now draw annotations and render data items...
2632: boolean foundData = false;
2633: DatasetRenderingOrder order = getDatasetRenderingOrder();
2634: if (order == DatasetRenderingOrder.FORWARD) {
2635:
2636: // draw background annotations
2637: int rendererCount = this .renderers.size();
2638: for (int i = 0; i < rendererCount; i++) {
2639: XYItemRenderer r = getRenderer(i);
2640: if (r != null) {
2641: ValueAxis domainAxis = getDomainAxisForDataset(i);
2642: ValueAxis rangeAxis = getRangeAxisForDataset(i);
2643: r.drawAnnotations(g2, dataArea, domainAxis,
2644: rangeAxis, Layer.BACKGROUND, info);
2645: }
2646: }
2647:
2648: // render data items...
2649: for (int i = 0; i < getDatasetCount(); i++) {
2650: foundData = render(g2, dataArea, i, info,
2651: crosshairState)
2652: || foundData;
2653: }
2654:
2655: // draw foreground annotations
2656: for (int i = 0; i < rendererCount; i++) {
2657: XYItemRenderer r = getRenderer(i);
2658: if (r != null) {
2659: ValueAxis domainAxis = getDomainAxisForDataset(i);
2660: ValueAxis rangeAxis = getRangeAxisForDataset(i);
2661: r.drawAnnotations(g2, dataArea, domainAxis,
2662: rangeAxis, Layer.FOREGROUND, info);
2663: }
2664: }
2665:
2666: } else if (order == DatasetRenderingOrder.REVERSE) {
2667:
2668: // draw background annotations
2669: int rendererCount = this .renderers.size();
2670: for (int i = rendererCount - 1; i >= 0; i--) {
2671: XYItemRenderer r = getRenderer(i);
2672: if (i >= getDatasetCount()) { // we need the dataset to make
2673: continue; // a link to the axes
2674: }
2675: if (r != null) {
2676: ValueAxis domainAxis = getDomainAxisForDataset(i);
2677: ValueAxis rangeAxis = getRangeAxisForDataset(i);
2678: r.drawAnnotations(g2, dataArea, domainAxis,
2679: rangeAxis, Layer.BACKGROUND, info);
2680: }
2681: }
2682:
2683: for (int i = getDatasetCount() - 1; i >= 0; i--) {
2684: foundData = render(g2, dataArea, i, info,
2685: crosshairState)
2686: || foundData;
2687: }
2688:
2689: // draw foreground annotations
2690: for (int i = rendererCount - 1; i >= 0; i--) {
2691: XYItemRenderer r = getRenderer(i);
2692: if (i >= getDatasetCount()) { // we need the dataset to make
2693: continue; // a link to the axes
2694: }
2695: if (r != null) {
2696: ValueAxis domainAxis = getDomainAxisForDataset(i);
2697: ValueAxis rangeAxis = getRangeAxisForDataset(i);
2698: r.drawAnnotations(g2, dataArea, domainAxis,
2699: rangeAxis, Layer.FOREGROUND, info);
2700: }
2701: }
2702:
2703: }
2704:
2705: // draw domain crosshair if required...
2706: int xAxisIndex = crosshairState.getDomainAxisIndex();
2707: ValueAxis xAxis = getDomainAxis(xAxisIndex);
2708: RectangleEdge xAxisEdge = getDomainAxisEdge(xAxisIndex);
2709: if (!this .domainCrosshairLockedOnData && anchor != null) {
2710: double xx;
2711: if (orient == PlotOrientation.VERTICAL) {
2712: xx = xAxis.java2DToValue(anchor.getX(), dataArea,
2713: xAxisEdge);
2714: } else {
2715: xx = xAxis.java2DToValue(anchor.getY(), dataArea,
2716: xAxisEdge);
2717: }
2718: crosshairState.setCrosshairX(xx);
2719: }
2720: setDomainCrosshairValue(crosshairState.getCrosshairX(), false);
2721: if (isDomainCrosshairVisible()) {
2722: double x = getDomainCrosshairValue();
2723: Paint paint = getDomainCrosshairPaint();
2724: Stroke stroke = getDomainCrosshairStroke();
2725: drawDomainCrosshair(g2, dataArea, orient, x, xAxis, stroke,
2726: paint);
2727: }
2728:
2729: // draw range crosshair if required...
2730: int yAxisIndex = crosshairState.getRangeAxisIndex();
2731: ValueAxis yAxis = getRangeAxis(yAxisIndex);
2732: RectangleEdge yAxisEdge = getRangeAxisEdge(yAxisIndex);
2733: if (!this .rangeCrosshairLockedOnData && anchor != null) {
2734: double yy;
2735: if (orient == PlotOrientation.VERTICAL) {
2736: yy = yAxis.java2DToValue(anchor.getY(), dataArea,
2737: yAxisEdge);
2738: } else {
2739: yy = yAxis.java2DToValue(anchor.getX(), dataArea,
2740: yAxisEdge);
2741: }
2742: crosshairState.setCrosshairY(yy);
2743: }
2744: setRangeCrosshairValue(crosshairState.getCrosshairY(), false);
2745: if (isRangeCrosshairVisible()) {
2746: double y = getRangeCrosshairValue();
2747: Paint paint = getRangeCrosshairPaint();
2748: Stroke stroke = getRangeCrosshairStroke();
2749: drawRangeCrosshair(g2, dataArea, orient, y, yAxis, stroke,
2750: paint);
2751: }
2752:
2753: if (!foundData) {
2754: drawNoDataMessage(g2, dataArea);
2755: }
2756:
2757: for (int i = 0; i < this .renderers.size(); i++) {
2758: drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND);
2759: }
2760: for (int i = 0; i < this .renderers.size(); i++) {
2761: drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND);
2762: }
2763:
2764: drawAnnotations(g2, dataArea, info);
2765: g2.setClip(originalClip);
2766: g2.setComposite(originalComposite);
2767:
2768: drawOutline(g2, dataArea);
2769:
2770: }
2771:
2772: /**
2773: * Draws the background for the plot.
2774: *
2775: * @param g2 the graphics device.
2776: * @param area the area.
2777: */
2778: public void drawBackground(Graphics2D g2, Rectangle2D area) {
2779: fillBackground(g2, area, this .orientation);
2780: drawQuadrants(g2, area);
2781: drawBackgroundImage(g2, area);
2782: }
2783:
2784: /**
2785: * Draws the quadrants.
2786: *
2787: * @param g2 the graphics device.
2788: * @param area the area.
2789: *
2790: * @see #setQuadrantOrigin(Point2D)
2791: * @see #setQuadrantPaint(int, Paint)
2792: */
2793: protected void drawQuadrants(Graphics2D g2, Rectangle2D area) {
2794: // 0 | 1
2795: // --+--
2796: // 2 | 3
2797: boolean somethingToDraw = false;
2798:
2799: ValueAxis xAxis = getDomainAxis();
2800: double x = this .quadrantOrigin.getX();
2801: double xx = xAxis.valueToJava2D(x, area, getDomainAxisEdge());
2802:
2803: ValueAxis yAxis = getRangeAxis();
2804: double y = this .quadrantOrigin.getY();
2805: double yy = yAxis.valueToJava2D(y, area, getRangeAxisEdge());
2806:
2807: double xmin = xAxis.getLowerBound();
2808: double xxmin = xAxis.valueToJava2D(xmin, area,
2809: getDomainAxisEdge());
2810:
2811: double xmax = xAxis.getUpperBound();
2812: double xxmax = xAxis.valueToJava2D(xmax, area,
2813: getDomainAxisEdge());
2814:
2815: double ymin = yAxis.getLowerBound();
2816: double yymin = yAxis.valueToJava2D(ymin, area,
2817: getRangeAxisEdge());
2818:
2819: double ymax = yAxis.getUpperBound();
2820: double yymax = yAxis.valueToJava2D(ymax, area,
2821: getRangeAxisEdge());
2822:
2823: Rectangle2D[] r = new Rectangle2D[] { null, null, null, null };
2824: if (this .quadrantPaint[0] != null) {
2825: if (x > xmin && y < ymax) {
2826: if (this .orientation == PlotOrientation.HORIZONTAL) {
2827: r[0] = new Rectangle2D.Double(Math.min(yymax, yy),
2828: Math.min(xxmin, xx), Math.abs(yy - yymax),
2829: Math.abs(xx - xxmin));
2830: } else { // PlotOrientation.VERTICAL
2831: r[0] = new Rectangle2D.Double(Math.min(xxmin, xx),
2832: Math.min(yymax, yy), Math.abs(xx - xxmin),
2833: Math.abs(yy - yymax));
2834: }
2835: somethingToDraw = true;
2836: }
2837: }
2838: if (this .quadrantPaint[1] != null) {
2839: if (x < xmax && y < ymax) {
2840: if (this .orientation == PlotOrientation.HORIZONTAL) {
2841: r[1] = new Rectangle2D.Double(Math.min(yymax, yy),
2842: Math.min(xxmax, xx), Math.abs(yy - yymax),
2843: Math.abs(xx - xxmax));
2844: } else { // PlotOrientation.VERTICAL
2845: r[1] = new Rectangle2D.Double(Math.min(xx, xxmax),
2846: Math.min(yymax, yy), Math.abs(xx - xxmax),
2847: Math.abs(yy - yymax));
2848: }
2849: somethingToDraw = true;
2850: }
2851: }
2852: if (this .quadrantPaint[2] != null) {
2853: if (x > xmin && y > ymin) {
2854: if (this .orientation == PlotOrientation.HORIZONTAL) {
2855: r[2] = new Rectangle2D.Double(Math.min(yymin, yy),
2856: Math.min(xxmin, xx), Math.abs(yy - yymin),
2857: Math.abs(xx - xxmin));
2858: } else { // PlotOrientation.VERTICAL
2859: r[2] = new Rectangle2D.Double(Math.min(xxmin, xx),
2860: Math.min(yymin, yy), Math.abs(xx - xxmin),
2861: Math.abs(yy - yymin));
2862: }
2863: somethingToDraw = true;
2864: }
2865: }
2866: if (this .quadrantPaint[3] != null) {
2867: if (x < xmax && y > ymin) {
2868: if (this .orientation == PlotOrientation.HORIZONTAL) {
2869: r[3] = new Rectangle2D.Double(Math.min(yymin, yy),
2870: Math.min(xxmax, xx), Math.abs(yy - yymin),
2871: Math.abs(xx - xxmax));
2872: } else { // PlotOrientation.VERTICAL
2873: r[3] = new Rectangle2D.Double(Math.min(xx, xxmax),
2874: Math.min(yymin, yy), Math.abs(xx - xxmax),
2875: Math.abs(yy - yymin));
2876: }
2877: somethingToDraw = true;
2878: }
2879: }
2880: if (somethingToDraw) {
2881: Composite originalComposite = g2.getComposite();
2882: g2.setComposite(AlphaComposite.getInstance(
2883: AlphaComposite.SRC_OVER, getBackgroundAlpha()));
2884: for (int i = 0; i < 4; i++) {
2885: if (this .quadrantPaint[i] != null && r[i] != null) {
2886: g2.setPaint(this .quadrantPaint[i]);
2887: g2.fill(r[i]);
2888: }
2889: }
2890: g2.setComposite(originalComposite);
2891: }
2892: }
2893:
2894: /**
2895: * Draws the domain tick bands, if any.
2896: *
2897: * @param g2 the graphics device.
2898: * @param dataArea the data area.
2899: * @param ticks the ticks.
2900: *
2901: * @see #setDomainTickBandPaint(Paint)
2902: */
2903: public void drawDomainTickBands(Graphics2D g2,
2904: Rectangle2D dataArea, List ticks) {
2905: // draw the domain tick bands, if any...
2906: Paint bandPaint = getDomainTickBandPaint();
2907: if (bandPaint != null) {
2908: boolean fillBand = false;
2909: ValueAxis xAxis = getDomainAxis();
2910: double previous = xAxis.getLowerBound();
2911: Iterator iterator = ticks.iterator();
2912: while (iterator.hasNext()) {
2913: ValueTick tick = (ValueTick) iterator.next();
2914: double current = tick.getValue();
2915: if (fillBand) {
2916: getRenderer().fillDomainGridBand(g2, this , xAxis,
2917: dataArea, previous, current);
2918: }
2919: previous = current;
2920: fillBand = !fillBand;
2921: }
2922: double end = xAxis.getUpperBound();
2923: if (fillBand) {
2924: getRenderer().fillDomainGridBand(g2, this , xAxis,
2925: dataArea, previous, end);
2926: }
2927: }
2928: }
2929:
2930: /**
2931: * Draws the range tick bands, if any.
2932: *
2933: * @param g2 the graphics device.
2934: * @param dataArea the data area.
2935: * @param ticks the ticks.
2936: *
2937: * @see #setRangeTickBandPaint(Paint)
2938: */
2939: public void drawRangeTickBands(Graphics2D g2, Rectangle2D dataArea,
2940: List ticks) {
2941:
2942: // draw the range tick bands, if any...
2943: Paint bandPaint = getRangeTickBandPaint();
2944: if (bandPaint != null) {
2945: boolean fillBand = false;
2946: ValueAxis axis = getRangeAxis();
2947: double previous = axis.getLowerBound();
2948: Iterator iterator = ticks.iterator();
2949: while (iterator.hasNext()) {
2950: ValueTick tick = (ValueTick) iterator.next();
2951: double current = tick.getValue();
2952: if (fillBand) {
2953: getRenderer().fillRangeGridBand(g2, this , axis,
2954: dataArea, previous, current);
2955: }
2956: previous = current;
2957: fillBand = !fillBand;
2958: }
2959: double end = axis.getUpperBound();
2960: if (fillBand) {
2961: getRenderer().fillRangeGridBand(g2, this , axis,
2962: dataArea, previous, end);
2963: }
2964: }
2965: }
2966:
2967: /**
2968: * A utility method for drawing the axes.
2969: *
2970: * @param g2 the graphics device (<code>null</code> not permitted).
2971: * @param plotArea the plot area (<code>null</code> not permitted).
2972: * @param dataArea the data area (<code>null</code> not permitted).
2973: * @param plotState collects information about the plot (<code>null</code>
2974: * permitted).
2975: *
2976: * @return A map containing the state for each axis drawn.
2977: */
2978: protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea,
2979: Rectangle2D dataArea, PlotRenderingInfo plotState) {
2980:
2981: AxisCollection axisCollection = new AxisCollection();
2982:
2983: // add domain axes to lists...
2984: for (int index = 0; index < this .domainAxes.size(); index++) {
2985: ValueAxis axis = (ValueAxis) this .domainAxes.get(index);
2986: if (axis != null) {
2987: axisCollection.add(axis, getDomainAxisEdge(index));
2988: }
2989: }
2990:
2991: // add range axes to lists...
2992: for (int index = 0; index < this .rangeAxes.size(); index++) {
2993: ValueAxis yAxis = (ValueAxis) this .rangeAxes.get(index);
2994: if (yAxis != null) {
2995: axisCollection.add(yAxis, getRangeAxisEdge(index));
2996: }
2997: }
2998:
2999: Map axisStateMap = new HashMap();
3000:
3001: // draw the top axes
3002: double cursor = dataArea.getMinY()
3003: - this .axisOffset.calculateTopOutset(dataArea
3004: .getHeight());
3005: Iterator iterator = axisCollection.getAxesAtTop().iterator();
3006: while (iterator.hasNext()) {
3007: ValueAxis axis = (ValueAxis) iterator.next();
3008: AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3009: RectangleEdge.TOP, plotState);
3010: cursor = info.getCursor();
3011: axisStateMap.put(axis, info);
3012: }
3013:
3014: // draw the bottom axes
3015: cursor = dataArea.getMaxY()
3016: + this .axisOffset.calculateBottomOutset(dataArea
3017: .getHeight());
3018: iterator = axisCollection.getAxesAtBottom().iterator();
3019: while (iterator.hasNext()) {
3020: ValueAxis axis = (ValueAxis) iterator.next();
3021: AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3022: RectangleEdge.BOTTOM, plotState);
3023: cursor = info.getCursor();
3024: axisStateMap.put(axis, info);
3025: }
3026:
3027: // draw the left axes
3028: cursor = dataArea.getMinX()
3029: - this .axisOffset.calculateLeftOutset(dataArea
3030: .getWidth());
3031: iterator = axisCollection.getAxesAtLeft().iterator();
3032: while (iterator.hasNext()) {
3033: ValueAxis axis = (ValueAxis) iterator.next();
3034: AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3035: RectangleEdge.LEFT, plotState);
3036: cursor = info.getCursor();
3037: axisStateMap.put(axis, info);
3038: }
3039:
3040: // draw the right axes
3041: cursor = dataArea.getMaxX()
3042: + this .axisOffset.calculateRightOutset(dataArea
3043: .getWidth());
3044: iterator = axisCollection.getAxesAtRight().iterator();
3045: while (iterator.hasNext()) {
3046: ValueAxis axis = (ValueAxis) iterator.next();
3047: AxisState info = axis.draw(g2, cursor, plotArea, dataArea,
3048: RectangleEdge.RIGHT, plotState);
3049: cursor = info.getCursor();
3050: axisStateMap.put(axis, info);
3051: }
3052:
3053: return axisStateMap;
3054: }
3055:
3056: /**
3057: * Draws a representation of the data within the dataArea region, using the
3058: * current renderer.
3059: * <P>
3060: * The <code>info</code> and <code>crosshairState</code> arguments may be
3061: * <code>null</code>.
3062: *
3063: * @param g2 the graphics device.
3064: * @param dataArea the region in which the data is to be drawn.
3065: * @param index the dataset index.
3066: * @param info an optional object for collection dimension information.
3067: * @param crosshairState collects crosshair information
3068: * (<code>null</code> permitted).
3069: *
3070: * @return A flag that indicates whether any data was actually rendered.
3071: */
3072: public boolean render(Graphics2D g2, Rectangle2D dataArea,
3073: int index, PlotRenderingInfo info,
3074: CrosshairState crosshairState) {
3075:
3076: boolean foundData = false;
3077: XYDataset dataset = getDataset(index);
3078: if (!DatasetUtilities.isEmptyOrNull(dataset)) {
3079: foundData = true;
3080: ValueAxis xAxis = getDomainAxisForDataset(index);
3081: ValueAxis yAxis = getRangeAxisForDataset(index);
3082: XYItemRenderer renderer = getRenderer(index);
3083: if (renderer == null) {
3084: renderer = getRenderer();
3085: if (renderer == null) { // no default renderer available
3086: return foundData;
3087: }
3088: }
3089:
3090: XYItemRendererState state = renderer.initialise(g2,
3091: dataArea, this , dataset, info);
3092: int passCount = renderer.getPassCount();
3093:
3094: SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder();
3095: if (seriesOrder == SeriesRenderingOrder.REVERSE) {
3096: //render series in reverse order
3097: for (int pass = 0; pass < passCount; pass++) {
3098: int seriesCount = dataset.getSeriesCount();
3099: for (int series = seriesCount - 1; series >= 0; series--) {
3100: int firstItem = 0;
3101: int lastItem = dataset.getItemCount(series) - 1;
3102: if (lastItem == -1) {
3103: continue;
3104: }
3105: if (state.getProcessVisibleItemsOnly()) {
3106: int[] itemBounds = RendererUtilities
3107: .findLiveItems(dataset, series,
3108: xAxis.getLowerBound(),
3109: xAxis.getUpperBound());
3110: firstItem = itemBounds[0];
3111: lastItem = itemBounds[1];
3112: }
3113: for (int item = firstItem; item <= lastItem; item++) {
3114: renderer.drawItem(g2, state, dataArea,
3115: info, this , xAxis, yAxis, dataset,
3116: series, item, crosshairState, pass);
3117: }
3118: }
3119: }
3120: } else {
3121: //render series in forward order
3122: for (int pass = 0; pass < passCount; pass++) {
3123: int seriesCount = dataset.getSeriesCount();
3124: for (int series = 0; series < seriesCount; series++) {
3125: int firstItem = 0;
3126: int lastItem = dataset.getItemCount(series) - 1;
3127: if (state.getProcessVisibleItemsOnly()) {
3128: int[] itemBounds = RendererUtilities
3129: .findLiveItems(dataset, series,
3130: xAxis.getLowerBound(),
3131: xAxis.getUpperBound());
3132: firstItem = itemBounds[0];
3133: lastItem = itemBounds[1];
3134: }
3135: for (int item = firstItem; item <= lastItem; item++) {
3136: renderer.drawItem(g2, state, dataArea,
3137: info, this , xAxis, yAxis, dataset,
3138: series, item, crosshairState, pass);
3139: }
3140: }
3141: }
3142: }
3143: }
3144: return foundData;
3145: }
3146:
3147: /**
3148: * Returns the domain axis for a dataset.
3149: *
3150: * @param index the dataset index.
3151: *
3152: * @return The axis.
3153: */
3154: public ValueAxis getDomainAxisForDataset(int index) {
3155:
3156: if (index < 0 || index >= getDatasetCount()) {
3157: throw new IllegalArgumentException(
3158: "Index 'index' out of bounds.");
3159: }
3160:
3161: ValueAxis valueAxis = null;
3162: Integer axisIndex = (Integer) this .datasetToDomainAxisMap
3163: .get(new Integer(index));
3164: if (axisIndex != null) {
3165: valueAxis = getDomainAxis(axisIndex.intValue());
3166: } else {
3167: valueAxis = getDomainAxis(0);
3168: }
3169: return valueAxis;
3170:
3171: }
3172:
3173: /**
3174: * Returns the range axis for a dataset.
3175: *
3176: * @param index the dataset index.
3177: *
3178: * @return The axis.
3179: */
3180: public ValueAxis getRangeAxisForDataset(int index) {
3181:
3182: if (index < 0 || index >= getDatasetCount()) {
3183: throw new IllegalArgumentException(
3184: "Index 'index' out of bounds.");
3185: }
3186:
3187: ValueAxis valueAxis = null;
3188: Integer axisIndex = (Integer) this .datasetToRangeAxisMap
3189: .get(new Integer(index));
3190: if (axisIndex != null) {
3191: valueAxis = getRangeAxis(axisIndex.intValue());
3192: } else {
3193: valueAxis = getRangeAxis(0);
3194: }
3195: return valueAxis;
3196:
3197: }
3198:
3199: /**
3200: * Draws the gridlines for the plot, if they are visible.
3201: *
3202: * @param g2 the graphics device.
3203: * @param dataArea the data area.
3204: * @param ticks the ticks.
3205: */
3206: protected void drawDomainGridlines(Graphics2D g2,
3207: Rectangle2D dataArea, List ticks) {
3208:
3209: // no renderer, no gridlines...
3210: if (getRenderer() == null) {
3211: return;
3212: }
3213:
3214: // draw the domain grid lines, if any...
3215: if (isDomainGridlinesVisible()) {
3216: Stroke gridStroke = getDomainGridlineStroke();
3217: Paint gridPaint = getDomainGridlinePaint();
3218: if ((gridStroke != null) && (gridPaint != null)) {
3219: Iterator iterator = ticks.iterator();
3220: while (iterator.hasNext()) {
3221: ValueTick tick = (ValueTick) iterator.next();
3222: getRenderer().drawDomainGridLine(g2, this ,
3223: getDomainAxis(), dataArea, tick.getValue());
3224: }
3225: }
3226: }
3227: }
3228:
3229: /**
3230: * Draws the gridlines for the plot's primary range axis, if they are
3231: * visible.
3232: *
3233: * @param g2 the graphics device.
3234: * @param area the data area.
3235: * @param ticks the ticks.
3236: */
3237: protected void drawRangeGridlines(Graphics2D g2, Rectangle2D area,
3238: List ticks) {
3239:
3240: // no renderer, no gridlines...
3241: if (getRenderer() == null) {
3242: return;
3243: }
3244:
3245: // draw the range grid lines, if any...
3246: if (isRangeGridlinesVisible()) {
3247: Stroke gridStroke = getRangeGridlineStroke();
3248: Paint gridPaint = getRangeGridlinePaint();
3249: ValueAxis axis = getRangeAxis();
3250: if (axis != null) {
3251: Iterator iterator = ticks.iterator();
3252: while (iterator.hasNext()) {
3253: ValueTick tick = (ValueTick) iterator.next();
3254: if (tick.getValue() != 0.0
3255: || !isRangeZeroBaselineVisible()) {
3256: getRenderer().drawRangeLine(g2, this ,
3257: getRangeAxis(), area, tick.getValue(),
3258: gridPaint, gridStroke);
3259: }
3260: }
3261: }
3262: }
3263: }
3264:
3265: /**
3266: * Draws a base line across the chart at value zero on the domain axis.
3267: *
3268: * @param g2 the graphics device.
3269: * @param area the data area.
3270: *
3271: * @see #setDomainZeroBaselineVisible(boolean)
3272: *
3273: * @since 1.0.5
3274: */
3275: protected void drawZeroDomainBaseline(Graphics2D g2,
3276: Rectangle2D area) {
3277: if (isDomainZeroBaselineVisible()) {
3278: XYItemRenderer r = getRenderer();
3279: // FIXME: the renderer interface doesn't have the drawDomainLine()
3280: // method, so we have to rely on the renderer being a subclass of
3281: // AbstractXYItemRenderer (which is lame)
3282: if (r instanceof AbstractXYItemRenderer) {
3283: AbstractXYItemRenderer renderer = (AbstractXYItemRenderer) r;
3284: renderer.drawDomainLine(g2, this , getDomainAxis(),
3285: area, 0.0, this .domainZeroBaselinePaint,
3286: this .domainZeroBaselineStroke);
3287: }
3288: }
3289: }
3290:
3291: /**
3292: * Draws a base line across the chart at value zero on the range axis.
3293: *
3294: * @param g2 the graphics device.
3295: * @param area the data area.
3296: *
3297: * @see #setRangeZeroBaselineVisible(boolean)
3298: */
3299: protected void drawZeroRangeBaseline(Graphics2D g2, Rectangle2D area) {
3300: if (isRangeZeroBaselineVisible()) {
3301: getRenderer().drawRangeLine(g2, this , getRangeAxis(), area,
3302: 0.0, this .rangeZeroBaselinePaint,
3303: this .rangeZeroBaselineStroke);
3304: }
3305: }
3306:
3307: /**
3308: * Draws the annotations for the plot.
3309: *
3310: * @param g2 the graphics device.
3311: * @param dataArea the data area.
3312: * @param info the chart rendering info.
3313: */
3314: public void drawAnnotations(Graphics2D g2, Rectangle2D dataArea,
3315: PlotRenderingInfo info) {
3316:
3317: Iterator iterator = this .annotations.iterator();
3318: while (iterator.hasNext()) {
3319: XYAnnotation annotation = (XYAnnotation) iterator.next();
3320: ValueAxis xAxis = getDomainAxis();
3321: ValueAxis yAxis = getRangeAxis();
3322: annotation.draw(g2, this , dataArea, xAxis, yAxis, 0, info);
3323: }
3324:
3325: }
3326:
3327: /**
3328: * Draws the domain markers (if any) for an axis and layer. This method is
3329: * typically called from within the draw() method.
3330: *
3331: * @param g2 the graphics device.
3332: * @param dataArea the data area.
3333: * @param index the renderer index.
3334: * @param layer the layer (foreground or background).
3335: */
3336: protected void drawDomainMarkers(Graphics2D g2,
3337: Rectangle2D dataArea, int index, Layer layer) {
3338:
3339: XYItemRenderer r = getRenderer(index);
3340: if (r == null) {
3341: return;
3342: }
3343: // check that the renderer has a corresponding dataset (it doesn't
3344: // matter if the dataset is null)
3345: if (index >= getDatasetCount()) {
3346: return;
3347: }
3348: Collection markers = getDomainMarkers(index, layer);
3349: ValueAxis axis = getDomainAxisForDataset(index);
3350: if (markers != null && axis != null) {
3351: Iterator iterator = markers.iterator();
3352: while (iterator.hasNext()) {
3353: Marker marker = (Marker) iterator.next();
3354: r.drawDomainMarker(g2, this , axis, marker, dataArea);
3355: }
3356: }
3357:
3358: }
3359:
3360: /**
3361: * Draws the range markers (if any) for a renderer and layer. This method
3362: * is typically called from within the draw() method.
3363: *
3364: * @param g2 the graphics device.
3365: * @param dataArea the data area.
3366: * @param index the renderer index.
3367: * @param layer the layer (foreground or background).
3368: */
3369: protected void drawRangeMarkers(Graphics2D g2,
3370: Rectangle2D dataArea, int index, Layer layer) {
3371:
3372: XYItemRenderer r = getRenderer(index);
3373: if (r == null) {
3374: return;
3375: }
3376: // check that the renderer has a corresponding dataset (it doesn't
3377: // matter if the dataset is null)
3378: if (index >= getDatasetCount()) {
3379: return;
3380: }
3381: Collection markers = getRangeMarkers(index, layer);
3382: ValueAxis axis = getRangeAxisForDataset(index);
3383: if (markers != null && axis != null) {
3384: Iterator iterator = markers.iterator();
3385: while (iterator.hasNext()) {
3386: Marker marker = (Marker) iterator.next();
3387: r.drawRangeMarker(g2, this , axis, marker, dataArea);
3388: }
3389: }
3390: }
3391:
3392: /**
3393: * Returns the list of domain markers (read only) for the specified layer.
3394: *
3395: * @param layer the layer (foreground or background).
3396: *
3397: * @return The list of domain markers.
3398: *
3399: * @see #getRangeMarkers(Layer)
3400: */
3401: public Collection getDomainMarkers(Layer layer) {
3402: return getDomainMarkers(0, layer);
3403: }
3404:
3405: /**
3406: * Returns the list of range markers (read only) for the specified layer.
3407: *
3408: * @param layer the layer (foreground or background).
3409: *
3410: * @return The list of range markers.
3411: *
3412: * @see #getDomainMarkers(Layer)
3413: */
3414: public Collection getRangeMarkers(Layer layer) {
3415: return getRangeMarkers(0, layer);
3416: }
3417:
3418: /**
3419: * Returns a collection of domain markers for a particular renderer and
3420: * layer.
3421: *
3422: * @param index the renderer index.
3423: * @param layer the layer.
3424: *
3425: * @return A collection of markers (possibly <code>null</code>).
3426: *
3427: * @see #getRangeMarkers(int, Layer)
3428: */
3429: public Collection getDomainMarkers(int index, Layer layer) {
3430: Collection result = null;
3431: Integer key = new Integer(index);
3432: if (layer == Layer.FOREGROUND) {
3433: result = (Collection) this .foregroundDomainMarkers.get(key);
3434: } else if (layer == Layer.BACKGROUND) {
3435: result = (Collection) this .backgroundDomainMarkers.get(key);
3436: }
3437: if (result != null) {
3438: result = Collections.unmodifiableCollection(result);
3439: }
3440: return result;
3441: }
3442:
3443: /**
3444: * Returns a collection of range markers for a particular renderer and
3445: * layer.
3446: *
3447: * @param index the renderer index.
3448: * @param layer the layer.
3449: *
3450: * @return A collection of markers (possibly <code>null</code>).
3451: *
3452: * @see #getDomainMarkers(int, Layer)
3453: */
3454: public Collection getRangeMarkers(int index, Layer layer) {
3455: Collection result = null;
3456: Integer key = new Integer(index);
3457: if (layer == Layer.FOREGROUND) {
3458: result = (Collection) this .foregroundRangeMarkers.get(key);
3459: } else if (layer == Layer.BACKGROUND) {
3460: result = (Collection) this .backgroundRangeMarkers.get(key);
3461: }
3462: if (result != null) {
3463: result = Collections.unmodifiableCollection(result);
3464: }
3465: return result;
3466: }
3467:
3468: /**
3469: * Utility method for drawing a horizontal line across the data area of the
3470: * plot.
3471: *
3472: * @param g2 the graphics device.
3473: * @param dataArea the data area.
3474: * @param value the coordinate, where to draw the line.
3475: * @param stroke the stroke to use.
3476: * @param paint the paint to use.
3477: */
3478: protected void drawHorizontalLine(Graphics2D g2,
3479: Rectangle2D dataArea, double value, Stroke stroke,
3480: Paint paint) {
3481:
3482: ValueAxis axis = getRangeAxis();
3483: if (getOrientation() == PlotOrientation.HORIZONTAL) {
3484: axis = getDomainAxis();
3485: }
3486: if (axis.getRange().contains(value)) {
3487: double yy = axis.valueToJava2D(value, dataArea,
3488: RectangleEdge.LEFT);
3489: Line2D line = new Line2D.Double(dataArea.getMinX(), yy,
3490: dataArea.getMaxX(), yy);
3491: g2.setStroke(stroke);
3492: g2.setPaint(paint);
3493: g2.draw(line);
3494: }
3495:
3496: }
3497:
3498: /**
3499: * Draws a domain crosshair.
3500: *
3501: * @param g2 the graphics target.
3502: * @param dataArea the data area.
3503: * @param orientation the plot orientation.
3504: * @param value the crosshair value.
3505: * @param axis the axis against which the value is measured.
3506: * @param stroke the stroke used to draw the crosshair line.
3507: * @param paint the paint used to draw the crosshair line.
3508: *
3509: * @since 1.0.4
3510: */
3511: protected void drawDomainCrosshair(Graphics2D g2,
3512: Rectangle2D dataArea, PlotOrientation orientation,
3513: double value, ValueAxis axis, Stroke stroke, Paint paint) {
3514:
3515: if (axis.getRange().contains(value)) {
3516: Line2D line = null;
3517: if (orientation == PlotOrientation.VERTICAL) {
3518: double xx = axis.valueToJava2D(value, dataArea,
3519: RectangleEdge.BOTTOM);
3520: line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3521: dataArea.getMaxY());
3522: } else {
3523: double yy = axis.valueToJava2D(value, dataArea,
3524: RectangleEdge.LEFT);
3525: line = new Line2D.Double(dataArea.getMinX(), yy,
3526: dataArea.getMaxX(), yy);
3527: }
3528: g2.setStroke(stroke);
3529: g2.setPaint(paint);
3530: g2.draw(line);
3531: }
3532:
3533: }
3534:
3535: /**
3536: * Utility method for drawing a vertical line on the data area of the plot.
3537: *
3538: * @param g2 the graphics device.
3539: * @param dataArea the data area.
3540: * @param value the coordinate, where to draw the line.
3541: * @param stroke the stroke to use.
3542: * @param paint the paint to use.
3543: */
3544: protected void drawVerticalLine(Graphics2D g2,
3545: Rectangle2D dataArea, double value, Stroke stroke,
3546: Paint paint) {
3547:
3548: ValueAxis axis = getDomainAxis();
3549: if (getOrientation() == PlotOrientation.HORIZONTAL) {
3550: axis = getRangeAxis();
3551: }
3552: if (axis.getRange().contains(value)) {
3553: double xx = axis.valueToJava2D(value, dataArea,
3554: RectangleEdge.BOTTOM);
3555: Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3556: dataArea.getMaxY());
3557: g2.setStroke(stroke);
3558: g2.setPaint(paint);
3559: g2.draw(line);
3560: }
3561:
3562: }
3563:
3564: /**
3565: * Draws a range crosshair.
3566: *
3567: * @param g2 the graphics target.
3568: * @param dataArea the data area.
3569: * @param orientation the plot orientation.
3570: * @param value the crosshair value.
3571: * @param axis the axis against which the value is measured.
3572: * @param stroke the stroke used to draw the crosshair line.
3573: * @param paint the paint used to draw the crosshair line.
3574: *
3575: * @since 1.0.4
3576: */
3577: protected void drawRangeCrosshair(Graphics2D g2,
3578: Rectangle2D dataArea, PlotOrientation orientation,
3579: double value, ValueAxis axis, Stroke stroke, Paint paint) {
3580:
3581: if (axis.getRange().contains(value)) {
3582: Line2D line = null;
3583: if (orientation == PlotOrientation.HORIZONTAL) {
3584: double xx = axis.valueToJava2D(value, dataArea,
3585: RectangleEdge.BOTTOM);
3586: line = new Line2D.Double(xx, dataArea.getMinY(), xx,
3587: dataArea.getMaxY());
3588: } else {
3589: double yy = axis.valueToJava2D(value, dataArea,
3590: RectangleEdge.LEFT);
3591: line = new Line2D.Double(dataArea.getMinX(), yy,
3592: dataArea.getMaxX(), yy);
3593: }
3594: g2.setStroke(stroke);
3595: g2.setPaint(paint);
3596: g2.draw(line);
3597: }
3598:
3599: }
3600:
3601: /**
3602: * Handles a 'click' on the plot by updating the anchor values.
3603: *
3604: * @param x the x-coordinate, where the click occurred, in Java2D space.
3605: * @param y the y-coordinate, where the click occurred, in Java2D space.
3606: * @param info object containing information about the plot dimensions.
3607: */
3608: public void handleClick(int x, int y, PlotRenderingInfo info) {
3609:
3610: Rectangle2D dataArea = info.getDataArea();
3611: if (dataArea.contains(x, y)) {
3612: // set the anchor value for the horizontal axis...
3613: ValueAxis da = getDomainAxis();
3614: if (da != null) {
3615: double hvalue = da.java2DToValue(x, info.getDataArea(),
3616: getDomainAxisEdge());
3617: setDomainCrosshairValue(hvalue);
3618: }
3619:
3620: // set the anchor value for the vertical axis...
3621: ValueAxis ra = getRangeAxis();
3622: if (ra != null) {
3623: double vvalue = ra.java2DToValue(y, info.getDataArea(),
3624: getRangeAxisEdge());
3625: setRangeCrosshairValue(vvalue);
3626: }
3627: }
3628: }
3629:
3630: /**
3631: * A utility method that returns a list of datasets that are mapped to a
3632: * particular axis.
3633: *
3634: * @param axisIndex the axis index (<code>null</code> not permitted).
3635: *
3636: * @return A list of datasets.
3637: */
3638: private List getDatasetsMappedToDomainAxis(Integer axisIndex) {
3639: if (axisIndex == null) {
3640: throw new IllegalArgumentException(
3641: "Null 'axisIndex' argument.");
3642: }
3643: List result = new ArrayList();
3644: for (int i = 0; i < this .datasets.size(); i++) {
3645: Integer mappedAxis = (Integer) this .datasetToDomainAxisMap
3646: .get(new Integer(i));
3647: if (mappedAxis == null) {
3648: if (axisIndex.equals(ZERO)) {
3649: result.add(this .datasets.get(i));
3650: }
3651: } else {
3652: if (mappedAxis.equals(axisIndex)) {
3653: result.add(this .datasets.get(i));
3654: }
3655: }
3656: }
3657: return result;
3658: }
3659:
3660: /**
3661: * A utility method that returns a list of datasets that are mapped to a
3662: * particular axis.
3663: *
3664: * @param axisIndex the axis index (<code>null</code> not permitted).
3665: *
3666: * @return A list of datasets.
3667: */
3668: private List getDatasetsMappedToRangeAxis(Integer axisIndex) {
3669: if (axisIndex == null) {
3670: throw new IllegalArgumentException(
3671: "Null 'axisIndex' argument.");
3672: }
3673: List result = new ArrayList();
3674: for (int i = 0; i < this .datasets.size(); i++) {
3675: Integer mappedAxis = (Integer) this .datasetToRangeAxisMap
3676: .get(new Integer(i));
3677: if (mappedAxis == null) {
3678: if (axisIndex.equals(ZERO)) {
3679: result.add(this .datasets.get(i));
3680: }
3681: } else {
3682: if (mappedAxis.equals(axisIndex)) {
3683: result.add(this .datasets.get(i));
3684: }
3685: }
3686: }
3687: return result;
3688: }
3689:
3690: /**
3691: * Returns the index of the given domain axis.
3692: *
3693: * @param axis the axis.
3694: *
3695: * @return The axis index.
3696: *
3697: * @see #getRangeAxisIndex(ValueAxis)
3698: */
3699: public int getDomainAxisIndex(ValueAxis axis) {
3700: int result = this .domainAxes.indexOf(axis);
3701: if (result < 0) {
3702: // try the parent plot
3703: Plot parent = getParent();
3704: if (parent instanceof XYPlot) {
3705: XYPlot p = (XYPlot) parent;
3706: result = p.getDomainAxisIndex(axis);
3707: }
3708: }
3709: return result;
3710: }
3711:
3712: /**
3713: * Returns the index of the given range axis.
3714: *
3715: * @param axis the axis.
3716: *
3717: * @return The axis index.
3718: *
3719: * @see #getDomainAxisIndex(ValueAxis)
3720: */
3721: public int getRangeAxisIndex(ValueAxis axis) {
3722: int result = this .rangeAxes.indexOf(axis);
3723: if (result < 0) {
3724: // try the parent plot
3725: Plot parent = getParent();
3726: if (parent instanceof XYPlot) {
3727: XYPlot p = (XYPlot) parent;
3728: result = p.getRangeAxisIndex(axis);
3729: }
3730: }
3731: return result;
3732: }
3733:
3734: /**
3735: * Returns the range for the specified axis.
3736: *
3737: * @param axis the axis.
3738: *
3739: * @return The range.
3740: */
3741: public Range getDataRange(ValueAxis axis) {
3742:
3743: Range result = null;
3744: List mappedDatasets = new ArrayList();
3745: boolean isDomainAxis = true;
3746:
3747: // is it a domain axis?
3748: int domainIndex = getDomainAxisIndex(axis);
3749: if (domainIndex >= 0) {
3750: isDomainAxis = true;
3751: mappedDatasets
3752: .addAll(getDatasetsMappedToDomainAxis(new Integer(
3753: domainIndex)));
3754: }
3755:
3756: // or is it a range axis?
3757: int rangeIndex = getRangeAxisIndex(axis);
3758: if (rangeIndex >= 0) {
3759: isDomainAxis = false;
3760: mappedDatasets
3761: .addAll(getDatasetsMappedToRangeAxis(new Integer(
3762: rangeIndex)));
3763: }
3764:
3765: // iterate through the datasets that map to the axis and get the union
3766: // of the ranges.
3767: Iterator iterator = mappedDatasets.iterator();
3768: while (iterator.hasNext()) {
3769: XYDataset d = (XYDataset) iterator.next();
3770: if (d != null) {
3771: XYItemRenderer r = getRendererForDataset(d);
3772: if (isDomainAxis) {
3773: if (r != null) {
3774: result = Range.combine(result, r
3775: .findDomainBounds(d));
3776: } else {
3777: result = Range.combine(result, DatasetUtilities
3778: .findDomainBounds(d));
3779: }
3780: } else {
3781: if (r != null) {
3782: result = Range.combine(result, r
3783: .findRangeBounds(d));
3784: } else {
3785: result = Range.combine(result, DatasetUtilities
3786: .findRangeBounds(d));
3787: }
3788: }
3789: }
3790: }
3791: return result;
3792:
3793: }
3794:
3795: /**
3796: * Receives notification of a change to the plot's dataset.
3797: * <P>
3798: * The axis ranges are updated if necessary.
3799: *
3800: * @param event information about the event (not used here).
3801: */
3802: public void datasetChanged(DatasetChangeEvent event) {
3803: configureDomainAxes();
3804: configureRangeAxes();
3805: if (getParent() != null) {
3806: getParent().datasetChanged(event);
3807: } else {
3808: PlotChangeEvent e = new PlotChangeEvent(this );
3809: e.setType(ChartChangeEventType.DATASET_UPDATED);
3810: notifyListeners(e);
3811: }
3812: }
3813:
3814: /**
3815: * Receives notification of a renderer change event.
3816: *
3817: * @param event the event.
3818: */
3819: public void rendererChanged(RendererChangeEvent event) {
3820: notifyListeners(new PlotChangeEvent(this ));
3821: }
3822:
3823: /**
3824: * Returns a flag indicating whether or not the domain crosshair is visible.
3825: *
3826: * @return The flag.
3827: *
3828: * @see #setDomainCrosshairVisible(boolean)
3829: */
3830: public boolean isDomainCrosshairVisible() {
3831: return this .domainCrosshairVisible;
3832: }
3833:
3834: /**
3835: * Sets the flag indicating whether or not the domain crosshair is visible
3836: * and, if the flag changes, sends a {@link PlotChangeEvent} to all
3837: * registered listeners.
3838: *
3839: * @param flag the new value of the flag.
3840: *
3841: * @see #isDomainCrosshairVisible()
3842: */
3843: public void setDomainCrosshairVisible(boolean flag) {
3844: if (this .domainCrosshairVisible != flag) {
3845: this .domainCrosshairVisible = flag;
3846: notifyListeners(new PlotChangeEvent(this ));
3847: }
3848: }
3849:
3850: /**
3851: * Returns a flag indicating whether or not the crosshair should "lock-on"
3852: * to actual data values.
3853: *
3854: * @return The flag.
3855: *
3856: * @see #setDomainCrosshairLockedOnData(boolean)
3857: */
3858: public boolean isDomainCrosshairLockedOnData() {
3859: return this .domainCrosshairLockedOnData;
3860: }
3861:
3862: /**
3863: * Sets the flag indicating whether or not the domain crosshair should
3864: * "lock-on" to actual data values. If the flag value changes, this
3865: * method sends a {@link PlotChangeEvent} to all registered listeners.
3866: *
3867: * @param flag the flag.
3868: *
3869: * @see #isDomainCrosshairLockedOnData()
3870: */
3871: public void setDomainCrosshairLockedOnData(boolean flag) {
3872: if (this .domainCrosshairLockedOnData != flag) {
3873: this .domainCrosshairLockedOnData = flag;
3874: notifyListeners(new PlotChangeEvent(this ));
3875: }
3876: }
3877:
3878: /**
3879: * Returns the domain crosshair value.
3880: *
3881: * @return The value.
3882: *
3883: * @see #setDomainCrosshairValue(double)
3884: */
3885: public double getDomainCrosshairValue() {
3886: return this .domainCrosshairValue;
3887: }
3888:
3889: /**
3890: * Sets the domain crosshair value and sends a {@link PlotChangeEvent} to
3891: * all registered listeners (provided that the domain crosshair is visible).
3892: *
3893: * @param value the value.
3894: *
3895: * @see #getDomainCrosshairValue()
3896: */
3897: public void setDomainCrosshairValue(double value) {
3898: setDomainCrosshairValue(value, true);
3899: }
3900:
3901: /**
3902: * Sets the domain crosshair value and, if requested, sends a
3903: * {@link PlotChangeEvent} to all registered listeners (provided that the
3904: * domain crosshair is visible).
3905: *
3906: * @param value the new value.
3907: * @param notify notify listeners?
3908: *
3909: * @see #getDomainCrosshairValue()
3910: */
3911: public void setDomainCrosshairValue(double value, boolean notify) {
3912: this .domainCrosshairValue = value;
3913: if (isDomainCrosshairVisible() && notify) {
3914: notifyListeners(new PlotChangeEvent(this ));
3915: }
3916: }
3917:
3918: /**
3919: * Returns the {@link Stroke} used to draw the crosshair (if visible).
3920: *
3921: * @return The crosshair stroke (never <code>null</code>).
3922: *
3923: * @see #setDomainCrosshairStroke(Stroke)
3924: * @see #isDomainCrosshairVisible()
3925: * @see #getDomainCrosshairPaint()
3926: */
3927: public Stroke getDomainCrosshairStroke() {
3928: return this .domainCrosshairStroke;
3929: }
3930:
3931: /**
3932: * Sets the Stroke used to draw the crosshairs (if visible) and notifies
3933: * registered listeners that the axis has been modified.
3934: *
3935: * @param stroke the new crosshair stroke (<code>null</code> not
3936: * permitted).
3937: *
3938: * @see #getDomainCrosshairStroke()
3939: */
3940: public void setDomainCrosshairStroke(Stroke stroke) {
3941: if (stroke == null) {
3942: throw new IllegalArgumentException(
3943: "Null 'stroke' argument.");
3944: }
3945: this .domainCrosshairStroke = stroke;
3946: notifyListeners(new PlotChangeEvent(this ));
3947: }
3948:
3949: /**
3950: * Returns the domain crosshair paint.
3951: *
3952: * @return The crosshair paint (never <code>null</code>).
3953: *
3954: * @see #setDomainCrosshairPaint(Paint)
3955: * @see #isDomainCrosshairVisible()
3956: * @see #getDomainCrosshairStroke()
3957: */
3958: public Paint getDomainCrosshairPaint() {
3959: return this .domainCrosshairPaint;
3960: }
3961:
3962: /**
3963: * Sets the paint used to draw the crosshairs (if visible) and sends a
3964: * {@link PlotChangeEvent} to all registered listeners.
3965: *
3966: * @param paint the new crosshair paint (<code>null</code> not permitted).
3967: *
3968: * @see #getDomainCrosshairPaint()
3969: */
3970: public void setDomainCrosshairPaint(Paint paint) {
3971: if (paint == null) {
3972: throw new IllegalArgumentException("Null 'paint' argument.");
3973: }
3974: this .domainCrosshairPaint = paint;
3975: notifyListeners(new PlotChangeEvent(this ));
3976: }
3977:
3978: /**
3979: * Returns a flag indicating whether or not the range crosshair is visible.
3980: *
3981: * @return The flag.
3982: *
3983: * @see #setRangeCrosshairVisible(boolean)
3984: * @see #isDomainCrosshairVisible()
3985: */
3986: public boolean isRangeCrosshairVisible() {
3987: return this .rangeCrosshairVisible;
3988: }
3989:
3990: /**
3991: * Sets the flag indicating whether or not the range crosshair is visible.
3992: * If the flag value changes, this method sends a {@link PlotChangeEvent}
3993: * to all registered listeners.
3994: *
3995: * @param flag the new value of the flag.
3996: *
3997: * @see #isRangeCrosshairVisible()
3998: */
3999: public void setRangeCrosshairVisible(boolean flag) {
4000: if (this .rangeCrosshairVisible != flag) {
4001: this .rangeCrosshairVisible = flag;
4002: notifyListeners(new PlotChangeEvent(this ));
4003: }
4004: }
4005:
4006: /**
4007: * Returns a flag indicating whether or not the crosshair should "lock-on"
4008: * to actual data values.
4009: *
4010: * @return The flag.
4011: *
4012: * @see #setRangeCrosshairLockedOnData(boolean)
4013: */
4014: public boolean isRangeCrosshairLockedOnData() {
4015: return this .rangeCrosshairLockedOnData;
4016: }
4017:
4018: /**
4019: * Sets the flag indicating whether or not the range crosshair should
4020: * "lock-on" to actual data values. If the flag value changes, this method
4021: * sends a {@link PlotChangeEvent} to all registered listeners.
4022: *
4023: * @param flag the flag.
4024: *
4025: * @see #isRangeCrosshairLockedOnData()
4026: */
4027: public void setRangeCrosshairLockedOnData(boolean flag) {
4028: if (this .rangeCrosshairLockedOnData != flag) {
4029: this .rangeCrosshairLockedOnData = flag;
4030: notifyListeners(new PlotChangeEvent(this ));
4031: }
4032: }
4033:
4034: /**
4035: * Returns the range crosshair value.
4036: *
4037: * @return The value.
4038: *
4039: * @see #setRangeCrosshairValue(double)
4040: */
4041: public double getRangeCrosshairValue() {
4042: return this .rangeCrosshairValue;
4043: }
4044:
4045: /**
4046: * Sets the range crosshair value.
4047: * <P>
4048: * Registered listeners are notified that the plot has been modified, but
4049: * only if the crosshair is visible.
4050: *
4051: * @param value the new value.
4052: *
4053: * @see #getRangeCrosshairValue()
4054: */
4055: public void setRangeCrosshairValue(double value) {
4056: setRangeCrosshairValue(value, true);
4057: }
4058:
4059: /**
4060: * Sets the range crosshair value and sends a {@link PlotChangeEvent} to
4061: * all registered listeners, but only if the crosshair is visible.
4062: *
4063: * @param value the new value.
4064: * @param notify a flag that controls whether or not listeners are
4065: * notified.
4066: *
4067: * @see #getRangeCrosshairValue()
4068: */
4069: public void setRangeCrosshairValue(double value, boolean notify) {
4070: this .rangeCrosshairValue = value;
4071: if (isRangeCrosshairVisible() && notify) {
4072: notifyListeners(new PlotChangeEvent(this ));
4073: }
4074: }
4075:
4076: /**
4077: * Returns the stroke used to draw the crosshair (if visible).
4078: *
4079: * @return The crosshair stroke (never <code>null</code>).
4080: *
4081: * @see #setRangeCrosshairStroke(Stroke)
4082: * @see #isRangeCrosshairVisible()
4083: * @see #getRangeCrosshairPaint()
4084: */
4085: public Stroke getRangeCrosshairStroke() {
4086: return this .rangeCrosshairStroke;
4087: }
4088:
4089: /**
4090: * Sets the stroke used to draw the crosshairs (if visible) and sends a
4091: * {@link PlotChangeEvent} to all registered listeners.
4092: *
4093: * @param stroke the new crosshair stroke (<code>null</code> not
4094: * permitted).
4095: *
4096: * @see #getRangeCrosshairStroke()
4097: */
4098: public void setRangeCrosshairStroke(Stroke stroke) {
4099: if (stroke == null) {
4100: throw new IllegalArgumentException(
4101: "Null 'stroke' argument.");
4102: }
4103: this .rangeCrosshairStroke = stroke;
4104: notifyListeners(new PlotChangeEvent(this ));
4105: }
4106:
4107: /**
4108: * Returns the range crosshair paint.
4109: *
4110: * @return The crosshair paint (never <code>null</code>).
4111: *
4112: * @see #setRangeCrosshairPaint(Paint)
4113: * @see #isRangeCrosshairVisible()
4114: * @see #getRangeCrosshairStroke()
4115: */
4116: public Paint getRangeCrosshairPaint() {
4117: return this .rangeCrosshairPaint;
4118: }
4119:
4120: /**
4121: * Sets the paint used to color the crosshairs (if visible) and sends a
4122: * {@link PlotChangeEvent} to all registered listeners.
4123: *
4124: * @param paint the new crosshair paint (<code>null</code> not permitted).
4125: *
4126: * @see #getRangeCrosshairPaint()
4127: */
4128: public void setRangeCrosshairPaint(Paint paint) {
4129: if (paint == null) {
4130: throw new IllegalArgumentException("Null 'paint' argument.");
4131: }
4132: this .rangeCrosshairPaint = paint;
4133: notifyListeners(new PlotChangeEvent(this ));
4134: }
4135:
4136: /**
4137: * Returns the fixed domain axis space.
4138: *
4139: * @return The fixed domain axis space (possibly <code>null</code>).
4140: *
4141: * @see #setFixedDomainAxisSpace(AxisSpace)
4142: */
4143: public AxisSpace getFixedDomainAxisSpace() {
4144: return this .fixedDomainAxisSpace;
4145: }
4146:
4147: /**
4148: * Sets the fixed domain axis space.
4149: *
4150: * @param space the space (<code>null</code> permitted).
4151: *
4152: * @see #getFixedDomainAxisSpace()
4153: */
4154: public void setFixedDomainAxisSpace(AxisSpace space) {
4155: this .fixedDomainAxisSpace = space;
4156: // TODO: notify listeners?
4157: }
4158:
4159: /**
4160: * Returns the fixed range axis space.
4161: *
4162: * @return The fixed range axis space (possibly <code>null</code>).
4163: *
4164: * @see #setFixedRangeAxisSpace(AxisSpace)
4165: */
4166: public AxisSpace getFixedRangeAxisSpace() {
4167: return this .fixedRangeAxisSpace;
4168: }
4169:
4170: /**
4171: * Sets the fixed range axis space.
4172: *
4173: * @param space the space (<code>null</code> permitted).
4174: *
4175: * @see #getFixedRangeAxisSpace()
4176: */
4177: public void setFixedRangeAxisSpace(AxisSpace space) {
4178: this .fixedRangeAxisSpace = space;
4179: // TODO: notify listeners?
4180: }
4181:
4182: /**
4183: * Multiplies the range on the domain axis/axes by the specified factor.
4184: *
4185: * @param factor the zoom factor.
4186: * @param info the plot rendering info.
4187: * @param source the source point.
4188: */
4189: public void zoomDomainAxes(double factor, PlotRenderingInfo info,
4190: Point2D source) {
4191: for (int i = 0; i < this .domainAxes.size(); i++) {
4192: ValueAxis domainAxis = (ValueAxis) this .domainAxes.get(i);
4193: if (domainAxis != null) {
4194: domainAxis.resizeRange(factor);
4195: }
4196: }
4197: }
4198:
4199: /**
4200: * Zooms in on the domain axis/axes. The new lower and upper bounds are
4201: * specified as percentages of the current axis range, where 0 percent is
4202: * the current lower bound and 100 percent is the current upper bound.
4203: *
4204: * @param lowerPercent a percentage that determines the new lower bound
4205: * for the axis (e.g. 0.20 is twenty percent).
4206: * @param upperPercent a percentage that determines the new upper bound
4207: * for the axis (e.g. 0.80 is eighty percent).
4208: * @param info the plot rendering info.
4209: * @param source the source point.
4210: */
4211: public void zoomDomainAxes(double lowerPercent,
4212: double upperPercent, PlotRenderingInfo info, Point2D source) {
4213: for (int i = 0; i < this .domainAxes.size(); i++) {
4214: ValueAxis domainAxis = (ValueAxis) this .domainAxes.get(i);
4215: if (domainAxis != null) {
4216: domainAxis.zoomRange(lowerPercent, upperPercent);
4217: }
4218: }
4219: }
4220:
4221: /**
4222: * Multiplies the range on the range axis/axes by the specified factor.
4223: *
4224: * @param factor the zoom factor.
4225: * @param info the plot rendering info.
4226: * @param source the source point.
4227: */
4228: public void zoomRangeAxes(double factor, PlotRenderingInfo info,
4229: Point2D source) {
4230: for (int i = 0; i < this .rangeAxes.size(); i++) {
4231: ValueAxis rangeAxis = (ValueAxis) this .rangeAxes.get(i);
4232: if (rangeAxis != null) {
4233: rangeAxis.resizeRange(factor);
4234: }
4235: }
4236: }
4237:
4238: /**
4239: * Zooms in on the range axes.
4240: *
4241: * @param lowerPercent the lower bound.
4242: * @param upperPercent the upper bound.
4243: * @param info the plot rendering info.
4244: * @param source the source point.
4245: */
4246: public void zoomRangeAxes(double lowerPercent, double upperPercent,
4247: PlotRenderingInfo info, Point2D source) {
4248: for (int i = 0; i < this .rangeAxes.size(); i++) {
4249: ValueAxis rangeAxis = (ValueAxis) this .rangeAxes.get(i);
4250: if (rangeAxis != null) {
4251: rangeAxis.zoomRange(lowerPercent, upperPercent);
4252: }
4253: }
4254: }
4255:
4256: /**
4257: * Returns <code>true</code>, indicating that the domain axis/axes for this
4258: * plot are zoomable.
4259: *
4260: * @return A boolean.
4261: *
4262: * @see #isRangeZoomable()
4263: */
4264: public boolean isDomainZoomable() {
4265: return true;
4266: }
4267:
4268: /**
4269: * Returns <code>true</code>, indicating that the range axis/axes for this
4270: * plot are zoomable.
4271: *
4272: * @return A boolean.
4273: *
4274: * @see #isDomainZoomable()
4275: */
4276: public boolean isRangeZoomable() {
4277: return true;
4278: }
4279:
4280: /**
4281: * Returns the number of series in the primary dataset for this plot. If
4282: * the dataset is <code>null</code>, the method returns 0.
4283: *
4284: * @return The series count.
4285: */
4286: public int getSeriesCount() {
4287: int result = 0;
4288: XYDataset dataset = getDataset();
4289: if (dataset != null) {
4290: result = dataset.getSeriesCount();
4291: }
4292: return result;
4293: }
4294:
4295: /**
4296: * Returns the fixed legend items, if any.
4297: *
4298: * @return The legend items (possibly <code>null</code>).
4299: *
4300: * @see #setFixedLegendItems(LegendItemCollection)
4301: */
4302: public LegendItemCollection getFixedLegendItems() {
4303: return this .fixedLegendItems;
4304: }
4305:
4306: /**
4307: * Sets the fixed legend items for the plot. Leave this set to
4308: * <code>null</code> if you prefer the legend items to be created
4309: * automatically.
4310: *
4311: * @param items the legend items (<code>null</code> permitted).
4312: *
4313: * @see #getFixedLegendItems()
4314: */
4315: public void setFixedLegendItems(LegendItemCollection items) {
4316: this .fixedLegendItems = items;
4317: notifyListeners(new PlotChangeEvent(this ));
4318: }
4319:
4320: /**
4321: * Returns the legend items for the plot. Each legend item is generated by
4322: * the plot's renderer, since the renderer is responsible for the visual
4323: * representation of the data.
4324: *
4325: * @return The legend items.
4326: */
4327: public LegendItemCollection getLegendItems() {
4328: if (this .fixedLegendItems != null) {
4329: return this .fixedLegendItems;
4330: }
4331: LegendItemCollection result = new LegendItemCollection();
4332: int count = this .datasets.size();
4333: for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) {
4334: XYDataset dataset = getDataset(datasetIndex);
4335: if (dataset != null) {
4336: XYItemRenderer renderer = getRenderer(datasetIndex);
4337: if (renderer == null) {
4338: renderer = getRenderer(0);
4339: }
4340: if (renderer != null) {
4341: int seriesCount = dataset.getSeriesCount();
4342: for (int i = 0; i < seriesCount; i++) {
4343: if (renderer.isSeriesVisible(i)
4344: && renderer.isSeriesVisibleInLegend(i)) {
4345: LegendItem item = renderer.getLegendItem(
4346: datasetIndex, i);
4347: if (item != null) {
4348: result.add(item);
4349: }
4350: }
4351: }
4352: }
4353: }
4354: }
4355: return result;
4356: }
4357:
4358: /**
4359: * Tests this plot for equality with another object.
4360: *
4361: * @param obj the object (<code>null</code> permitted).
4362: *
4363: * @return <code>true</code> or <code>false</code>.
4364: */
4365: public boolean equals(Object obj) {
4366:
4367: if (obj == this ) {
4368: return true;
4369: }
4370: if (!(obj instanceof XYPlot)) {
4371: return false;
4372: }
4373: if (!super .equals(obj)) {
4374: return false;
4375: }
4376:
4377: XYPlot that = (XYPlot) obj;
4378: if (this .weight != that.weight) {
4379: return false;
4380: }
4381: if (this .orientation != that.orientation) {
4382: return false;
4383: }
4384: if (!this .domainAxes.equals(that.domainAxes)) {
4385: return false;
4386: }
4387: if (!this .domainAxisLocations.equals(that.domainAxisLocations)) {
4388: return false;
4389: }
4390: if (this .rangeCrosshairLockedOnData != that.rangeCrosshairLockedOnData) {
4391: return false;
4392: }
4393: if (this .domainGridlinesVisible != that.domainGridlinesVisible) {
4394: return false;
4395: }
4396: if (this .rangeGridlinesVisible != that.rangeGridlinesVisible) {
4397: return false;
4398: }
4399: if (this .domainZeroBaselineVisible != that.domainZeroBaselineVisible) {
4400: return false;
4401: }
4402: if (this .rangeZeroBaselineVisible != that.rangeZeroBaselineVisible) {
4403: return false;
4404: }
4405: if (this .domainCrosshairVisible != that.domainCrosshairVisible) {
4406: return false;
4407: }
4408: if (this .domainCrosshairValue != that.domainCrosshairValue) {
4409: return false;
4410: }
4411: if (this .domainCrosshairLockedOnData != that.domainCrosshairLockedOnData) {
4412: return false;
4413: }
4414: if (this .rangeCrosshairVisible != that.rangeCrosshairVisible) {
4415: return false;
4416: }
4417: if (this .rangeCrosshairValue != that.rangeCrosshairValue) {
4418: return false;
4419: }
4420: if (!ObjectUtilities.equal(this .axisOffset, that.axisOffset)) {
4421: return false;
4422: }
4423: if (!ObjectUtilities.equal(this .renderers, that.renderers)) {
4424: return false;
4425: }
4426: if (!ObjectUtilities.equal(this .rangeAxes, that.rangeAxes)) {
4427: return false;
4428: }
4429: if (!this .rangeAxisLocations.equals(that.rangeAxisLocations)) {
4430: return false;
4431: }
4432: if (!ObjectUtilities.equal(this .datasetToDomainAxisMap,
4433: that.datasetToDomainAxisMap)) {
4434: return false;
4435: }
4436: if (!ObjectUtilities.equal(this .datasetToRangeAxisMap,
4437: that.datasetToRangeAxisMap)) {
4438: return false;
4439: }
4440: if (!ObjectUtilities.equal(this .domainGridlineStroke,
4441: that.domainGridlineStroke)) {
4442: return false;
4443: }
4444: if (!PaintUtilities.equal(this .domainGridlinePaint,
4445: that.domainGridlinePaint)) {
4446: return false;
4447: }
4448: if (!ObjectUtilities.equal(this .rangeGridlineStroke,
4449: that.rangeGridlineStroke)) {
4450: return false;
4451: }
4452: if (!PaintUtilities.equal(this .rangeGridlinePaint,
4453: that.rangeGridlinePaint)) {
4454: return false;
4455: }
4456: if (!PaintUtilities.equal(this .domainZeroBaselinePaint,
4457: that.domainZeroBaselinePaint)) {
4458: return false;
4459: }
4460: if (!ObjectUtilities.equal(this .domainZeroBaselineStroke,
4461: that.domainZeroBaselineStroke)) {
4462: return false;
4463: }
4464: if (!PaintUtilities.equal(this .rangeZeroBaselinePaint,
4465: that.rangeZeroBaselinePaint)) {
4466: return false;
4467: }
4468: if (!ObjectUtilities.equal(this .rangeZeroBaselineStroke,
4469: that.rangeZeroBaselineStroke)) {
4470: return false;
4471: }
4472: if (!ObjectUtilities.equal(this .domainCrosshairStroke,
4473: that.domainCrosshairStroke)) {
4474: return false;
4475: }
4476: if (!PaintUtilities.equal(this .domainCrosshairPaint,
4477: that.domainCrosshairPaint)) {
4478: return false;
4479: }
4480: if (!ObjectUtilities.equal(this .rangeCrosshairStroke,
4481: that.rangeCrosshairStroke)) {
4482: return false;
4483: }
4484: if (!PaintUtilities.equal(this .rangeCrosshairPaint,
4485: that.rangeCrosshairPaint)) {
4486: return false;
4487: }
4488: if (!ObjectUtilities.equal(this .foregroundDomainMarkers,
4489: that.foregroundDomainMarkers)) {
4490: return false;
4491: }
4492: if (!ObjectUtilities.equal(this .backgroundDomainMarkers,
4493: that.backgroundDomainMarkers)) {
4494: return false;
4495: }
4496: if (!ObjectUtilities.equal(this .foregroundRangeMarkers,
4497: that.foregroundRangeMarkers)) {
4498: return false;
4499: }
4500: if (!ObjectUtilities.equal(this .backgroundRangeMarkers,
4501: that.backgroundRangeMarkers)) {
4502: return false;
4503: }
4504: if (!ObjectUtilities.equal(this .foregroundDomainMarkers,
4505: that.foregroundDomainMarkers)) {
4506: return false;
4507: }
4508: if (!ObjectUtilities.equal(this .backgroundDomainMarkers,
4509: that.backgroundDomainMarkers)) {
4510: return false;
4511: }
4512: if (!ObjectUtilities.equal(this .foregroundRangeMarkers,
4513: that.foregroundRangeMarkers)) {
4514: return false;
4515: }
4516: if (!ObjectUtilities.equal(this .backgroundRangeMarkers,
4517: that.backgroundRangeMarkers)) {
4518: return false;
4519: }
4520: if (!ObjectUtilities.equal(this .annotations, that.annotations)) {
4521: return false;
4522: }
4523: if (!this .quadrantOrigin.equals(that.quadrantOrigin)) {
4524: return false;
4525: }
4526: for (int i = 0; i < 4; i++) {
4527: if (!PaintUtilities.equal(this .quadrantPaint[i],
4528: that.quadrantPaint[i])) {
4529: return false;
4530: }
4531: }
4532: return true;
4533: }
4534:
4535: /**
4536: * Returns a clone of the plot.
4537: *
4538: * @return A clone.
4539: *
4540: * @throws CloneNotSupportedException this can occur if some component of
4541: * the plot cannot be cloned.
4542: */
4543: public Object clone() throws CloneNotSupportedException {
4544:
4545: XYPlot clone = (XYPlot) super .clone();
4546: clone.domainAxes = (ObjectList) ObjectUtilities
4547: .clone(this .domainAxes);
4548: for (int i = 0; i < this .domainAxes.size(); i++) {
4549: ValueAxis axis = (ValueAxis) this .domainAxes.get(i);
4550: if (axis != null) {
4551: ValueAxis clonedAxis = (ValueAxis) axis.clone();
4552: clone.domainAxes.set(i, clonedAxis);
4553: clonedAxis.setPlot(clone);
4554: clonedAxis.addChangeListener(clone);
4555: }
4556: }
4557: clone.domainAxisLocations = (ObjectList) this .domainAxisLocations
4558: .clone();
4559:
4560: clone.rangeAxes = (ObjectList) ObjectUtilities
4561: .clone(this .rangeAxes);
4562: for (int i = 0; i < this .rangeAxes.size(); i++) {
4563: ValueAxis axis = (ValueAxis) this .rangeAxes.get(i);
4564: if (axis != null) {
4565: ValueAxis clonedAxis = (ValueAxis) axis.clone();
4566: clone.rangeAxes.set(i, clonedAxis);
4567: clonedAxis.setPlot(clone);
4568: clonedAxis.addChangeListener(clone);
4569: }
4570: }
4571: clone.rangeAxisLocations = (ObjectList) ObjectUtilities
4572: .clone(this .rangeAxisLocations);
4573:
4574: // the datasets are not cloned, but listeners need to be added...
4575: clone.datasets = (ObjectList) ObjectUtilities
4576: .clone(this .datasets);
4577: for (int i = 0; i < clone.datasets.size(); ++i) {
4578: XYDataset d = getDataset(i);
4579: if (d != null) {
4580: d.addChangeListener(clone);
4581: }
4582: }
4583:
4584: clone.datasetToDomainAxisMap = new TreeMap();
4585: clone.datasetToDomainAxisMap
4586: .putAll(this .datasetToDomainAxisMap);
4587: clone.datasetToRangeAxisMap = new TreeMap();
4588: clone.datasetToRangeAxisMap.putAll(this .datasetToRangeAxisMap);
4589:
4590: clone.renderers = (ObjectList) ObjectUtilities
4591: .clone(this .renderers);
4592: for (int i = 0; i < this .renderers.size(); i++) {
4593: XYItemRenderer renderer2 = (XYItemRenderer) this .renderers
4594: .get(i);
4595: if (renderer2 instanceof PublicCloneable) {
4596: PublicCloneable pc = (PublicCloneable) renderer2;
4597: clone.renderers.set(i, pc.clone());
4598: }
4599: }
4600: clone.foregroundDomainMarkers = (Map) ObjectUtilities
4601: .clone(this .foregroundDomainMarkers);
4602: clone.backgroundDomainMarkers = (Map) ObjectUtilities
4603: .clone(this .backgroundDomainMarkers);
4604: clone.foregroundRangeMarkers = (Map) ObjectUtilities
4605: .clone(this .foregroundRangeMarkers);
4606: clone.backgroundRangeMarkers = (Map) ObjectUtilities
4607: .clone(this .backgroundRangeMarkers);
4608: clone.annotations = (List) ObjectUtilities
4609: .deepClone(this .annotations);
4610: if (this .fixedDomainAxisSpace != null) {
4611: clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtilities
4612: .clone(this .fixedDomainAxisSpace);
4613: }
4614: if (this .fixedRangeAxisSpace != null) {
4615: clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtilities
4616: .clone(this .fixedRangeAxisSpace);
4617: }
4618:
4619: clone.quadrantOrigin = (Point2D) ObjectUtilities
4620: .clone(this .quadrantOrigin);
4621: clone.quadrantPaint = (Paint[]) this .quadrantPaint.clone();
4622: return clone;
4623:
4624: }
4625:
4626: /**
4627: * Provides serialization support.
4628: *
4629: * @param stream the output stream.
4630: *
4631: * @throws IOException if there is an I/O error.
4632: */
4633: private void writeObject(ObjectOutputStream stream)
4634: throws IOException {
4635: stream.defaultWriteObject();
4636: SerialUtilities.writeStroke(this .domainGridlineStroke, stream);
4637: SerialUtilities.writePaint(this .domainGridlinePaint, stream);
4638: SerialUtilities.writeStroke(this .rangeGridlineStroke, stream);
4639: SerialUtilities.writePaint(this .rangeGridlinePaint, stream);
4640: SerialUtilities.writeStroke(this .rangeZeroBaselineStroke,
4641: stream);
4642: SerialUtilities.writePaint(this .rangeZeroBaselinePaint, stream);
4643: SerialUtilities.writeStroke(this .domainCrosshairStroke, stream);
4644: SerialUtilities.writePaint(this .domainCrosshairPaint, stream);
4645: SerialUtilities.writeStroke(this .rangeCrosshairStroke, stream);
4646: SerialUtilities.writePaint(this .rangeCrosshairPaint, stream);
4647: SerialUtilities.writePaint(this .domainTickBandPaint, stream);
4648: SerialUtilities.writePaint(this .rangeTickBandPaint, stream);
4649: SerialUtilities.writePoint2D(this .quadrantOrigin, stream);
4650: for (int i = 0; i < 4; i++) {
4651: SerialUtilities.writePaint(this .quadrantPaint[i], stream);
4652: }
4653: SerialUtilities.writeStroke(this .domainZeroBaselineStroke,
4654: stream);
4655: SerialUtilities
4656: .writePaint(this .domainZeroBaselinePaint, stream);
4657: }
4658:
4659: /**
4660: * Provides serialization support.
4661: *
4662: * @param stream the input stream.
4663: *
4664: * @throws IOException if there is an I/O error.
4665: * @throws ClassNotFoundException if there is a classpath problem.
4666: */
4667: private void readObject(ObjectInputStream stream)
4668: throws IOException, ClassNotFoundException {
4669:
4670: stream.defaultReadObject();
4671: this .domainGridlineStroke = SerialUtilities.readStroke(stream);
4672: this .domainGridlinePaint = SerialUtilities.readPaint(stream);
4673: this .rangeGridlineStroke = SerialUtilities.readStroke(stream);
4674: this .rangeGridlinePaint = SerialUtilities.readPaint(stream);
4675: this .rangeZeroBaselineStroke = SerialUtilities
4676: .readStroke(stream);
4677: this .rangeZeroBaselinePaint = SerialUtilities.readPaint(stream);
4678: this .domainCrosshairStroke = SerialUtilities.readStroke(stream);
4679: this .domainCrosshairPaint = SerialUtilities.readPaint(stream);
4680: this .rangeCrosshairStroke = SerialUtilities.readStroke(stream);
4681: this .rangeCrosshairPaint = SerialUtilities.readPaint(stream);
4682: this .domainTickBandPaint = SerialUtilities.readPaint(stream);
4683: this .rangeTickBandPaint = SerialUtilities.readPaint(stream);
4684: this .quadrantOrigin = SerialUtilities.readPoint2D(stream);
4685: this .quadrantPaint = new Paint[4];
4686: for (int i = 0; i < 4; i++) {
4687: this .quadrantPaint[i] = SerialUtilities.readPaint(stream);
4688: }
4689:
4690: this .domainZeroBaselineStroke = SerialUtilities
4691: .readStroke(stream);
4692: this .domainZeroBaselinePaint = SerialUtilities
4693: .readPaint(stream);
4694:
4695: // register the plot as a listener with its axes, datasets, and
4696: // renderers...
4697: int domainAxisCount = this .domainAxes.size();
4698: for (int i = 0; i < domainAxisCount; i++) {
4699: Axis axis = (Axis) this .domainAxes.get(i);
4700: if (axis != null) {
4701: axis.setPlot(this );
4702: axis.addChangeListener(this );
4703: }
4704: }
4705: int rangeAxisCount = this .rangeAxes.size();
4706: for (int i = 0; i < rangeAxisCount; i++) {
4707: Axis axis = (Axis) this .rangeAxes.get(i);
4708: if (axis != null) {
4709: axis.setPlot(this );
4710: axis.addChangeListener(this );
4711: }
4712: }
4713: int datasetCount = this .datasets.size();
4714: for (int i = 0; i < datasetCount; i++) {
4715: Dataset dataset = (Dataset) this .datasets.get(i);
4716: if (dataset != null) {
4717: dataset.addChangeListener(this );
4718: }
4719: }
4720: int rendererCount = this .renderers.size();
4721: for (int i = 0; i < rendererCount; i++) {
4722: XYItemRenderer renderer = (XYItemRenderer) this.renderers
4723: .get(i);
4724: if (renderer != null) {
4725: renderer.addChangeListener(this);
4726: }
4727: }
4728:
4729: }
4730:
4731: }
|