1 /* ===========================================================
2 * JFreeChart : a free chart library for the Java(tm) platform
3 * ===========================================================
4 *
5 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
6 *
7 * Project Info: http://www.jfree.org/jfreechart/index.html
8 *
9 * This library is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17 * License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 * USA.
23 *
24 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25 * in the United States and other countries.]
26 *
27 * -----------
28 * XYPlot.java
29 * -----------
30 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
31 *
32 * Original Author: David Gilbert (for Object Refinery Limited);
33 * Contributor(s): Craig MacFarlane;
34 * Mark Watson (www.markwatson.com);
35 * Jonathan Nash;
36 * Gideon Krause;
37 * Klaus Rheinwald;
38 * Xavier Poinsard;
39 * Richard Atkinson;
40 * Arnaud Lelievre;
41 * Nicolas Brodu;
42 * Eduardo Ramalho;
43 * Sergei Ivanov;
44 * Richard West, Advanced Micro Devices, Inc.;
45 *
46 * Changes (from 21-Jun-2001)
47 * --------------------------
48 * 21-Jun-2001 : Removed redundant JFreeChart parameter from constructors (DG);
49 * 18-Sep-2001 : Updated header and fixed DOS encoding problem (DG);
50 * 15-Oct-2001 : Data source classes moved to com.jrefinery.data.* (DG);
51 * 19-Oct-2001 : Removed the code for drawing the visual representation of each
52 * data point into a separate class StandardXYItemRenderer.
53 * This will make it easier to add variations to the way the
54 * charts are drawn. Based on code contributed by Mark
55 * Watson (DG);
56 * 22-Oct-2001 : Renamed DataSource.java --> Dataset.java etc. (DG);
57 * 20-Nov-2001 : Fixed clipping bug that shows up when chart is displayed
58 * inside JScrollPane (DG);
59 * 12-Dec-2001 : Removed unnecessary 'throws' clauses from constructor (DG);
60 * 13-Dec-2001 : Added skeleton code for tooltips. Added new constructor. (DG);
61 * 16-Jan-2002 : Renamed the tooltips class (DG);
62 * 22-Jan-2002 : Added DrawInfo class, incorporating tooltips and crosshairs.
63 * Crosshairs based on code by Jonathan Nash (DG);
64 * 05-Feb-2002 : Added alpha-transparency setting based on code by Sylvain
65 * Vieujot (DG);
66 * 26-Feb-2002 : Updated getMinimumXXX() and getMaximumXXX() methods to handle
67 * special case when chart is null (DG);
68 * 28-Feb-2002 : Renamed Datasets.java --> DatasetUtilities.java (DG);
69 * 28-Mar-2002 : The plot now registers with the renderer as a property change
70 * listener. Also added a new constructor (DG);
71 * 09-Apr-2002 : Removed the transRangeZero from the renderer.drawItem()
72 * method. Moved the tooltip generator into the renderer (DG);
73 * 23-Apr-2002 : Fixed bug in methods for drawing horizontal and vertical
74 * lines (DG);
75 * 13-May-2002 : Small change to the draw() method so that it works for
76 * OverlaidXYPlot also (DG);
77 * 25-Jun-2002 : Removed redundant import (DG);
78 * 20-Aug-2002 : Renamed getItemRenderer() --> getRenderer(), and
79 * setXYItemRenderer() --> setRenderer() (DG);
80 * 28-Aug-2002 : Added mechanism for (optional) plot annotations (DG);
81 * 02-Oct-2002 : Fixed errors reported by Checkstyle (DG);
82 * 18-Nov-2002 : Added grid settings for both domain and range axis (previously
83 * these were set in the axes) (DG);
84 * 09-Jan-2003 : Further additions to the grid settings, plus integrated plot
85 * border bug fix contributed by Gideon Krause (DG);
86 * 22-Jan-2003 : Removed monolithic constructor (DG);
87 * 04-Mar-2003 : Added 'no data' message, see bug report 691634. Added
88 * secondary range markers using code contributed by Klaus
89 * Rheinwald (DG);
90 * 26-Mar-2003 : Implemented Serializable (DG);
91 * 03-Apr-2003 : Added setDomainAxisLocation() method (DG);
92 * 30-Apr-2003 : Moved annotation drawing into a separate method (DG);
93 * 01-May-2003 : Added multi-pass mechanism for renderers (DG);
94 * 02-May-2003 : Changed axis locations from int to AxisLocation (DG);
95 * 15-May-2003 : Added an orientation attribute (DG);
96 * 02-Jun-2003 : Removed range axis compatibility test (DG);
97 * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
98 * Services Ltd) (DG);
99 * 26-Jun-2003 : Fixed bug (757303) in getDataRange() method (DG);
100 * 02-Jul-2003 : Added patch from bug report 698646 (secondary axes for
101 * overlaid plots) (DG);
102 * 23-Jul-2003 : Added support for multiple secondary datasets, axes and
103 * renderers (DG);
104 * 27-Jul-2003 : Added support for stacked XY area charts (RA);
105 * 19-Aug-2003 : Implemented Cloneable (DG);
106 * 01-Sep-2003 : Fixed bug where change to secondary datasets didn't generate
107 * change event (797466) (DG)
108 * 08-Sep-2003 : Added internationalization via use of properties
109 * resourceBundle (RFE 690236) (AL);
110 * 08-Sep-2003 : Changed ValueAxis API (DG);
111 * 08-Sep-2003 : Fixes for serialization (NB);
112 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
113 * 17-Sep-2003 : Fixed zooming to include secondary domain axes (DG);
114 * 18-Sep-2003 : Added getSecondaryDomainAxisCount() and
115 * getSecondaryRangeAxisCount() methods suggested by Eduardo
116 * Ramalho (RFE 808548) (DG);
117 * 23-Sep-2003 : Split domain and range markers into foreground and
118 * background (DG);
119 * 06-Oct-2003 : Fixed bug in clearDomainMarkers() and clearRangeMarkers()
120 * methods. Fixed bug (815876) in addSecondaryRangeMarker()
121 * method. Added new addSecondaryDomainMarker methods (see bug
122 * id 815869) (DG);
123 * 10-Nov-2003 : Added getSecondaryDomain/RangeAxisMappedToDataset() methods
124 * requested by Eduardo Ramalho (DG);
125 * 24-Nov-2003 : Removed unnecessary notification when updating axis anchor
126 * values (DG);
127 * 21-Jan-2004 : Update for renamed method in ValueAxis (DG);
128 * 25-Feb-2004 : Replaced CrosshairInfo with CrosshairState (DG);
129 * 12-Mar-2004 : Fixed bug where primary renderer is always used to determine
130 * range type (DG);
131 * 22-Mar-2004 : Fixed cloning bug (DG);
132 * 23-Mar-2004 : Fixed more cloning bugs (DG);
133 * 07-Apr-2004 : Fixed problem with axis range when the secondary renderer is
134 * stacked, see this post in the forum:
135 * http://www.jfree.org/phpBB2/viewtopic.php?t=8204 (DG);
136 * 07-Apr-2004 : Added get/setDatasetRenderingOrder() methods (DG);
137 * 26-Apr-2004 : Added option to fill quadrant areas in the background of the
138 * plot (DG);
139 * 27-Apr-2004 : Removed major distinction between primary and secondary
140 * datasets, renderers and axes (DG);
141 * 30-Apr-2004 : Modified to make use of the new getRangeExtent() method in the
142 * renderer interface (DG);
143 * 13-May-2004 : Added optional fixedLegendItems attribute (DG);
144 * 19-May-2004 : Added indexOf() method (DG);
145 * 03-Jun-2004 : Fixed zooming bug (DG);
146 * 18-Aug-2004 : Added removedAnnotation() method (by tkram01) (DG);
147 * 05-Oct-2004 : Modified storage type for dataset-to-axis maps (DG);
148 * 06-Oct-2004 : Modified getDataRange() method to use renderer to determine
149 * the x-value range (now matches behaviour for y-values). Added
150 * getDomainAxisIndex() method (DG);
151 * 12-Nov-2004 : Implemented new Zoomable interface (DG);
152 * 25-Nov-2004 : Small update to clone() implementation (DG);
153 * 22-Feb-2005 : Changed axis offsets from Spacer --> RectangleInsets (DG);
154 * 24-Feb-2005 : Added indexOf(XYItemRenderer) method (DG);
155 * 21-Mar-2005 : Register plot as change listener in setRenderer() method (DG);
156 * 21-Apr-2005 : Added get/setSeriesRenderingOrder() methods (ET);
157 * 26-Apr-2005 : Removed LOGGER (DG);
158 * 04-May-2005 : Fixed serialization of domain and range markers (DG);
159 * 05-May-2005 : Removed unused draw() method (DG);
160 * 20-May-2005 : Added setDomainAxes() and setRangeAxes() methods, as per
161 * RFE 1183100 (DG);
162 * 01-Jun-2005 : Upon deserialization, register plot as a listener with its
163 * axes, dataset(s) and renderer(s) - see patch 1209475 (DG);
164 * 01-Jun-2005 : Added clearDomainMarkers(int) method to match
165 * clearRangeMarkers(int) (DG);
166 * 06-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
167 * 09-Jun-2005 : Added setRenderers(), as per RFE 1183100 (DG);
168 * 06-Jul-2005 : Fixed crosshair bug (id = 1233336) (DG);
169 * ------------- JFREECHART 1.0.x ---------------------------------------------
170 * 26-Jan-2006 : Added getAnnotations() method (DG);
171 * 05-Sep-2006 : Added MarkerChangeEvent support (DG);
172 * 13-Oct-2006 : Fixed initialisation of CrosshairState - see bug report
173 * 1565168 (DG);
174 * 22-Nov-2006 : Fixed equals() and cloning() for quadrant attributes, plus
175 * API doc updates (DG);
176 * 29-Nov-2006 : Added argument checks (DG);
177 * 15-Jan-2007 : Fixed bug in drawRangeMarkers() (DG);
178 * 07-Feb-2007 : Fixed bug 1654215, renderer with no dataset (DG);
179 * 26-Feb-2007 : Added missing setDomainAxisLocation() and
180 * setRangeAxisLocation() methods (DG);
181 * 02-Mar-2007 : Fix for crosshair positioning with horizontal orientation
182 * (see patch 1671648 by Sergei Ivanov) (DG);
183 * 13-Mar-2007 : Added null argument checks for crosshair attributes (DG);
184 * 23-Mar-2007 : Added domain zero base line facility (DG);
185 * 04-May-2007 : Render only visible data items if possible (DG);
186 * 24-May-2007 : Fixed bug in render method for an empty series (DG);
187 * 07-Jun-2007 : Modified drawBackground() to pass orientation to
188 * fillBackground() for handling GradientPaint (DG);
189 * 24-Sep-2007 : Added new zoom methods (DG);
190 * 26-Sep-2007 : Include index value in IllegalArgumentExceptions (DG);
191 * 05-Nov-2007 : Applied patch 1823697, by Richard West, for removal of domain
192 * and range markers (DG);
193 * 12-Nov-2007 : Fixed bug in equals() method for domain and range tick
194 * band paint attributes (DG);
195 * 27-Nov-2007 : Added new setFixedDomain/RangeAxisSpace() methods (DG);
196 * 04-Jan-2008 : Fix for quadrant painting error - see patch 1849564 (DG);
197 * 25-Mar-2008 : Added new methods with optional notification - see patch
198 * 1913751 (DG);
199 * 07-Apr-2008 : Fixed NPE in removeDomainMarker() and
200 * removeRangeMarker() (DG);
201 * 22-May-2008 : Modified calculateAxisSpace() to process range axes first,
202 * then adjust the plot area before calculating the space
203 * for the domain axes (DG);
204 *
205 */
206
207 package org.jfree.chart.plot;
208
209 import java.awt.AlphaComposite;
210 import java.awt.BasicStroke;
211 import java.awt.Color;
212 import java.awt.Composite;
213 import java.awt.Graphics2D;
214 import java.awt.Paint;
215 import java.awt.Shape;
216 import java.awt.Stroke;
217 import java.awt.geom.Line2D;
218 import java.awt.geom.Point2D;
219 import java.awt.geom.Rectangle2D;
220 import java.io.IOException;
221 import java.io.ObjectInputStream;
222 import java.io.ObjectOutputStream;
223 import java.io.Serializable;
224 import java.util.ArrayList;
225 import java.util.Collection;
226 import java.util.Collections;
227 import java.util.HashMap;
228 import java.util.Iterator;
229 import java.util.List;
230 import java.util.Map;
231 import java.util.ResourceBundle;
232 import java.util.Set;
233 import java.util.TreeMap;
234
235 import org.jfree.chart.LegendItem;
236 import org.jfree.chart.LegendItemCollection;
237 import org.jfree.chart.annotations.XYAnnotation;
238 import org.jfree.chart.axis.Axis;
239 import org.jfree.chart.axis.AxisCollection;
240 import org.jfree.chart.axis.AxisLocation;
241 import org.jfree.chart.axis.AxisSpace;
242 import org.jfree.chart.axis.AxisState;
243 import org.jfree.chart.axis.ValueAxis;
244 import org.jfree.chart.axis.ValueTick;
245 import org.jfree.chart.event.ChartChangeEventType;
246 import org.jfree.chart.event.PlotChangeEvent;
247 import org.jfree.chart.event.RendererChangeEvent;
248 import org.jfree.chart.event.RendererChangeListener;
249 import org.jfree.chart.renderer.RendererUtilities;
250 import org.jfree.chart.renderer.xy.AbstractXYItemRenderer;
251 import org.jfree.chart.renderer.xy.XYItemRenderer;
252 import org.jfree.chart.renderer.xy.XYItemRendererState;
253 import org.jfree.data.Range;
254 import org.jfree.data.general.Dataset;
255 import org.jfree.data.general.DatasetChangeEvent;
256 import org.jfree.data.general.DatasetUtilities;
257 import org.jfree.data.xy.XYDataset;
258 import org.jfree.io.SerialUtilities;
259 import org.jfree.ui.Layer;
260 import org.jfree.ui.RectangleEdge;
261 import org.jfree.ui.RectangleInsets;
262 import org.jfree.util.ObjectList;
263 import org.jfree.util.ObjectUtilities;
264 import org.jfree.util.PaintUtilities;
265 import org.jfree.util.PublicCloneable;
266
267 /**
268 * A general class for plotting data in the form of (x, y) pairs. This plot can
269 * use data from any class that implements the {@link XYDataset} interface.
270 * <P>
271 * <code>XYPlot</code> makes use of an {@link XYItemRenderer} to draw each point
272 * on the plot. By using different renderers, various chart types can be
273 * produced.
274 * <p>
275 * The {@link org.jfree.chart.ChartFactory} class contains static methods for
276 * creating pre-configured charts.
277 */
278 public class XYPlot extends Plot implements ValueAxisPlot, Zoomable,
279 RendererChangeListener, Cloneable, PublicCloneable, Serializable {
280
281 /** For serialization. */
282 private static final long serialVersionUID = 7044148245716569264L;
283
284 /** The default grid line stroke. */
285 public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
286 BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f,
287 new float[] {2.0f, 2.0f}, 0.0f);
288
289 /** The default grid line paint. */
290 public static final Paint DEFAULT_GRIDLINE_PAINT = Color.lightGray;
291
292 /** The default crosshair visibility. */
293 public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false;
294
295 /** The default crosshair stroke. */
296 public static final Stroke DEFAULT_CROSSHAIR_STROKE
297 = DEFAULT_GRIDLINE_STROKE;
298
299 /** The default crosshair paint. */
300 public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.blue;
301
302 /** The resourceBundle for the localization. */
303 protected static ResourceBundle localizationResources
304 = ResourceBundle.getBundle(
305 "org.jfree.chart.plot.LocalizationBundle");
306
307 /** The plot orientation. */
308 private PlotOrientation orientation;
309
310 /** The offset between the data area and the axes. */
311 private RectangleInsets axisOffset;
312
313 /** The domain axis / axes (used for the x-values). */
314 private ObjectList domainAxes;
315
316 /** The domain axis locations. */
317 private ObjectList domainAxisLocations;
318
319 /** The range axis (used for the y-values). */
320 private ObjectList rangeAxes;
321
322 /** The range axis location. */
323 private ObjectList rangeAxisLocations;
324
325 /** Storage for the datasets. */
326 private ObjectList datasets;
327
328 /** Storage for the renderers. */
329 private ObjectList renderers;
330
331 /**
332 * Storage for keys that map datasets/renderers to domain axes. If the
333 * map contains no entry for a dataset, it is assumed to map to the
334 * primary domain axis (index = 0).
335 */
336 private Map datasetToDomainAxisMap;
337
338 /**
339 * Storage for keys that map datasets/renderers to range axes. If the
340 * map contains no entry for a dataset, it is assumed to map to the
341 * primary domain axis (index = 0).
342 */
343 private Map datasetToRangeAxisMap;
344
345 /** The origin point for the quadrants (if drawn). */
346 private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0);
347
348 /** The paint used for each quadrant. */
349 private transient Paint[] quadrantPaint
350 = new Paint[] {null, null, null, null};
351
352 /** A flag that controls whether the domain grid-lines are visible. */
353 private boolean domainGridlinesVisible;
354
355 /** The stroke used to draw the domain grid-lines. */
356 private transient Stroke domainGridlineStroke;
357
358 /** The paint used to draw the domain grid-lines. */
359 private transient Paint domainGridlinePaint;
360
361 /** A flag that controls whether the range grid-lines are visible. */
362 private boolean rangeGridlinesVisible;
363
364 /** The stroke used to draw the range grid-lines. */
365 private transient Stroke rangeGridlineStroke;
366
367 /** The paint used to draw the range grid-lines. */
368 private transient Paint rangeGridlinePaint;
369
370 /**
371 * A flag that controls whether or not the zero baseline against the domain
372 * axis is visible.
373 *
374 * @since 1.0.5
375 */
376 private boolean domainZeroBaselineVisible;
377
378 /**
379 * The stroke used for the zero baseline against the domain axis.
380 *
381 * @since 1.0.5
382 */
383 private transient Stroke domainZeroBaselineStroke;
384
385 /**
386 * The paint used for the zero baseline against the domain axis.
387 *
388 * @since 1.0.5
389 */
390 private transient Paint domainZeroBaselinePaint;
391
392 /**
393 * A flag that controls whether or not the zero baseline against the range
394 * axis is visible.
395 */
396 private boolean rangeZeroBaselineVisible;
397
398 /** The stroke used for the zero baseline against the range axis. */
399 private transient Stroke rangeZeroBaselineStroke;
400
401 /** The paint used for the zero baseline against the range axis. */
402 private transient Paint rangeZeroBaselinePaint;
403
404 /** A flag that controls whether or not a domain crosshair is drawn..*/
405 private boolean domainCrosshairVisible;
406
407 /** The domain crosshair value. */
408 private double domainCrosshairValue;
409
410 /** The pen/brush used to draw the crosshair (if any). */
411 private transient Stroke domainCrosshairStroke;
412
413 /** The color used to draw the crosshair (if any). */
414 private transient Paint domainCrosshairPaint;
415
416 /**
417 * A flag that controls whether or not the crosshair locks onto actual
418 * data points.
419 */
420 private boolean domainCrosshairLockedOnData = true;
421
422 /** A flag that controls whether or not a range crosshair is drawn..*/
423 private boolean rangeCrosshairVisible;
424
425 /** The range crosshair value. */
426 private double rangeCrosshairValue;
427
428 /** The pen/brush used to draw the crosshair (if any). */
429 private transient Stroke rangeCrosshairStroke;
430
431 /** The color used to draw the crosshair (if any). */
432 private transient Paint rangeCrosshairPaint;
433
434 /**
435 * A flag that controls whether or not the crosshair locks onto actual
436 * data points.
437 */
438 private boolean rangeCrosshairLockedOnData = true;
439
440 /** A map of lists of foreground markers (optional) for the domain axes. */
441 private Map foregroundDomainMarkers;
442
443 /** A map of lists of background markers (optional) for the domain axes. */
444 private Map backgroundDomainMarkers;
445
446 /** A map of lists of foreground markers (optional) for the range axes. */
447 private Map foregroundRangeMarkers;
448
449 /** A map of lists of background markers (optional) for the range axes. */
450 private Map backgroundRangeMarkers;
451
452 /**
453 * A (possibly empty) list of annotations for the plot. The list should
454 * be initialised in the constructor and never allowed to be
455 * <code>null</code>.
456 */
457 private List annotations;
458
459 /** The paint used for the domain tick bands (if any). */
460 private transient Paint domainTickBandPaint;
461
462 /** The paint used for the range tick bands (if any). */
463 private transient Paint rangeTickBandPaint;
464
465 /** The fixed domain axis space. */
466 private AxisSpace fixedDomainAxisSpace;
467
468 /** The fixed range axis space. */
469 private AxisSpace fixedRangeAxisSpace;
470
471 /**
472 * The order of the dataset rendering (REVERSE draws the primary dataset
473 * last so that it appears to be on top).
474 */
475 private DatasetRenderingOrder datasetRenderingOrder
476 = DatasetRenderingOrder.REVERSE;
477
478 /**
479 * The order of the series rendering (REVERSE draws the primary series
480 * last so that it appears to be on top).
481 */
482 private SeriesRenderingOrder seriesRenderingOrder
483 = SeriesRenderingOrder.REVERSE;
484
485 /**
486 * The weight for this plot (only relevant if this is a subplot in a
487 * combined plot).
488 */
489 private int weight;
490
491 /**
492 * An optional collection of legend items that can be returned by the
493 * getLegendItems() method.
494 */
495 private LegendItemCollection fixedLegendItems;
496
497 /**
498 * Creates a new <code>XYPlot</code> instance with no dataset, no axes and
499 * no renderer. You should specify these items before using the plot.
500 */
501 public XYPlot() {
502 this(null, null, null, null);
503 }
504
505 /**
506 * Creates a new plot with the specified dataset, axes and renderer. Any
507 * of the arguments can be <code>null</code>, but in that case you should
508 * take care to specify the value before using the plot (otherwise a
509 * <code>NullPointerException</code> may be thrown).
510 *
511 * @param dataset the dataset (<code>null</code> permitted).
512 * @param domainAxis the domain axis (<code>null</code> permitted).
513 * @param rangeAxis the range axis (<code>null</code> permitted).
514 * @param renderer the renderer (<code>null</code> permitted).
515 */
516 public XYPlot(XYDataset dataset,
517 ValueAxis domainAxis,
518 ValueAxis rangeAxis,
519 XYItemRenderer renderer) {
520
521 super();
522
523 this.orientation = PlotOrientation.VERTICAL;
524 this.weight = 1; // only relevant when this is a subplot
525 this.axisOffset = RectangleInsets.ZERO_INSETS;
526
527 // allocate storage for datasets, axes and renderers (all optional)
528 this.domainAxes = new ObjectList();
529 this.domainAxisLocations = new ObjectList();
530 this.foregroundDomainMarkers = new HashMap();
531 this.backgroundDomainMarkers = new HashMap();
532
533 this.rangeAxes = new ObjectList();
534 this.rangeAxisLocations = new ObjectList();
535 this.foregroundRangeMarkers = new HashMap();
536 this.backgroundRangeMarkers = new HashMap();
537
538 this.datasets = new ObjectList();
539 this.renderers = new ObjectList();
540
541 this.datasetToDomainAxisMap = new TreeMap();
542 this.datasetToRangeAxisMap = new TreeMap();
543
544 this.datasets.set(0, dataset);
545 if (dataset != null) {
546 dataset.addChangeListener(this);
547 }
548
549 this.renderers.set(0, renderer);
550 if (renderer != null) {
551 renderer.setPlot(this);
552 renderer.addChangeListener(this);
553 }
554
555 this.domainAxes.set(0, domainAxis);
556 this.mapDatasetToDomainAxis(0, 0);
557 if (domainAxis != null) {
558 domainAxis.setPlot(this);
559 domainAxis.addChangeListener(this);
560 }
561 this.domainAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
562
563 this.rangeAxes.set(0, rangeAxis);
564 this.mapDatasetToRangeAxis(0, 0);
565 if (rangeAxis != null) {
566 rangeAxis.setPlot(this);
567 rangeAxis.addChangeListener(this);
568 }
569 this.rangeAxisLocations.set(0, AxisLocation.BOTTOM_OR_LEFT);
570
571 configureDomainAxes();
572 configureRangeAxes();
573
574 this.domainGridlinesVisible = true;
575 this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE;
576 this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT;
577
578 this.domainZeroBaselineVisible = false;
579 this.domainZeroBaselinePaint = Color.black;
580 this.domainZeroBaselineStroke = new BasicStroke(0.5f);
581
582 this.rangeGridlinesVisible = true;
583 this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE;
584 this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT;
585
586 this.rangeZeroBaselineVisible = false;
587 this.rangeZeroBaselinePaint = Color.black;
588 this.rangeZeroBaselineStroke = new BasicStroke(0.5f);
589
590 this.domainCrosshairVisible = false;
591 this.domainCrosshairValue = 0.0;
592 this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
593 this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
594
595 this.rangeCrosshairVisible = false;
596 this.rangeCrosshairValue = 0.0;
597 this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE;
598 this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT;
599
600 this.annotations = new java.util.ArrayList();
601
602 }
603
604 /**
605 * Returns the plot type as a string.
606 *
607 * @return A short string describing the type of plot.
608 */
609 public String getPlotType() {
610 return localizationResources.getString("XY_Plot");
611 }
612
613 /**
614 * Returns the orientation of the plot.
615 *
616 * @return The orientation (never <code>null</code>).
617 *
618 * @see #setOrientation(PlotOrientation)
619 */
620 public PlotOrientation getOrientation() {
621 return this.orientation;
622 }
623
624 /**
625 * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to
626 * all registered listeners.
627 *
628 * @param orientation the orientation (<code>null</code> not allowed).
629 *
630 * @see #getOrientation()
631 */
632 public void setOrientation(PlotOrientation orientation) {
633 if (orientation == null) {
634 throw new IllegalArgumentException("Null 'orientation' argument.");
635 }
636 if (orientation != this.orientation) {
637 this.orientation = orientation;
638 fireChangeEvent();
639 }
640 }
641
642 /**
643 * Returns the axis offset.
644 *
645 * @return The axis offset (never <code>null</code>).
646 *
647 * @see #setAxisOffset(RectangleInsets)
648 */
649 public RectangleInsets getAxisOffset() {
650 return this.axisOffset;
651 }
652
653 /**
654 * Sets the axis offsets (gap between the data area and the axes) and sends
655 * a {@link PlotChangeEvent} to all registered listeners.
656 *
657 * @param offset the offset (<code>null</code> not permitted).
658 *
659 * @see #getAxisOffset()
660 */
661 public void setAxisOffset(RectangleInsets offset) {
662 if (offset == null) {
663 throw new IllegalArgumentException("Null 'offset' argument.");
664 }
665 this.axisOffset = offset;
666 fireChangeEvent();
667 }
668
669 /**
670 * Returns the domain axis with index 0. If the domain axis for this plot
671 * is <code>null</code>, then the method will return the parent plot's
672 * domain axis (if there is a parent plot).
673 *
674 * @return The domain axis (possibly <code>null</code>).
675 *
676 * @see #getDomainAxis(int)
677 * @see #setDomainAxis(ValueAxis)
678 */
679 public ValueAxis getDomainAxis() {
680 return getDomainAxis(0);
681 }
682
683 /**
684 * Returns the domain axis with the specified index, or <code>null</code>.
685 *
686 * @param index the axis index.
687 *
688 * @return The axis (<code>null</code> possible).
689 *
690 * @see #setDomainAxis(int, ValueAxis)
691 */
692 public ValueAxis getDomainAxis(int index) {
693 ValueAxis result = null;
694 if (index < this.domainAxes.size()) {
695 result = (ValueAxis) this.domainAxes.get(index);
696 }
697 if (result == null) {
698 Plot parent = getParent();
699 if (parent instanceof XYPlot) {
700 XYPlot xy = (XYPlot) parent;
701 result = xy.getDomainAxis(index);
702 }
703 }
704 return result;
705 }
706
707 /**
708 * Sets the domain axis for the plot and sends a {@link PlotChangeEvent}
709 * to all registered listeners.
710 *
711 * @param axis the new axis (<code>null</code> permitted).
712 *
713 * @see #getDomainAxis()
714 * @see #setDomainAxis(int, ValueAxis)
715 */
716 public void setDomainAxis(ValueAxis axis) {
717 setDomainAxis(0, axis);
718 }
719
720 /**
721 * Sets a domain axis and sends a {@link PlotChangeEvent} to all
722 * registered listeners.
723 *
724 * @param index the axis index.
725 * @param axis the axis (<code>null</code> permitted).
726 *
727 * @see #getDomainAxis(int)
728 * @see #setRangeAxis(int, ValueAxis)
729 */
730 public void setDomainAxis(int index, ValueAxis axis) {
731 setDomainAxis(index, axis, true);
732 }
733
734 /**
735 * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to
736 * all registered listeners.
737 *
738 * @param index the axis index.
739 * @param axis the axis.
740 * @param notify notify listeners?
741 *
742 * @see #getDomainAxis(int)
743 */
744 public void setDomainAxis(int index, ValueAxis axis, boolean notify) {
745 ValueAxis existing = getDomainAxis(index);
746 if (existing != null) {
747 existing.removeChangeListener(this);
748 }
749 if (axis != null) {
750 axis.setPlot(this);
751 }
752 this.domainAxes.set(index, axis);
753 if (axis != null) {
754 axis.configure();
755 axis.addChangeListener(this);
756 }
757 if (notify) {
758 fireChangeEvent();
759 }
760 }
761
762 /**
763 * Sets the domain axes for this plot and sends a {@link PlotChangeEvent}
764 * to all registered listeners.
765 *
766 * @param axes the axes (<code>null</code> not permitted).
767 *
768 * @see #setRangeAxes(ValueAxis[])
769 */
770 public void setDomainAxes(ValueAxis[] axes) {
771 for (int i = 0; i < axes.length; i++) {
772 setDomainAxis(i, axes[i], false);
773 }
774 fireChangeEvent();
775 }
776
777 /**
778 * Returns the location of the primary domain axis.
779 *
780 * @return The location (never <code>null</code>).
781 *
782 * @see #setDomainAxisLocation(AxisLocation)
783 */
784 public AxisLocation getDomainAxisLocation() {
785 return (AxisLocation) this.domainAxisLocations.get(0);
786 }
787
788 /**
789 * Sets the location of the primary domain axis and sends a
790 * {@link PlotChangeEvent} to all registered listeners.
791 *
792 * @param location the location (<code>null</code> not permitted).
793 *
794 * @see #getDomainAxisLocation()
795 */
796 public void setDomainAxisLocation(AxisLocation location) {
797 // delegate...
798 setDomainAxisLocation(0, location, true);
799 }
800
801 /**
802 * Sets the location of the domain axis and, if requested, sends a
803 * {@link PlotChangeEvent} to all registered listeners.
804 *
805 * @param location the location (<code>null</code> not permitted).
806 * @param notify notify listeners?
807 *
808 * @see #getDomainAxisLocation()
809 */
810 public void setDomainAxisLocation(AxisLocation location, boolean notify) {
811 // delegate...
812 setDomainAxisLocation(0, location, notify);
813 }
814
815 /**
816 * Returns the edge for the primary domain axis (taking into account the
817 * plot's orientation).
818 *
819 * @return The edge.
820 *
821 * @see #getDomainAxisLocation()
822 * @see #getOrientation()
823 */
824 public RectangleEdge getDomainAxisEdge() {
825 return Plot.resolveDomainAxisLocation(getDomainAxisLocation(),
826 this.orientation);
827 }
828
829 /**
830 * Returns the number of domain axes.
831 *
832 * @return The axis count.
833 *
834 * @see #getRangeAxisCount()
835 */
836 public int getDomainAxisCount() {
837 return this.domainAxes.size();
838 }
839
840 /**
841 * Clears the domain axes from the plot and sends a {@link PlotChangeEvent}
842 * to all registered listeners.
843 *
844 * @see #clearRangeAxes()
845 */
846 public void clearDomainAxes() {
847 for (int i = 0; i < this.domainAxes.size(); i++) {
848 ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
849 if (axis != null) {
850 axis.removeChangeListener(this);
851 }
852 }
853 this.domainAxes.clear();
854 fireChangeEvent();
855 }
856
857 /**
858 * Configures the domain axes.
859 */
860 public void configureDomainAxes() {
861 for (int i = 0; i < this.domainAxes.size(); i++) {
862 ValueAxis axis = (ValueAxis) this.domainAxes.get(i);
863 if (axis != null) {
864 axis.configure();
865 }
866 }
867 }
868
869 /**
870 * Returns the location for a domain axis. If this hasn't been set
871 * explicitly, the method returns the location that is opposite to the
872 * primary domain axis location.
873 *
874 * @param index the axis index.
875 *
876 * @return The location (never <code>null</code>).
877 *
878 * @see #setDomainAxisLocation(int, AxisLocation)
879 */
880 public AxisLocation getDomainAxisLocation(int index) {
881 AxisLocation result = null;
882 if (index < this.domainAxisLocations.size()) {
883 result = (AxisLocation) this.domainAxisLocations.get(index);
884 }
885 if (result == null) {
886 result = AxisLocation.getOpposite(getDomainAxisLocation());
887 }
888 return result;
889 }
890
891 /**
892 * Sets the location for a domain axis and sends a {@link PlotChangeEvent}
893 * to all registered listeners.
894 *
895 * @param index the axis index.
896 * @param location the location (<code>null</code> not permitted for index
897 * 0).
898 *
899 * @see #getDomainAxisLocation(int)
900 */
901 public void setDomainAxisLocation(int index, AxisLocation location) {
902 // delegate...
903 setDomainAxisLocation(index, location, true);
904 }
905
906 /**
907 * Sets the axis location for a domain axis and, if requested, sends a
908 * {@link PlotChangeEvent} to all registered listeners.
909 *
910 * @param index the axis index.
911 * @param location the location (<code>null</code> not permitted for
912 * index 0).
913 * @param notify notify listeners?
914 *
915 * @since 1.0.5
916 *
917 * @see #getDomainAxisLocation(int)
918 * @see #setRangeAxisLocation(int, AxisLocation, boolean)
919 */
920 public void setDomainAxisLocation(int index, AxisLocation location,
921 boolean notify) {
922
923 if (index == 0 && location == null) {
924 throw new IllegalArgumentException(
925 "Null 'location' for index 0 not permitted.");
926 }
927 this.domainAxisLocations.set(index, location);
928 if (notify) {
929 fireChangeEvent();
930 }
931 }
932
933 /**
934 * Returns the edge for a domain axis.
935 *
936 * @param index the axis index.
937 *
938 * @return The edge.
939 *
940 * @see #getRangeAxisEdge(int)
941 */
942 public RectangleEdge getDomainAxisEdge(int index) {
943 AxisLocation location = getDomainAxisLocation(index);
944 RectangleEdge result = Plot.resolveDomainAxisLocation(location,
945 this.orientation);
946 if (result == null) {
947 result = RectangleEdge.opposite(getDomainAxisEdge());
948 }
949 return result;
950 }
951
952 /**
953 * Returns the range axis for the plot. If the range axis for this plot is
954 * <code>null</code>, then the method will return the parent plot's range
955 * axis (if there is a parent plot).
956 *
957 * @return The range axis.
958 *
959 * @see #getRangeAxis(int)
960 * @see #setRangeAxis(ValueAxis)
961 */
962 public ValueAxis getRangeAxis() {
963 return getRangeAxis(0);
964 }
965
966 /**
967 * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to
968 * all registered listeners.
969 *
970 * @param axis the axis (<code>null</code> permitted).
971 *
972 * @see #getRangeAxis()
973 * @see #setRangeAxis(int, ValueAxis)
974 */
975 public void setRangeAxis(ValueAxis axis) {
976
977 if (axis != null) {
978 axis.setPlot(this);
979 }
980
981 // plot is likely registered as a listener with the existing axis...
982 ValueAxis existing = getRangeAxis();
983 if (existing != null) {
984 existing.removeChangeListener(this);
985 }
986
987 this.rangeAxes.set(0, axis);
988 if (axis != null) {
989 axis.configure();
990 axis.addChangeListener(this);
991 }
992 fireChangeEvent();
993
994 }
995
996 /**
997 * Returns the location of the primary range axis.
998 *
999 * @return The location (never <code>null</code>).
1000 *
1001 * @see #setRangeAxisLocation(AxisLocation)
1002 */
1003 public AxisLocation getRangeAxisLocation() {
1004 return (AxisLocation) this.rangeAxisLocations.get(0);
1005 }
1006
1007 /**
1008 * Sets the location of the primary range axis and sends a
1009 * {@link PlotChangeEvent} to all registered listeners.
1010 *
1011 * @param location the location (<code>null</code> not permitted).
1012 *
1013 * @see #getRangeAxisLocation()
1014 */
1015 public void setRangeAxisLocation(AxisLocation location) {
1016 // delegate...
1017 setRangeAxisLocation(0, location, true);
1018 }
1019
1020 /**
1021 * Sets the location of the primary range axis and, if requested, sends a
1022 * {@link PlotChangeEvent} to all registered listeners.
1023 *
1024 * @param location the location (<code>null</code> not permitted).
1025 * @param notify notify listeners?
1026 *
1027 * @see #getRangeAxisLocation()
1028 */
1029 public void setRangeAxisLocation(AxisLocation location, boolean notify) {
1030 // delegate...
1031 setRangeAxisLocation(0, location, notify);
1032 }
1033
1034 /**
1035 * Returns the edge for the primary range axis.
1036 *
1037 * @return The range axis edge.
1038 *
1039 * @see #getRangeAxisLocation()
1040 * @see #getOrientation()
1041 */
1042 public RectangleEdge getRangeAxisEdge() {
1043 return Plot.resolveRangeAxisLocation(getRangeAxisLocation(),
1044 this.orientation);
1045 }
1046
1047 /**
1048 * Returns a range axis.
1049 *
1050 * @param index the axis index.
1051 *
1052 * @return The axis (<code>null</code> possible).
1053 *
1054 * @see #setRangeAxis(int, ValueAxis)
1055 */
1056 public ValueAxis getRangeAxis(int index) {
1057 ValueAxis result = null;
1058 if (index < this.rangeAxes.size()) {
1059 result = (ValueAxis) this.rangeAxes.get(index);
1060 }
1061 if (result == null) {
1062 Plot parent = getParent();
1063 if (parent instanceof XYPlot) {
1064 XYPlot xy = (XYPlot) parent;
1065 result = xy.getRangeAxis(index);
1066 }
1067 }
1068 return result;
1069 }
1070
1071 /**
1072 * Sets a range axis and sends a {@link PlotChangeEvent} to all registered
1073 * listeners.
1074 *
1075 * @param index the axis index.
1076 * @param axis the axis (<code>null</code> permitted).
1077 *
1078 * @see #getRangeAxis(int)
1079 */
1080 public void setRangeAxis(int index, ValueAxis axis) {
1081 setRangeAxis(index, axis, true);
1082 }
1083
1084 /**
1085 * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to
1086 * all registered listeners.
1087 *
1088 * @param index the axis index.
1089 * @param axis the axis (<code>null</code> permitted).
1090 * @param notify notify listeners?
1091 *
1092 * @see #getRangeAxis(int)
1093 */
1094 public void setRangeAxis(int index, ValueAxis axis, boolean notify) {
1095 ValueAxis existing = getRangeAxis(index);
1096 if (existing != null) {
1097 existing.removeChangeListener(this);
1098 }
1099 if (axis != null) {
1100 axis.setPlot(this);
1101 }
1102 this.rangeAxes.set(index, axis);
1103 if (axis != null) {
1104 axis.configure();
1105 axis.addChangeListener(this);
1106 }
1107 if (notify) {
1108 fireChangeEvent();
1109 }
1110 }
1111
1112 /**
1113 * Sets the range axes for this plot and sends a {@link PlotChangeEvent}
1114 * to all registered listeners.
1115 *
1116 * @param axes the axes (<code>null</code> not permitted).
1117 *
1118 * @see #setDomainAxes(ValueAxis[])
1119 */
1120 public void setRangeAxes(ValueAxis[] axes) {
1121 for (int i = 0; i < axes.length; i++) {
1122 setRangeAxis(i, axes[i], false);
1123 }
1124 fireChangeEvent();
1125 }
1126
1127 /**
1128 * Returns the number of range axes.
1129 *
1130 * @return The axis count.
1131 *
1132 * @see #getDomainAxisCount()
1133 */
1134 public int getRangeAxisCount() {
1135 return this.rangeAxes.size();
1136 }
1137
1138 /**
1139 * Clears the range axes from the plot and sends a {@link PlotChangeEvent}
1140 * to all registered listeners.
1141 *
1142 * @see #clearDomainAxes()
1143 */
1144 public void clearRangeAxes() {
1145 for (int i = 0; i < this.rangeAxes.size(); i++) {
1146 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1147 if (axis != null) {
1148 axis.removeChangeListener(this);
1149 }
1150 }
1151 this.rangeAxes.clear();
1152 fireChangeEvent();
1153 }
1154
1155 /**
1156 * Configures the range axes.
1157 *
1158 * @see #configureDomainAxes()
1159 */
1160 public void configureRangeAxes() {
1161 for (int i = 0; i < this.rangeAxes.size(); i++) {
1162 ValueAxis axis = (ValueAxis) this.rangeAxes.get(i);
1163 if (axis != null) {
1164 axis.configure();
1165 }
1166 }
1167 }
1168
1169 /**
1170 * Returns the location for a range axis. If this hasn't been set
1171 * explicitly, the method returns the location that is opposite to the
1172 * primary range axis location.
1173 *
1174 * @param index the axis index.
1175 *
1176 * @return The location (never <code>null</code>).
1177 *
1178 * @see #setRangeAxisLocation(int, AxisLocation)
1179 */
1180 public AxisLocation getRangeAxisLocation(int index) {
1181 AxisLocation result = null;
1182 if (index < this.rangeAxisLocations.size()) {
1183 result = (AxisLocation) this.rangeAxisLocations.get(index);
1184 }
1185 if (result == null) {
1186 result = AxisLocation.getOpposite(getRangeAxisLocation());
1187 }
1188 return result;
1189 }
1190
1191 /**
1192 * Sets the location for a range axis and sends a {@link PlotChangeEvent}
1193 * to all registered listeners.
1194 *
1195 * @param index the axis index.
1196 * @param location the location (<code>null</code> permitted).
1197 *
1198 * @see #getRangeAxisLocation(int)
1199 */
1200 public void setRangeAxisLocation(int index, AxisLocation location) {
1201 // delegate...
1202 setRangeAxisLocation(index, location, true);
1203 }
1204
1205 /**
1206 * Sets the axis location for a domain axis and, if requested, sends a
1207 * {@link PlotChangeEvent} to all registered listeners.
1208 *
1209 * @param index the axis index.
1210 * @param location the location (<code>null</code> not permitted for
1211 * index 0).
1212 * @param notify notify listeners?
1213 *
1214 * @since 1.0.5
1215 *
1216 * @see #getRangeAxisLocation(int)
1217 * @see #setDomainAxisLocation(int, AxisLocation, boolean)
1218 */
1219 public void setRangeAxisLocation(int index, AxisLocation location,
1220 boolean notify) {
1221
1222 if (index == 0 && location == null) {
1223 throw new IllegalArgumentException(
1224 "Null 'location' for index 0 not permitted.");
1225 }
1226 this.rangeAxisLocations.set(index, location);
1227 if (notify) {
1228 fireChangeEvent();
1229 }
1230 }
1231
1232 /**
1233 * Returns the edge for a range axis.
1234 *
1235 * @param index the axis index.
1236 *
1237 * @return The edge.
1238 *
1239 * @see #getRangeAxisLocation(int)
1240 * @see #getOrientation()
1241 */
1242 public RectangleEdge getRangeAxisEdge(int index) {
1243 AxisLocation location = getRangeAxisLocation(index);
1244 RectangleEdge result = Plot.resolveRangeAxisLocation(location,
1245 this.orientation);
1246 if (result == null) {
1247 result = RectangleEdge.opposite(getRangeAxisEdge());
1248 }
1249 return result;
1250 }
1251
1252 /**
1253 * Returns the primary dataset for the plot.
1254 *
1255 * @return The primary dataset (possibly <code>null</code>).
1256 *
1257 * @see #getDataset(int)
1258 * @see #setDataset(XYDataset)
1259 */
1260 public XYDataset getDataset() {
1261 return getDataset(0);
1262 }
1263
1264 /**
1265 * Returns a dataset.
1266 *
1267 * @param index the dataset index.
1268 *
1269 * @return The dataset (possibly <code>null</code>).
1270 *
1271 * @see #setDataset(int, XYDataset)
1272 */
1273 public XYDataset getDataset(int index) {
1274 XYDataset result = null;
1275 if (this.datasets.size() > index) {
1276 result = (XYDataset) this.datasets.get(index);
1277 }
1278 return result;
1279 }
1280
1281 /**
1282 * Sets the primary dataset for the plot, replacing the existing dataset if
1283 * there is one.
1284 *
1285 * @param dataset the dataset (<code>null</code> permitted).
1286 *
1287 * @see #getDataset()
1288 * @see #setDataset(int, XYDataset)
1289 */
1290 public void setDataset(XYDataset dataset) {
1291 setDataset(0, dataset);
1292 }
1293
1294 /**
1295 * Sets a dataset for the plot.
1296 *
1297 * @param index the dataset index.
1298 * @param dataset the dataset (<code>null</code> permitted).
1299 *
1300 * @see #getDataset(int)
1301 */
1302 public void setDataset(int index, XYDataset dataset) {
1303 XYDataset existing = getDataset(index);
1304 if (existing != null) {
1305 existing.removeChangeListener(this);
1306 }
1307 this.datasets.set(index, dataset);
1308 if (dataset != null) {
1309 dataset.addChangeListener(this);
1310 }
1311
1312 // send a dataset change event to self...
1313 DatasetChangeEvent event = new DatasetChangeEvent(this, dataset);
1314 datasetChanged(event);
1315 }
1316
1317 /**
1318 * Returns the number of datasets.
1319 *
1320 * @return The number of datasets.
1321 */
1322 public int getDatasetCount() {
1323 return this.datasets.size();
1324 }
1325
1326 /**
1327 * Returns the index of the specified dataset, or <code>-1</code> if the
1328 * dataset does not belong to the plot.
1329 *
1330 * @param dataset the dataset (<code>null</code> not permitted).
1331 *
1332 * @return The index.
1333 */
1334 public int indexOf(XYDataset dataset) {
1335 int result = -1;
1336 for (int i = 0; i < this.datasets.size(); i++) {
1337 if (dataset == this.datasets.get(i)) {
1338 result = i;
1339 break;
1340 }
1341 }
1342 return result;
1343 }
1344
1345 /**
1346 * Maps a dataset to a particular domain axis. All data will be plotted
1347 * against axis zero by default, no mapping is required for this case.
1348 *
1349 * @param index the dataset index (zero-based).
1350 * @param axisIndex the axis index.
1351 *
1352 * @see #mapDatasetToRangeAxis(int, int)
1353 */
1354 public void mapDatasetToDomainAxis(int index, int axisIndex) {
1355 this.datasetToDomainAxisMap.put(new Integer(index),
1356 new Integer(axisIndex));
1357 // fake a dataset change event to update axes...
1358 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1359 }
1360
1361 /**
1362 * Maps a dataset to a particular range axis. All data will be plotted
1363 * against axis zero by default, no mapping is required for this case.
1364 *
1365 * @param index the dataset index (zero-based).
1366 * @param axisIndex the axis index.
1367 *
1368 * @see #mapDatasetToDomainAxis(int, int)
1369 */
1370 public void mapDatasetToRangeAxis(int index, int axisIndex) {
1371 this.datasetToRangeAxisMap.put(new Integer(index),
1372 new Integer(axisIndex));
1373 // fake a dataset change event to update axes...
1374 datasetChanged(new DatasetChangeEvent(this, getDataset(index)));
1375 }
1376
1377 /**
1378 * Returns the renderer for the primary dataset.
1379 *
1380 * @return The item renderer (possibly <code>null</code>).
1381 *
1382 * @see #setRenderer(XYItemRenderer)
1383 */
1384 public XYItemRenderer getRenderer() {
1385 return getRenderer(0);
1386 }
1387
1388 /**
1389 * Returns the renderer for a dataset, or <code>null</code>.
1390 *
1391 * @param index the renderer index.
1392 *
1393 * @return The renderer (possibly <code>null</code>).
1394 *
1395 * @see #setRenderer(int, XYItemRenderer)
1396 */
1397 public XYItemRenderer getRenderer(int index) {
1398 XYItemRenderer result = null;
1399 if (this.renderers.size() > index) {
1400 result = (XYItemRenderer) this.renderers.get(index);
1401 }
1402 return result;
1403
1404 }
1405
1406 /**
1407 * Sets the renderer for the primary dataset and sends a
1408 * {@link PlotChangeEvent} to all registered listeners. If the renderer
1409 * is set to <code>null</code>, no data will be displayed.
1410 *
1411 * @param renderer the renderer (<code>null</code> permitted).
1412 *
1413 * @see #getRenderer()
1414 */
1415 public void setRenderer(XYItemRenderer renderer) {
1416 setRenderer(0, renderer);
1417 }
1418
1419 /**
1420 * Sets a renderer and sends a {@link PlotChangeEvent} to all
1421 * registered listeners.
1422 *
1423 * @param index the index.
1424 * @param renderer the renderer.
1425 *
1426 * @see #getRenderer(int)
1427 */
1428 public void setRenderer(int index, XYItemRenderer renderer) {
1429 setRenderer(index, renderer, true);
1430 }
1431
1432 /**
1433 * Sets a renderer and sends a {@link PlotChangeEvent} to all
1434 * registered listeners.
1435 *
1436 * @param index the index.
1437 * @param renderer the renderer.
1438 * @param notify notify listeners?
1439 *
1440 * @see #getRenderer(int)
1441 */
1442 public void setRenderer(int index, XYItemRenderer renderer,
1443 boolean notify) {
1444 XYItemRenderer existing = getRenderer(index);
1445 if (existing != null) {
1446 existing.removeChangeListener(this);
1447 }
1448 this.renderers.set(index, renderer);
1449 if (renderer != null) {
1450 renderer.setPlot(this);
1451 renderer.addChangeListener(this);
1452 }
1453 configureDomainAxes();
1454 configureRangeAxes();
1455 if (notify) {
1456 fireChangeEvent();
1457 }
1458 }
1459
1460 /**
1461 * Sets the renderers for this plot and sends a {@link PlotChangeEvent}
1462 * to all registered listeners.
1463 *
1464 * @param renderers the renderers (<code>null</code> not permitted).
1465 */
1466 public void setRenderers(XYItemRenderer[] renderers) {
1467 for (int i = 0; i < renderers.length; i++) {
1468 setRenderer(i, renderers[i], false);
1469 }
1470 fireChangeEvent();
1471 }
1472
1473 /**
1474 * Returns the dataset rendering order.
1475 *
1476 * @return The order (never <code>null</code>).
1477 *
1478 * @see #setDatasetRenderingOrder(DatasetRenderingOrder)
1479 */
1480 public DatasetRenderingOrder getDatasetRenderingOrder() {
1481 return this.datasetRenderingOrder;
1482 }
1483
1484 /**
1485 * Sets the rendering order and sends a {@link PlotChangeEvent} to all
1486 * registered listeners. By default, the plot renders the primary dataset
1487 * last (so that the primary dataset overlays the secondary datasets).
1488 * You can reverse this if you want to.
1489 *
1490 * @param order the rendering order (<code>null</code> not permitted).
1491 *
1492 * @see #getDatasetRenderingOrder()
1493 */
1494 public void setDatasetRenderingOrder(DatasetRenderingOrder order) {
1495 if (order == null) {
1496 throw new IllegalArgumentException("Null 'order' argument.");
1497 }
1498 this.datasetRenderingOrder = order;
1499 fireChangeEvent();
1500 }
1501
1502 /**
1503 * Returns the series rendering order.
1504 *
1505 * @return the order (never <code>null</code>).
1506 *
1507 * @see #setSeriesRenderingOrder(SeriesRenderingOrder)
1508 */
1509 public SeriesRenderingOrder getSeriesRenderingOrder() {
1510 return this.seriesRenderingOrder;
1511 }
1512
1513 /**
1514 * Sets the series order and sends a {@link PlotChangeEvent} to all
1515 * registered listeners. By default, the plot renders the primary series
1516 * last (so that the primary series appears to be on top).
1517 * You can reverse this if you want to.
1518 *
1519 * @param order the rendering order (<code>null</code> not permitted).
1520 *
1521 * @see #getSeriesRenderingOrder()
1522 */
1523 public void setSeriesRenderingOrder(SeriesRenderingOrder order) {
1524 if (order == null) {
1525 throw new IllegalArgumentException("Null 'order' argument.");
1526 }
1527 this.seriesRenderingOrder = order;
1528 fireChangeEvent();
1529 }
1530
1531 /**
1532 * Returns the index of the specified renderer, or <code>-1</code> if the
1533 * renderer is not assigned to this plot.
1534 *
1535 * @param renderer the renderer (<code>null</code> permitted).
1536 *
1537 * @return The renderer index.
1538 */
1539 public int getIndexOf(XYItemRenderer renderer) {
1540 return this.renderers.indexOf(renderer);
1541 }
1542
1543 /**
1544 * Returns the renderer for the specified dataset. The code first
1545 * determines the index of the dataset, then checks if there is a
1546 * renderer with the same index (if not, the method returns renderer(0).
1547 *
1548 * @param dataset the dataset (<code>null</code> permitted).
1549 *
1550 * @return The renderer (possibly <code>null</code>).
1551 */
1552 public XYItemRenderer getRendererForDataset(XYDataset dataset) {
1553 XYItemRenderer result = null;
1554 for (int i = 0; i < this.datasets.size(); i++) {
1555 if (this.datasets.get(i) == dataset) {
1556 result = (XYItemRenderer) this.renderers.get(i);
1557 if (result == null) {
1558 result = getRenderer();
1559 }
1560 break;
1561 }
1562 }
1563 return result;
1564 }
1565
1566 /**
1567 * Returns the weight for this plot when it is used as a subplot within a
1568 * combined plot.
1569 *
1570 * @return The weight.
1571 *
1572 * @see #setWeight(int)
1573 */
1574 public int getWeight() {
1575 return this.weight;
1576 }
1577
1578 /**
1579 * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all
1580 * registered listeners.
1581 *
1582 * @param weight the weight.
1583 *
1584 * @see #getWeight()
1585 */
1586 public void setWeight(int weight) {
1587 this.weight = weight;
1588 fireChangeEvent();
1589 }
1590
1591 /**
1592 * Returns <code>true</code> if the domain gridlines are visible, and
1593 * <code>false<code> otherwise.
1594 *
1595 * @return <code>true</code> or <code>false</code>.
1596 *
1597 * @see #setDomainGridlinesVisible(boolean)
1598 */
1599 public boolean isDomainGridlinesVisible() {
1600 return this.domainGridlinesVisible;
1601 }
1602
1603 /**
1604 * Sets the flag that controls whether or not the domain grid-lines are
1605 * visible.
1606 * <p>
1607 * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1608 * registered listeners.
1609 *
1610 * @param visible the new value of the flag.
1611 *
1612 * @see #isDomainGridlinesVisible()
1613 */
1614 public void setDomainGridlinesVisible(boolean visible) {
1615 if (this.domainGridlinesVisible != visible) {
1616 this.domainGridlinesVisible = visible;
1617 fireChangeEvent();
1618 }
1619 }
1620
1621 /**
1622 * Returns the stroke for the grid-lines (if any) plotted against the
1623 * domain axis.
1624 *
1625 * @return The stroke (never <code>null</code>).
1626 *
1627 * @see #setDomainGridlineStroke(Stroke)
1628 */
1629 public Stroke getDomainGridlineStroke() {
1630 return this.domainGridlineStroke;
1631 }
1632
1633 /**
1634 * Sets the stroke for the grid lines plotted against the domain axis, and
1635 * sends a {@link PlotChangeEvent} to all registered listeners.
1636 * <p>
1637 * If you set this to <code>null</code>, no grid lines will be drawn.
1638 *
1639 * @param stroke the stroke (<code>null</code> not permitted).
1640 *
1641 * @throws IllegalArgumentException if <code>stroke</code> is
1642 * <code>null</code>.
1643 *
1644 * @see #getDomainGridlineStroke()
1645 */
1646 public void setDomainGridlineStroke(Stroke stroke) {
1647 if (stroke == null) {
1648 throw new IllegalArgumentException("Null 'stroke' argument.");
1649 }
1650 this.domainGridlineStroke = stroke;
1651 fireChangeEvent();
1652 }
1653
1654 /**
1655 * Returns the paint for the grid lines (if any) plotted against the domain
1656 * axis.
1657 *
1658 * @return The paint (never <code>null</code>).
1659 *
1660 * @see #setDomainGridlinePaint(Paint)
1661 */
1662 public Paint getDomainGridlinePaint() {
1663 return this.domainGridlinePaint;
1664 }
1665
1666 /**
1667 * Sets the paint for the grid lines plotted against the domain axis, and
1668 * sends a {@link PlotChangeEvent} to all registered listeners.
1669 *
1670 * @param paint the paint (<code>null</code> not permitted).
1671 *
1672 * @throws IllegalArgumentException if <code>paint</code> is
1673 * <code>null</code>.
1674 *
1675 * @see #getDomainGridlinePaint()
1676 */
1677 public void setDomainGridlinePaint(Paint paint) {
1678 if (paint == null) {
1679 throw new IllegalArgumentException("Null 'paint' argument.");
1680 }
1681 this.domainGridlinePaint = paint;
1682 fireChangeEvent();
1683 }
1684
1685 /**
1686 * Returns <code>true</code> if the range axis grid is visible, and
1687 * <code>false<code> otherwise.
1688 *
1689 * @return A boolean.
1690 *
1691 * @see #setRangeGridlinesVisible(boolean)
1692 */
1693 public boolean isRangeGridlinesVisible() {
1694 return this.rangeGridlinesVisible;
1695 }
1696
1697 /**
1698 * Sets the flag that controls whether or not the range axis grid lines
1699 * are visible.
1700 * <p>
1701 * If the flag value is changed, a {@link PlotChangeEvent} is sent to all
1702 * registered listeners.
1703 *
1704 * @param visible the new value of the flag.
1705 *
1706 * @see #isRangeGridlinesVisible()
1707 */
1708 public void setRangeGridlinesVisible(boolean visible) {
1709 if (this.rangeGridlinesVisible != visible) {
1710 this.rangeGridlinesVisible = visible;
1711 fireChangeEvent();
1712 }
1713 }
1714
1715 /**
1716 * Returns the stroke for the grid lines (if any) plotted against the
1717 * range axis.
1718 *
1719 * @return The stroke (never <code>null</code>).
1720 *
1721 * @see #setRangeGridlineStroke(Stroke)
1722 */
1723 public Stroke getRangeGridlineStroke() {
1724 return this.rangeGridlineStroke;
1725 }
1726
1727 /**
1728 * Sets the stroke for the grid lines plotted against the range axis,
1729 * and sends a {@link PlotChangeEvent} to all registered listeners.
1730 *
1731 * @param stroke the stroke (<code>null</code> not permitted).
1732 *
1733 * @see #getRangeGridlineStroke()
1734 */
1735 public void setRangeGridlineStroke(Stroke stroke) {
1736 if (stroke == null) {
1737 throw new IllegalArgumentException("Null 'stroke' argument.");
1738 }
1739 this.rangeGridlineStroke = stroke;
1740 fireChangeEvent();
1741 }
1742
1743 /**
1744 * Returns the paint for the grid lines (if any) plotted against the range
1745 * axis.
1746 *
1747 * @return The paint (never <code>null</code>).
1748 *
1749 * @see #setRangeGridlinePaint(Paint)
1750 */
1751 public Paint getRangeGridlinePaint() {
1752 return this.rangeGridlinePaint;
1753 }
1754
1755 /**
1756 * Sets the paint for the grid lines plotted against the range axis and
1757 * sends a {@link PlotChangeEvent} to all registered listeners.
1758 *
1759 * @param paint the paint (<code>null</code> not permitted).
1760 *
1761 * @see #getRangeGridlinePaint()
1762 */
1763 public void setRangeGridlinePaint(Paint paint) {
1764 if (paint == null) {
1765 throw new IllegalArgumentException("Null 'paint' argument.");
1766 }
1767 this.rangeGridlinePaint = paint;
1768 fireChangeEvent();
1769 }
1770
1771 /**
1772 * Returns a flag that controls whether or not a zero baseline is
1773 * displayed for the domain axis.
1774 *
1775 * @return A boolean.
1776 *
1777 * @since 1.0.5
1778 *
1779 * @see #setDomainZeroBaselineVisible(boolean)
1780 */
1781 public boolean isDomainZeroBaselineVisible() {
1782 return this.domainZeroBaselineVisible;
1783 }
1784
1785 /**
1786 * Sets the flag that controls whether or not the zero baseline is
1787 * displayed for the domain axis, and sends a {@link PlotChangeEvent} to
1788 * all registered listeners.
1789 *
1790 * @param visible the flag.
1791 *
1792 * @since 1.0.5
1793 *
1794 * @see #isDomainZeroBaselineVisible()
1795 */
1796 public void setDomainZeroBaselineVisible(boolean visible) {
1797 this.domainZeroBaselineVisible = visible;
1798 fireChangeEvent();
1799 }
1800
1801 /**
1802 * Returns the stroke used for the zero baseline against the domain axis.
1803 *
1804 * @return The stroke (never <code>null</code>).
1805 *
1806 * @since 1.0.5
1807 *
1808 * @see #setDomainZeroBaselineStroke(Stroke)
1809 */
1810 public Stroke getDomainZeroBaselineStroke() {
1811 return this.domainZeroBaselineStroke;
1812 }
1813
1814 /**
1815 * Sets the stroke for the zero baseline for the domain axis,
1816 * and sends a {@link PlotChangeEvent} to all registered listeners.
1817 *
1818 * @param stroke the stroke (<code>null</code> not permitted).
1819 *
1820 * @since 1.0.5
1821 *
1822 * @see #getRangeZeroBaselineStroke()
1823 */
1824 public void setDomainZeroBaselineStroke(Stroke stroke) {
1825 if (stroke == null) {
1826 throw new IllegalArgumentException("Null 'stroke' argument.");
1827 }
1828 this.domainZeroBaselineStroke = stroke;
1829 fireChangeEvent();
1830 }
1831
1832 /**
1833 * Returns the paint for the zero baseline (if any) plotted against the
1834 * domain axis.
1835 *
1836 * @since 1.0.5
1837 *
1838 * @return The paint (never <code>null</code>).
1839 *
1840 * @see #setDomainZeroBaselinePaint(Paint)
1841 */
1842 public Paint getDomainZeroBaselinePaint() {
1843 return this.domainZeroBaselinePaint;
1844 }
1845
1846 /**
1847 * Sets the paint for the zero baseline plotted against the domain axis and
1848 * sends a {@link PlotChangeEvent} to all registered listeners.
1849 *
1850 * @param paint the paint (<code>null</code> not permitted).
1851 *
1852 * @since 1.0.5
1853 *
1854 * @see #getDomainZeroBaselinePaint()
1855 */
1856 public void setDomainZeroBaselinePaint(Paint paint) {
1857 if (paint == null) {
1858 throw new IllegalArgumentException("Null 'paint' argument.");
1859 }
1860 this.domainZeroBaselinePaint = paint;
1861 fireChangeEvent();
1862 }
1863
1864 /**
1865 * Returns a flag that controls whether or not a zero baseline is
1866 * displayed for the range axis.
1867 *
1868 * @return A boolean.
1869 *
1870 * @see #setRangeZeroBaselineVisible(boolean)
1871 */
1872 public boolean isRangeZeroBaselineVisible() {
1873 return this.rangeZeroBaselineVisible;
1874 }
1875
1876 /**
1877 * Sets the flag that controls whether or not the zero baseline is
1878 * displayed for the range axis, and sends a {@link PlotChangeEvent} to
1879 * all registered listeners.
1880 *
1881 * @param visible the flag.
1882 *
1883 * @see #isRangeZeroBaselineVisible()
1884 */
1885 public void setRangeZeroBaselineVisible(boolean visible) {
1886 this.rangeZeroBaselineVisible = visible;
1887 fireChangeEvent();
1888 }
1889
1890 /**
1891 * Returns the stroke used for the zero baseline against the range axis.
1892 *
1893 * @return The stroke (never <code>null</code>).
1894 *
1895 * @see #setRangeZeroBaselineStroke(Stroke)
1896 */
1897 public Stroke getRangeZeroBaselineStroke() {
1898 return this.rangeZeroBaselineStroke;
1899 }
1900
1901 /**
1902 * Sets the stroke for the zero baseline for the range axis,
1903 * and sends a {@link PlotChangeEvent} to all registered listeners.
1904 *
1905 * @param stroke the stroke (<code>null</code> not permitted).
1906 *
1907 * @see #getRangeZeroBaselineStroke()
1908 */
1909 public void setRangeZeroBaselineStroke(Stroke stroke) {
1910 if (stroke == null) {
1911 throw new IllegalArgumentException("Null 'stroke' argument.");
1912 }
1913 this.rangeZeroBaselineStroke = stroke;
1914 fireChangeEvent();
1915 }
1916
1917 /**
1918 * Returns the paint for the zero baseline (if any) plotted against the
1919 * range axis.
1920 *
1921 * @return The paint (never <code>null</code>).
1922 *
1923 * @see #setRangeZeroBaselinePaint(Paint)
1924 */
1925 public Paint getRangeZeroBaselinePaint() {
1926 return this.rangeZeroBaselinePaint;
1927 }
1928
1929 /**
1930 * Sets the paint for the zero baseline plotted against the range axis and
1931 * sends a {@link PlotChangeEvent} to all registered listeners.
1932 *
1933 * @param paint the paint (<code>null</code> not permitted).
1934 *
1935 * @see #getRangeZeroBaselinePaint()
1936 */
1937 public void setRangeZeroBaselinePaint(Paint paint) {
1938 if (paint == null) {
1939 throw new IllegalArgumentException("Null 'paint' argument.");
1940 }
1941 this.rangeZeroBaselinePaint = paint;
1942 fireChangeEvent();
1943 }
1944
1945 /**
1946 * Returns the paint used for the domain tick bands. If this is
1947 * <code>null</code>, no tick bands will be drawn.
1948 *
1949 * @return The paint (possibly <code>null</code>).
1950 *
1951 * @see #setDomainTickBandPaint(Paint)
1952 */
1953 public Paint getDomainTickBandPaint() {
1954 return this.domainTickBandPaint;
1955 }
1956
1957 /**
1958 * Sets the paint for the domain tick bands.
1959 *
1960 * @param paint the paint (<code>null</code> permitted).
1961 *
1962 * @see #getDomainTickBandPaint()
1963 */
1964 public void setDomainTickBandPaint(Paint paint) {
1965 this.domainTickBandPaint = paint;
1966 fireChangeEvent();
1967 }
1968
1969 /**
1970 * Returns the paint used for the range tick bands. If this is
1971 * <code>null</code>, no tick bands will be drawn.
1972 *
1973 * @return The paint (possibly <code>null</code>).
1974 *
1975 * @see #setRangeTickBandPaint(Paint)
1976 */
1977 public Paint getRangeTickBandPaint() {
1978 return this.rangeTickBandPaint;
1979 }
1980
1981 /**
1982 * Sets the paint for the range tick bands.
1983 *
1984 * @param paint the paint (<code>null</code> permitted).
1985 *
1986 * @see #getRangeTickBandPaint()
1987 */
1988 public void setRangeTickBandPaint(Paint paint) {
1989 this.rangeTickBandPaint = paint;
1990 fireChangeEvent();
1991 }
1992
1993 /**
1994 * Returns the origin for the quadrants that can be displayed on the plot.
1995 * This defaults to (0, 0).
1996 *
1997 * @return The origin point (never <code>null</code>).
1998 *
1999 * @see #setQuadrantOrigin(Point2D)
2000 */
2001 public Point2D getQuadrantOrigin() {
2002 return this.quadrantOrigin;
2003 }
2004
2005 /**
2006 * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all
2007 * registered listeners.
2008 *
2009 * @param origin the origin (<code>null</code> not permitted).
2010 *
2011 * @see #getQuadrantOrigin()
2012 */
2013 public void setQuadrantOrigin(Point2D origin) {
2014 if (origin == null) {
2015 throw new IllegalArgumentException("Null 'origin' argument.");
2016 }
2017 this.quadrantOrigin = origin;
2018 fireChangeEvent();
2019 }
2020
2021 /**
2022 * Returns the paint used for the specified quadrant.
2023 *
2024 * @param index the quadrant index (0-3).
2025 *
2026 * @return The paint (possibly <code>null</code>).
2027 *
2028 * @see #setQuadrantPaint(int, Paint)
2029 */
2030 public Paint getQuadrantPaint(int index) {
2031 if (index < 0 || index > 3) {
2032 throw new IllegalArgumentException("The index value (" + index
2033 + ") should be in the range 0 to 3.");
2034 }
2035 return this.quadrantPaint[index];
2036 }
2037
2038 /**
2039 * Sets the paint used for the specified quadrant and sends a
2040 * {@link PlotChangeEvent} to all registered listeners.
2041 *
2042 * @param index the quadrant index (0-3).
2043 * @param paint the paint (<code>null</code> permitted).
2044 *
2045 * @see #getQuadrantPaint(int)
2046 */
2047 public void setQuadrantPaint(int index, Paint paint) {
2048 if (index < 0 || index > 3) {
2049 throw new IllegalArgumentException("The index value (" + index
2050 + ") should be in the range 0 to 3.");
2051 }
2052 this.quadrantPaint[index] = paint;
2053 fireChangeEvent();
2054 }
2055
2056 /**
2057 * Adds a marker for the domain axis and sends a {@link PlotChangeEvent}
2058 * to all registered listeners.
2059 * <P>
2060 * Typically a marker will be drawn by the renderer as a line perpendicular
2061 * to the range axis, however this is entirely up to the renderer.
2062 *
2063 * @param marker the marker (<code>null</code> not permitted).
2064 *
2065 * @see #addDomainMarker(Marker, Layer)
2066 * @see #clearDomainMarkers()
2067 */
2068 public void addDomainMarker(Marker marker) {
2069 // defer argument checking...
2070 addDomainMarker(marker, Layer.FOREGROUND);
2071 }
2072
2073 /**
2074 * Adds a marker for the domain axis in the specified layer and sends a
2075 * {@link PlotChangeEvent} to all registered listeners.
2076 * <P>
2077 * Typically a marker will be drawn by the renderer as a line perpendicular
2078 * to the range axis, however this is entirely up to the renderer.
2079 *
2080 * @param marker the marker (<code>null</code> not permitted).
2081 * @param layer the layer (foreground or background).
2082 *
2083 * @see #addDomainMarker(int, Marker, Layer)
2084 */
2085 public void addDomainMarker(Marker marker, Layer layer) {
2086 addDomainMarker(0, marker, layer);
2087 }
2088
2089 /**
2090 * Clears all the (foreground and background) domain markers and sends a
2091 * {@link PlotChangeEvent} to all registered listeners.
2092 *
2093 * @see #addDomainMarker(int, Marker, Layer)
2094 */
2095 public void clearDomainMarkers() {
2096 if (this.backgroundDomainMarkers != null) {
2097 Set keys = this.backgroundDomainMarkers.keySet();
2098 Iterator iterator = keys.iterator();
2099 while (iterator.hasNext()) {
2100 Integer key = (Integer) iterator.next();
2101 clearDomainMarkers(key.intValue());
2102 }
2103 this.backgroundDomainMarkers.clear();
2104 }
2105 if (this.foregroundDomainMarkers != null) {
2106 Set keys = this.foregroundDomainMarkers.keySet();
2107 Iterator iterator = keys.iterator();
2108 while (iterator.hasNext()) {
2109 Integer key = (Integer) iterator.next();
2110 clearDomainMarkers(key.intValue());
2111 }
2112 this.foregroundDomainMarkers.clear();
2113 }
2114 fireChangeEvent();
2115 }
2116
2117 /**
2118 * Clears the (foreground and background) domain markers for a particular
2119 * renderer.
2120 *
2121 * @param index the renderer index.
2122 *
2123 * @see #clearRangeMarkers(int)
2124 */
2125 public void clearDomainMarkers(int index) {
2126 Integer key = new Integer(index);
2127 if (this.backgroundDomainMarkers != null) {
2128 Collection markers
2129 = (Collection) this.backgroundDomainMarkers.get(key);
2130 if (markers != null) {
2131 Iterator iterator = markers.iterator();
2132 while (iterator.hasNext()) {
2133 Marker m = (Marker) iterator.next();
2134 m.removeChangeListener(this);
2135 }
2136 markers.clear();
2137 }
2138 }
2139 if (this.foregroundRangeMarkers != null) {
2140 Collection markers
2141 = (Collection) this.foregroundDomainMarkers.get(key);
2142 if (markers != null) {
2143 Iterator iterator = markers.iterator();
2144 while (iterator.hasNext()) {
2145 Marker m = (Marker) iterator.next();
2146 m.removeChangeListener(this);
2147 }
2148 markers.clear();
2149 }
2150 }
2151 fireChangeEvent();
2152 }
2153
2154 /**
2155 * Adds a marker for a specific dataset/renderer and sends a
2156 * {@link PlotChangeEvent} to all registered listeners.
2157 * <P>
2158 * Typically a marker will be drawn by the renderer as a line perpendicular
2159 * to the domain axis (that the renderer is mapped to), however this is
2160 * entirely up to the renderer.
2161 *
2162 * @param index the dataset/renderer index.
2163 * @param marker the marker.
2164 * @param layer the layer (foreground or background).
2165 *
2166 * @see #clearDomainMarkers(int)
2167 * @see #addRangeMarker(int, Marker, Layer)
2168 */
2169 public void addDomainMarker(int index, Marker marker, Layer layer) {
2170 addDomainMarker(index, marker, layer, true);
2171 }
2172
2173 /**
2174 * Adds a marker for a specific dataset/renderer and, if requested, sends a
2175 * {@link PlotChangeEvent} to all registered listeners.
2176 * <P>
2177 * Typically a marker will be drawn by the renderer as a line perpendicular
2178 * to the domain axis (that the renderer is mapped to), however this is
2179 * entirely up to the renderer.
2180 *
2181 * @param index the dataset/renderer index.
2182 * @param marker the marker.
2183 * @param layer the layer (foreground or background).
2184 * @param notify notify listeners?
2185 *
2186 * @since 1.0.10
2187 */
2188 public void addDomainMarker(int index, Marker marker, Layer layer,
2189 boolean notify) {
2190 if (marker == null) {
2191 throw new IllegalArgumentException("Null 'marker' not permitted.");
2192 }
2193 if (layer == null) {
2194 throw new IllegalArgumentException("Null 'layer' not permitted.");
2195 }
2196 Collection markers;
2197 if (layer == Layer.FOREGROUND) {
2198 markers = (Collection) this.foregroundDomainMarkers.get(
2199 new Integer(index));
2200 if (markers == null) {
2201 markers = new java.util.ArrayList();
2202 this.foregroundDomainMarkers.put(new Integer(index), markers);
2203 }
2204 markers.add(marker);
2205 }
2206 else if (layer == Layer.BACKGROUND) {
2207 markers = (Collection) this.backgroundDomainMarkers.get(
2208 new Integer(index));
2209 if (markers == null) {
2210 markers = new java.util.ArrayList();
2211 this.backgroundDomainMarkers.put(new Integer(index), markers);
2212 }
2213 markers.add(marker);
2214 }
2215 marker.addChangeListener(this);
2216 if (notify) {
2217 fireChangeEvent();
2218 }
2219 }
2220
2221 /**
2222 * Removes a marker for the domain axis and sends a {@link PlotChangeEvent}
2223 * to all registered listeners.
2224 *
2225 * @param marker the marker.
2226 *
2227 * @return A boolean indicating whether or not the marker was actually
2228 * removed.
2229 *
2230 * @since 1.0.7
2231 */
2232 public boolean removeDomainMarker(Marker marker) {
2233 return removeDomainMarker(marker, Layer.FOREGROUND);
2234 }
2235
2236 /**
2237 * Removes a marker for the domain axis in the specified layer and sends a
2238 * {@link PlotChangeEvent} to all registered listeners.
2239 *
2240 * @param marker the marker (<code>null</code> not permitted).
2241 * @param layer the layer (foreground or background).
2242 *
2243 * @return A boolean indicating whether or not the marker was actually
2244 * removed.
2245 *
2246 * @since 1.0.7
2247 */
2248 public boolean removeDomainMarker(Marker marker, Layer layer) {
2249 return removeDomainMarker(0, marker, layer);
2250 }
2251
2252 /**
2253 * Removes a marker for a specific dataset/renderer and sends a
2254 * {@link PlotChangeEvent} to all registered listeners.
2255 *
2256 * @param index the dataset/renderer index.
2257 * @param marker the marker.
2258 * @param layer the layer (foreground or background).
2259 *
2260 * @return A boolean indicating whether or not the marker was actually
2261 * removed.
2262 *
2263 * @since 1.0.7
2264 */
2265 public boolean removeDomainMarker(int index, Marker marker, Layer layer) {
2266 return removeDomainMarker(index, marker, layer, true);
2267 }
2268
2269 /**
2270 * Removes a marker for a specific dataset/renderer and, if requested,
2271 * sends a {@link PlotChangeEvent} to all registered listeners.
2272 *
2273 * @param index the dataset/renderer index.
2274 * @param marker the marker.
2275 * @param layer the layer (foreground or background).
2276 * @param notify notify listeners?
2277 *
2278 * @return A boolean indicating whether or not the marker was actually
2279 * removed.
2280 *
2281 * @since 1.0.10
2282 */
2283 public boolean removeDomainMarker(int index, Marker marker, Layer layer,
2284 boolean notify) {
2285 ArrayList markers;
2286 if (layer == Layer.FOREGROUND) {
2287 markers = (ArrayList) this.foregroundDomainMarkers.get(
2288 new Integer(index));
2289 }
2290 else {
2291 markers = (ArrayList) this.backgroundDomainMarkers.get(
2292 new Integer(index));
2293 }
2294 if (markers == null) {
2295 return false;
2296 }
2297 boolean removed = markers.remove(marker);
2298 if (removed && notify) {
2299 fireChangeEvent();
2300 }
2301 return removed;
2302 }
2303
2304 /**
2305 * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to
2306 * all registered listeners.
2307 * <P>
2308 * Typically a marker will be drawn by the renderer as a line perpendicular
2309 * to the range axis, however this is entirely up to the renderer.
2310 *
2311 * @param marker the marker (<code>null</code> not permitted).
2312 *
2313 * @see #addRangeMarker(Marker, Layer)
2314 */
2315 public void addRangeMarker(Marker marker) {
2316 addRangeMarker(marker, Layer.FOREGROUND);
2317 }
2318
2319 /**
2320 * Adds a marker for the range axis in the specified layer and sends a
2321 * {@link PlotChangeEvent} to all registered listeners.
2322 * <P>
2323 * Typically a marker will be drawn by the renderer as a line perpendicular
2324 * to the range axis, however this is entirely up to the renderer.
2325 *
2326 * @param marker the marker (<code>null</code> not permitted).
2327 * @param layer the layer (foreground or background).
2328 *
2329 * @see #addRangeMarker(int, Marker, Layer)
2330 */
2331 public void addRangeMarker(Marker marker, Layer layer) {
2332 addRangeMarker(0, marker, layer);
2333 }
2334
2335 /**
2336 * Clears all the range markers and sends a {@link PlotChangeEvent} to all
2337 * registered listeners.
2338 *
2339 * @see #clearRangeMarkers()
2340 */
2341 public void clearRangeMarkers() {
2342 if (this.backgroundRangeMarkers != null) {
2343 Set keys = this.backgroundRangeMarkers.keySet();
2344 Iterator iterator = keys.iterator();
2345 while (iterator.hasNext()) {
2346 Integer key = (Integer) iterator.next();
2347 clearRangeMarkers(key.intValue());
2348 }
2349 this.backgroundRangeMarkers.clear();
2350 }
2351 if (this.foregroundRangeMarkers != null) {
2352 Set keys = this.foregroundRangeMarkers.keySet();
2353 Iterator iterator = keys.iterator();
2354 while (iterator.hasNext()) {
2355 Integer key = (Integer) iterator.next();
2356 clearRangeMarkers(key.intValue());
2357 }
2358 this.foregroundRangeMarkers.clear();
2359 }
2360 fireChangeEvent();
2361 }
2362
2363 /**
2364 * Adds a marker for a specific dataset/renderer and sends a
2365 * {@link PlotChangeEvent} to all registered listeners.
2366 * <P>
2367 * Typically a marker will be drawn by the renderer as a line perpendicular
2368 * to the range axis, however this is entirely up to the renderer.
2369 *
2370 * @param index the dataset/renderer index.
2371 * @param marker the marker.
2372 * @param layer the layer (foreground or background).
2373 *
2374 * @see #clearRangeMarkers(int)
2375 * @see #addDomainMarker(int, Marker, Layer)
2376 */
2377 public void addRangeMarker(int index, Marker marker, Layer layer) {
2378 addRangeMarker(index, marker, layer, true);
2379 }
2380
2381 /**
2382 * Adds a marker for a specific dataset/renderer and, if requested, sends a
2383 * {@link PlotChangeEvent} to all registered listeners.
2384 * <P>
2385 * Typically a marker will be drawn by the renderer as a line perpendicular
2386 * to the range axis, however this is entirely up to the renderer.
2387 *
2388 * @param index the dataset/renderer index.
2389 * @param marker the marker.
2390 * @param layer the layer (foreground or background).
2391 * @param notify notify listeners?
2392 *
2393 * @since 1.0.10
2394 */
2395 public void addRangeMarker(int index, Marker marker, Layer layer,
2396 boolean notify) {
2397 Collection markers;
2398 if (layer == Layer.FOREGROUND) {
2399 markers = (Collection) this.foregroundRangeMarkers.get(
2400 new Integer(index));
2401 if (markers == null) {
2402 markers = new java.util.ArrayList();
2403 this.foregroundRangeMarkers.put(new Integer(index), markers);
2404 }
2405 markers.add(marker);
2406 }
2407 else if (layer == Layer.BACKGROUND) {
2408 markers = (Collection) this.backgroundRangeMarkers.get(
2409 new Integer(index));
2410 if (markers == null) {
2411 markers = new java.util.ArrayList();
2412 this.backgroundRangeMarkers.put(new Integer(index), markers);
2413 }
2414 markers.add(marker);
2415 }
2416 marker.addChangeListener(this);
2417 if (notify) {
2418 fireChangeEvent();
2419 }
2420 }
2421
2422 /**
2423 * Clears the (foreground and background) range markers for a particular
2424 * renderer.
2425 *
2426 * @param index the renderer index.
2427 */
2428 public void clearRangeMarkers(int index) {
2429 Integer key = new Integer(index);
2430 if (this.backgroundRangeMarkers != null) {
2431 Collection markers
2432 = (Collection) this.backgroundRangeMarkers.get(key);
2433 if (markers != null) {
2434 Iterator iterator = markers.iterator();
2435 while (iterator.hasNext()) {
2436 Marker m = (Marker) iterator.next();
2437 m.removeChangeListener(this);
2438 }
2439 markers.clear();
2440 }
2441 }
2442 if (this.foregroundRangeMarkers != null) {
2443 Collection markers
2444 = (Collection) this.foregroundRangeMarkers.get(key);
2445 if (markers != null) {
2446 Iterator iterator = markers.iterator();
2447 while (iterator.hasNext()) {
2448 Marker m = (Marker) iterator.next();
2449 m.removeChangeListener(this);
2450 }
2451 markers.clear();
2452 }
2453 }
2454 fireChangeEvent();
2455 }
2456
2457 /**
2458 * Removes a marker for the range axis and sends a {@link PlotChangeEvent}
2459 * to all registered listeners.
2460 *
2461 * @param marker the marker.
2462 *
2463 * @return A boolean indicating whether or not the marker was actually
2464 * removed.
2465 *
2466 * @since 1.0.7
2467 */
2468 public boolean removeRangeMarker(Marker marker) {
2469 return removeRangeMarker(marker, Layer.FOREGROUND);
2470 }
2471
2472 /**
2473 * Removes a marker for the range axis in the specified layer and sends a
2474 * {@link PlotChangeEvent} to all registered listeners.
2475 *
2476 * @param marker the marker (<code>null</code> not permitted).
2477 * @param layer the layer (foreground or background).
2478 *
2479 * @return A boolean indicating whether or not the marker was actually
2480 * removed.
2481 *
2482 * @since 1.0.7
2483 */
2484 public boolean removeRangeMarker(Marker marker, Layer layer) {
2485 return removeRangeMarker(0, marker, layer);
2486 }
2487
2488 /**
2489 * Removes a marker for a specific dataset/renderer and sends a
2490 * {@link PlotChangeEvent} to all registered listeners.
2491 *
2492 * @param index the dataset/renderer index.
2493 * @param marker the marker.
2494 * @param layer the layer (foreground or background).
2495 *
2496 * @return A boolean indicating whether or not the marker was actually
2497 * removed.
2498 *
2499 * @since 1.0.7
2500 */
2501 public boolean removeRangeMarker(int index, Marker marker, Layer layer) {
2502 return removeRangeMarker(index, marker, layer, true);
2503 }
2504
2505 /**
2506 * Removes a marker for a specific dataset/renderer and sends a
2507 * {@link PlotChangeEvent} to all registered listeners.
2508 *
2509 * @param index the dataset/renderer index.
2510 * @param marker the marker.
2511 * @param layer the layer (foreground or background).
2512 * @param notify notify listeners?
2513 *
2514 * @return A boolean indicating whether or not the marker was actually
2515 * removed.
2516 *
2517 * @since 1.0.10
2518 */
2519 public boolean removeRangeMarker(int index, Marker marker, Layer layer,
2520 boolean notify) {
2521 if (marker == null) {
2522 throw new IllegalArgumentException("Null 'marker' argument.");
2523 }
2524 ArrayList markers;
2525 if (layer == Layer.FOREGROUND) {
2526 markers = (ArrayList) this.foregroundRangeMarkers.get(
2527 new Integer(index));
2528 }
2529 else {
2530