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