001 /* ===========================================================
002 * JFreeChart : a free chart library for the Java(tm) platform
003 * ===========================================================
004 *
005 * (C) Copyright 2000-2008, by Object Refinery Limited and Contributors.
006 *
007 * Project Info: http://www.jfree.org/jfreechart/index.html
008 *
009 * This library is free software; you can redistribute it and/or modify it
010 * under the terms of the GNU Lesser General Public License as published by
011 * the Free Software Foundation; either version 2.1 of the License, or
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
022 * USA.
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
025 * in the United States and other countries.]
026 *
027 * ---------------------------
028 * AbstractXYItemRenderer.java
029 * ---------------------------
030 * (C) Copyright 2002-2008, by Object Refinery Limited and Contributors.
031 *
032 * Original Author: David Gilbert (for Object Refinery Limited);
033 * Contributor(s): Richard Atkinson;
034 * Focus Computer Services Limited;
035 * Tim Bardzil;
036 * Sergei Ivanov;
037 *
038 * Changes:
039 * --------
040 * 15-Mar-2002 : Version 1 (DG);
041 * 09-Apr-2002 : Added a getToolTipGenerator() method reflecting the change in
042 * the XYItemRenderer interface (DG);
043 * 05-Aug-2002 : Added a urlGenerator member variable to support HTML image
044 * maps (RA);
045 * 20-Aug-2002 : Added property change events for the tooltip and URL
046 * generators (DG);
047 * 22-Aug-2002 : Moved property change support into AbstractRenderer class (DG);
048 * 23-Sep-2002 : Fixed errors reported by Checkstyle tool (DG);
049 * 18-Nov-2002 : Added methods for drawing grid lines (DG);
050 * 17-Jan-2003 : Moved plot classes into a separate package (DG);
051 * 25-Mar-2003 : Implemented Serializable (DG);
052 * 01-May-2003 : Modified initialise() return type and drawItem() method
053 * signature (DG);
054 * 15-May-2003 : Modified to take into account the plot orientation (DG);
055 * 21-May-2003 : Added labels to markers (DG);
056 * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
057 * Services Ltd) (DG);
058 * 27-Jul-2003 : Added getRangeType() to support stacked XY area charts (RA);
059 * 31-Jul-2003 : Deprecated all but the default constructor (DG);
060 * 13-Aug-2003 : Implemented Cloneable (DG);
061 * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
062 * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
063 * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG);
064 * 11-Feb-2004 : Updated labelling for markers (DG);
065 * 25-Feb-2004 : Added updateCrosshairValues() method. Moved deprecated code
066 * to bottom of source file (DG);
067 * 16-Apr-2004 : Added support for IntervalMarker in drawRangeMarker() method
068 * - thanks to Tim Bardzil (DG);
069 * 05-May-2004 : Fixed bug (948310) where interval markers extend beyond axis
070 * range (DG);
071 * 03-Jun-2004 : Fixed more bugs in drawing interval markers (DG);
072 * 26-Aug-2004 : Added the addEntity() method (DG);
073 * 29-Sep-2004 : Added annotation support (with layers) (DG);
074 * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities -->
075 * TextUtilities (DG);
076 * 06-Oct-2004 : Added findDomainBounds() method and renamed
077 * getRangeExtent() --> findRangeBounds() (DG);
078 * 07-Jan-2005 : Removed deprecated code (DG);
079 * 27-Jan-2005 : Modified getLegendItem() to omit hidden series (DG);
080 * 24-Feb-2005 : Added getLegendItems() method (DG);
081 * 08-Mar-2005 : Fixed positioning of marker labels (DG);
082 * 20-Apr-2005 : Renamed XYLabelGenerator --> XYItemLabelGenerator and
083 * added generators for legend labels, tooltips and URLs (DG);
084 * 01-Jun-2005 : Handle one dimension of the marker label adjustment
085 * automatically (DG);
086 * ------------- JFREECHART 1.0.x ---------------------------------------------
087 * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
088 * 24-Oct-2006 : Respect alpha setting in markers (see patch 1567843 by Sergei
089 * Ivanov) (DG);
090 * 24-Oct-2006 : Added code to draw outlines for interval markers (DG);
091 * 24-Nov-2006 : Fixed cloning for legend item generators (DG);
092 * 06-Feb-2007 : Added new updateCrosshairValues() method that takes into
093 * account multiple axis plots (see bug 1086307) (DG);
094 * 20-Feb-2007 : Fixed equals() method implementation (DG);
095 * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to
096 * Sergei Ivanov) (DG);
097 * 22-Mar-2007 : Modified the tool tip generator look up (DG);
098 * 23-Mar-2007 : Added drawDomainLine() method (DG);
099 * 20-Apr-2007 : Updated getLegendItem() for renderer change, and deprecated
100 * itemLabelGenerator and toolTipGenerator override fields (DG);
101 * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
102 * 12-Nov-2007 : Fixed domain and range band drawing methods (DG);
103 * 07-Apr-2008 : Minor API doc update (DG);
104 * 14-May-2008 : Updated addEntity() method to take plot orientation into
105 * account when the incoming area is null (DG);
106 * 02-Jun-2008 : Added isPointInRect() method (DG);
107 * 17-Jun-2008 : Apply legend shape, font and paint attributes (DG);
108 *
109 */
110
111 package org.jfree.chart.renderer.xy;
112
113 import java.awt.AlphaComposite;
114 import java.awt.Composite;
115 import java.awt.Font;
116 import java.awt.GradientPaint;
117 import java.awt.Graphics2D;
118 import java.awt.Paint;
119 import java.awt.Shape;
120 import java.awt.Stroke;
121 import java.awt.geom.Ellipse2D;
122 import java.awt.geom.Line2D;
123 import java.awt.geom.Point2D;
124 import java.awt.geom.Rectangle2D;
125 import java.io.Serializable;
126 import java.util.Iterator;
127 import java.util.List;
128
129 import org.jfree.chart.LegendItem;
130 import org.jfree.chart.LegendItemCollection;
131 import org.jfree.chart.annotations.XYAnnotation;
132 import org.jfree.chart.axis.ValueAxis;
133 import org.jfree.chart.entity.EntityCollection;
134 import org.jfree.chart.entity.XYItemEntity;
135 import org.jfree.chart.event.RendererChangeEvent;
136 import org.jfree.chart.labels.ItemLabelPosition;
137 import org.jfree.chart.labels.StandardXYSeriesLabelGenerator;
138 import org.jfree.chart.labels.XYItemLabelGenerator;
139 import org.jfree.chart.labels.XYSeriesLabelGenerator;
140 import org.jfree.chart.labels.XYToolTipGenerator;
141 import org.jfree.chart.plot.CrosshairState;
142 import org.jfree.chart.plot.DrawingSupplier;
143 import org.jfree.chart.plot.IntervalMarker;
144 import org.jfree.chart.plot.Marker;
145 import org.jfree.chart.plot.Plot;
146 import org.jfree.chart.plot.PlotOrientation;
147 import org.jfree.chart.plot.PlotRenderingInfo;
148 import org.jfree.chart.plot.ValueMarker;
149 import org.jfree.chart.plot.XYPlot;
150 import org.jfree.chart.renderer.AbstractRenderer;
151 import org.jfree.chart.urls.XYURLGenerator;
152 import org.jfree.data.Range;
153 import org.jfree.data.general.DatasetUtilities;
154 import org.jfree.data.xy.XYDataset;
155 import org.jfree.text.TextUtilities;
156 import org.jfree.ui.GradientPaintTransformer;
157 import org.jfree.ui.Layer;
158 import org.jfree.ui.LengthAdjustmentType;
159 import org.jfree.ui.RectangleAnchor;
160 import org.jfree.ui.RectangleInsets;
161 import org.jfree.util.ObjectList;
162 import org.jfree.util.ObjectUtilities;
163 import org.jfree.util.PublicCloneable;
164
165 /**
166 * A base class that can be used to create new {@link XYItemRenderer}
167 * implementations.
168 */
169 public abstract class AbstractXYItemRenderer extends AbstractRenderer
170 implements XYItemRenderer, Cloneable, Serializable {
171
172 /** For serialization. */
173 private static final long serialVersionUID = 8019124836026607990L;
174
175 /** The plot. */
176 private XYPlot plot;
177
178 /**
179 * The item label generator for ALL series.
180 *
181 * @deprecated This field is redundant, use itemLabelGeneratorList and
182 * baseItemLabelGenerator instead. Deprecated as of version 1.0.6.
183 */
184 private XYItemLabelGenerator itemLabelGenerator;
185
186 /** A list of item label generators (one per series). */
187 private ObjectList itemLabelGeneratorList;
188
189 /** The base item label generator. */
190 private XYItemLabelGenerator baseItemLabelGenerator;
191
192 /**
193 * The tool tip generator for ALL series.
194 *
195 * @deprecated This field is redundant, use tooltipGeneratorList and
196 * baseToolTipGenerator instead. Deprecated as of version 1.0.6.
197 */
198 private XYToolTipGenerator toolTipGenerator;
199
200 /** A list of tool tip generators (one per series). */
201 private ObjectList toolTipGeneratorList;
202
203 /** The base tool tip generator. */
204 private XYToolTipGenerator baseToolTipGenerator;
205
206 /** The URL text generator. */
207 private XYURLGenerator urlGenerator;
208
209 /**
210 * Annotations to be drawn in the background layer ('underneath' the data
211 * items).
212 */
213 private List backgroundAnnotations;
214
215 /**
216 * Annotations to be drawn in the foreground layer ('on top' of the data
217 * items).
218 */
219 private List foregroundAnnotations;
220
221 /** The default radius for the entity 'hotspot' */
222 private int defaultEntityRadius;
223
224 /** The legend item label generator. */
225 private XYSeriesLabelGenerator legendItemLabelGenerator;
226
227 /** The legend item tool tip generator. */
228 private XYSeriesLabelGenerator legendItemToolTipGenerator;
229
230 /** The legend item URL generator. */
231 private XYSeriesLabelGenerator legendItemURLGenerator;
232
233 /**
234 * Creates a renderer where the tooltip generator and the URL generator are
235 * both <code>null</code>.
236 */
237 protected AbstractXYItemRenderer() {
238 super();
239 this.itemLabelGenerator = null;
240 this.itemLabelGeneratorList = new ObjectList();
241 this.toolTipGenerator = null;
242 this.toolTipGeneratorList = new ObjectList();
243 this.urlGenerator = null;
244 this.backgroundAnnotations = new java.util.ArrayList();
245 this.foregroundAnnotations = new java.util.ArrayList();
246 this.defaultEntityRadius = 3;
247 this.legendItemLabelGenerator = new StandardXYSeriesLabelGenerator(
248 "{0}");
249 }
250
251 /**
252 * Returns the number of passes through the data that the renderer requires
253 * in order to draw the chart. Most charts will require a single pass, but
254 * some require two passes.
255 *
256 * @return The pass count.
257 */
258 public int getPassCount() {
259 return 1;
260 }
261
262 /**
263 * Returns the plot that the renderer is assigned to.
264 *
265 * @return The plot (possibly <code>null</code>).
266 */
267 public XYPlot getPlot() {
268 return this.plot;
269 }
270
271 /**
272 * Sets the plot that the renderer is assigned to.
273 *
274 * @param plot the plot (<code>null</code> permitted).
275 */
276 public void setPlot(XYPlot plot) {
277 this.plot = plot;
278 }
279
280 /**
281 * Initialises the renderer and returns a state object that should be
282 * passed to all subsequent calls to the drawItem() method.
283 * <P>
284 * This method will be called before the first item is rendered, giving the
285 * renderer an opportunity to initialise any state information it wants to
286 * maintain. The renderer can do nothing if it chooses.
287 *
288 * @param g2 the graphics device.
289 * @param dataArea the area inside the axes.
290 * @param plot the plot.
291 * @param data the data.
292 * @param info an optional info collection object to return data back to
293 * the caller.
294 *
295 * @return The renderer state (never <code>null</code>).
296 */
297 public XYItemRendererState initialise(Graphics2D g2,
298 Rectangle2D dataArea,
299 XYPlot plot,
300 XYDataset data,
301 PlotRenderingInfo info) {
302
303 XYItemRendererState state = new XYItemRendererState(info);
304 return state;
305
306 }
307
308 // ITEM LABEL GENERATOR
309
310 /**
311 * Returns the label generator for a data item. This implementation simply
312 * passes control to the {@link #getSeriesItemLabelGenerator(int)} method.
313 * If, for some reason, you want a different generator for individual
314 * items, you can override this method.
315 *
316 * @param series the series index (zero based).
317 * @param item the item index (zero based).
318 *
319 * @return The generator (possibly <code>null</code>).
320 */
321 public XYItemLabelGenerator getItemLabelGenerator(int series, int item) {
322 // return the generator for ALL series, if there is one...
323 if (this.itemLabelGenerator != null) {
324 return this.itemLabelGenerator;
325 }
326
327 // otherwise look up the generator table
328 XYItemLabelGenerator generator
329 = (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series);
330 if (generator == null) {
331 generator = this.baseItemLabelGenerator;
332 }
333 return generator;
334 }
335
336 /**
337 * Returns the item label generator for a series.
338 *
339 * @param series the series index (zero based).
340 *
341 * @return The generator (possibly <code>null</code>).
342 */
343 public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) {
344 return (XYItemLabelGenerator) this.itemLabelGeneratorList.get(series);
345 }
346
347 /**
348 * Returns the item label generator override.
349 *
350 * @return The generator (possibly <code>null</code>).
351 *
352 * @since 1.0.5
353 *
354 * @see #setItemLabelGenerator(XYItemLabelGenerator)
355 *
356 * @deprecated As of version 1.0.6, this override setting should not be
357 * used. You can use the base setting instead
358 * ({@link #getBaseItemLabelGenerator()}).
359 */
360 public XYItemLabelGenerator getItemLabelGenerator() {
361 return this.itemLabelGenerator;
362 }
363
364 /**
365 * Sets the item label generator for ALL series and sends a
366 * {@link RendererChangeEvent} to all registered listeners.
367 *
368 * @param generator the generator (<code>null</code> permitted).
369 *
370 * @see #getItemLabelGenerator()
371 *
372 * @deprecated As of version 1.0.6, this override setting should not be
373 * used. You can use the base setting instead
374 * ({@link #setBaseItemLabelGenerator(XYItemLabelGenerator)}).
375 */
376 public void setItemLabelGenerator(XYItemLabelGenerator generator) {
377 this.itemLabelGenerator = generator;
378 fireChangeEvent();
379 }
380
381 /**
382 * Sets the item label generator for a series and sends a
383 * {@link RendererChangeEvent} to all registered listeners.
384 *
385 * @param series the series index (zero based).
386 * @param generator the generator (<code>null</code> permitted).
387 */
388 public void setSeriesItemLabelGenerator(int series,
389 XYItemLabelGenerator generator) {
390 this.itemLabelGeneratorList.set(series, generator);
391 fireChangeEvent();
392 }
393
394 /**
395 * Returns the base item label generator.
396 *
397 * @return The generator (possibly <code>null</code>).
398 */
399 public XYItemLabelGenerator getBaseItemLabelGenerator() {
400 return this.baseItemLabelGenerator;
401 }
402
403 /**
404 * Sets the base item label generator and sends a
405 * {@link RendererChangeEvent} to all registered listeners.
406 *
407 * @param generator the generator (<code>null</code> permitted).
408 */
409 public void setBaseItemLabelGenerator(XYItemLabelGenerator generator) {
410 this.baseItemLabelGenerator = generator;
411 fireChangeEvent();
412 }
413
414 // TOOL TIP GENERATOR
415
416 /**
417 * Returns the tool tip generator for a data item. If, for some reason,
418 * you want a different generator for individual items, you can override
419 * this method.
420 *
421 * @param series the series index (zero based).
422 * @param item the item index (zero based).
423 *
424 * @return The generator (possibly <code>null</code>).
425 */
426 public XYToolTipGenerator getToolTipGenerator(int series, int item) {
427 // return the generator for ALL series, if there is one...
428 if (this.toolTipGenerator != null) {
429 return this.toolTipGenerator;
430 }
431
432 // otherwise look up the generator table
433 XYToolTipGenerator generator
434 = (XYToolTipGenerator) this.toolTipGeneratorList.get(series);
435 if (generator == null) {
436 generator = this.baseToolTipGenerator;
437 }
438 return generator;
439 }
440
441 /**
442 * Returns the override tool tip generator.
443 *
444 * @return The tool tip generator (possible <code>null</code>).
445 *
446 * @since 1.0.5
447 *
448 * @see #setToolTipGenerator(XYToolTipGenerator)
449 *
450 * @deprecated As of version 1.0.6, this override setting should not be
451 * used. You can use the base setting instead
452 * ({@link #getBaseToolTipGenerator()}).
453 */
454 public XYToolTipGenerator getToolTipGenerator() {
455 return this.toolTipGenerator;
456 }
457
458 /**
459 * Sets the tool tip generator for ALL series and sends a
460 * {@link RendererChangeEvent} to all registered listeners.
461 *
462 * @param generator the generator (<code>null</code> permitted).
463 *
464 * @see #getToolTipGenerator()
465 *
466 * @deprecated As of version 1.0.6, this override setting should not be
467 * used. You can use the base setting instead
468 * ({@link #setBaseToolTipGenerator(XYToolTipGenerator)}).
469 */
470 public void setToolTipGenerator(XYToolTipGenerator generator) {
471 this.toolTipGenerator = generator;
472 fireChangeEvent();
473 }
474
475 /**
476 * Returns the tool tip generator for a series.
477 *
478 * @param series the series index (zero based).
479 *
480 * @return The generator (possibly <code>null</code>).
481 */
482 public XYToolTipGenerator getSeriesToolTipGenerator(int series) {
483 return (XYToolTipGenerator) this.toolTipGeneratorList.get(series);
484 }
485
486 /**
487 * Sets the tool tip generator for a series and sends a
488 * {@link RendererChangeEvent} to all registered listeners.
489 *
490 * @param series the series index (zero based).
491 * @param generator the generator (<code>null</code> permitted).
492 */
493 public void setSeriesToolTipGenerator(int series,
494 XYToolTipGenerator generator) {
495 this.toolTipGeneratorList.set(series, generator);
496 fireChangeEvent();
497 }
498
499 /**
500 * Returns the base tool tip generator.
501 *
502 * @return The generator (possibly <code>null</code>).
503 *
504 * @see #setBaseToolTipGenerator(XYToolTipGenerator)
505 */
506 public XYToolTipGenerator getBaseToolTipGenerator() {
507 return this.baseToolTipGenerator;
508 }
509
510 /**
511 * Sets the base tool tip generator and sends a {@link RendererChangeEvent}
512 * to all registered listeners.
513 *
514 * @param generator the generator (<code>null</code> permitted).
515 *
516 * @see #getBaseToolTipGenerator()
517 */
518 public void setBaseToolTipGenerator(XYToolTipGenerator generator) {
519 this.baseToolTipGenerator = generator;
520 fireChangeEvent();
521 }
522
523 // URL GENERATOR
524
525 /**
526 * Returns the URL generator for HTML image maps.
527 *
528 * @return The URL generator (possibly <code>null</code>).
529 */
530 public XYURLGenerator getURLGenerator() {
531 return this.urlGenerator;
532 }
533
534 /**
535 * Sets the URL generator for HTML image maps and sends a
536 * {@link RendererChangeEvent} to all registered listeners.
537 *
538 * @param urlGenerator the URL generator (<code>null</code> permitted).
539 */
540 public void setURLGenerator(XYURLGenerator urlGenerator) {
541 this.urlGenerator = urlGenerator;
542 fireChangeEvent();
543 }
544
545 /**
546 * Adds an annotation and sends a {@link RendererChangeEvent} to all
547 * registered listeners. The annotation is added to the foreground
548 * layer.
549 *
550 * @param annotation the annotation (<code>null</code> not permitted).
551 */
552 public void addAnnotation(XYAnnotation annotation) {
553 // defer argument checking
554 addAnnotation(annotation, Layer.FOREGROUND);
555 }
556
557 /**
558 * Adds an annotation to the specified layer and sends a
559 * {@link RendererChangeEvent} to all registered listeners.
560 *
561 * @param annotation the annotation (<code>null</code> not permitted).
562 * @param layer the layer (<code>null</code> not permitted).
563 */
564 public void addAnnotation(XYAnnotation annotation, Layer layer) {
565 if (annotation == null) {
566 throw new IllegalArgumentException("Null 'annotation' argument.");
567 }
568 if (layer.equals(Layer.FOREGROUND)) {
569 this.foregroundAnnotations.add(annotation);
570 fireChangeEvent();
571 }
572 else if (layer.equals(Layer.BACKGROUND)) {
573 this.backgroundAnnotations.add(annotation);
574 fireChangeEvent();
575 }
576 else {
577 // should never get here
578 throw new RuntimeException("Unknown layer.");
579 }
580 }
581 /**
582 * Removes the specified annotation and sends a {@link RendererChangeEvent}
583 * to all registered listeners.
584 *
585 * @param annotation the annotation to remove (<code>null</code> not
586 * permitted).
587 *
588 * @return A boolean to indicate whether or not the annotation was
589 * successfully removed.
590 */
591 public boolean removeAnnotation(XYAnnotation annotation) {
592 boolean removed = this.foregroundAnnotations.remove(annotation);
593 removed = removed & this.backgroundAnnotations.remove(annotation);
594 fireChangeEvent();
595 return removed;
596 }
597
598 /**
599 * Removes all annotations and sends a {@link RendererChangeEvent}
600 * to all registered listeners.
601 */
602 public void removeAnnotations() {
603 this.foregroundAnnotations.clear();
604 this.backgroundAnnotations.clear();
605 fireChangeEvent();
606 }
607
608 /**
609 * Returns the radius of the circle used for the default entity area
610 * when no area is specified.
611 *
612 * @return A radius.
613 *
614 * @see #setDefaultEntityRadius(int)
615 */
616 public int getDefaultEntityRadius() {
617 return this.defaultEntityRadius;
618 }
619
620 /**
621 * Sets the radius of the circle used for the default entity area
622 * when no area is specified.
623 *
624 * @param radius the radius.
625 *
626 * @see #getDefaultEntityRadius()
627 */
628 public void setDefaultEntityRadius(int radius) {
629 this.defaultEntityRadius = radius;
630 }
631
632 /**
633 * Returns the legend item label generator.
634 *
635 * @return The label generator (never <code>null</code>).
636 *
637 * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator)
638 */
639 public XYSeriesLabelGenerator getLegendItemLabelGenerator() {
640 return this.legendItemLabelGenerator;
641 }
642
643 /**
644 * Sets the legend item label generator and sends a
645 * {@link RendererChangeEvent} to all registered listeners.
646 *
647 * @param generator the generator (<code>null</code> not permitted).
648 *
649 * @see #getLegendItemLabelGenerator()
650 */
651 public void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator) {
652 if (generator == null) {
653 throw new IllegalArgumentException("Null 'generator' argument.");
654 }
655 this.legendItemLabelGenerator = generator;
656 fireChangeEvent();
657 }
658
659 /**
660 * Returns the legend item tool tip generator.
661 *
662 * @return The tool tip generator (possibly <code>null</code>).
663 *
664 * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator)
665 */
666 public XYSeriesLabelGenerator getLegendItemToolTipGenerator() {
667 return this.legendItemToolTipGenerator;
668 }
669
670 /**
671 * Sets the legend item tool tip generator and sends a
672 * {@link RendererChangeEvent} to all registered listeners.
673 *
674 * @param generator the generator (<code>null</code> permitted).
675 *
676 * @see #getLegendItemToolTipGenerator()
677 */
678 public void setLegendItemToolTipGenerator(
679 XYSeriesLabelGenerator generator) {
680 this.legendItemToolTipGenerator = generator;
681 fireChangeEvent();
682 }
683
684 /**
685 * Returns the legend item URL generator.
686 *
687 * @return The URL generator (possibly <code>null</code>).
688 *
689 * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator)
690 */
691 public XYSeriesLabelGenerator getLegendItemURLGenerator() {
692 return this.legendItemURLGenerator;
693 }
694
695 /**
696 * Sets the legend item URL generator and sends a
697 * {@link RendererChangeEvent} to all registered listeners.
698 *
699 * @param generator the generator (<code>null</code> permitted).
700 *
701 * @see #getLegendItemURLGenerator()
702 */
703 public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) {
704 this.legendItemURLGenerator = generator;
705 fireChangeEvent();
706 }
707
708 /**
709 * Returns the lower and upper bounds (range) of the x-values in the
710 * specified dataset.
711 *
712 * @param dataset the dataset (<code>null</code> permitted).
713 *
714 * @return The range (<code>null</code> if the dataset is <code>null</code>
715 * or empty).
716 *
717 * @see #findRangeBounds(XYDataset)
718 */
719 public Range findDomainBounds(XYDataset dataset) {
720 if (dataset != null) {
721 return DatasetUtilities.findDomainBounds(dataset, false);
722 }
723 else {
724 return null;
725 }
726 }
727
728 /**
729 * Returns the range of values the renderer requires to display all the
730 * items from the specified dataset.
731 *
732 * @param dataset the dataset (<code>null</code> permitted).
733 *
734 * @return The range (<code>null</code> if the dataset is <code>null</code>
735 * or empty).
736 *
737 * @see #findDomainBounds(XYDataset)
738 */
739 public Range findRangeBounds(XYDataset dataset) {
740 if (dataset != null) {
741 return DatasetUtilities.findRangeBounds(dataset, false);
742 }
743 else {
744 return null;
745 }
746 }
747
748 /**
749 * Returns a (possibly empty) collection of legend items for the series
750 * that this renderer is responsible for drawing.
751 *
752 * @return The legend item collection (never <code>null</code>).
753 */
754 public LegendItemCollection getLegendItems() {
755 if (this.plot == null) {
756 return new LegendItemCollection();
757 }
758 LegendItemCollection result = new LegendItemCollection();
759 int index = this.plot.getIndexOf(this);
760 XYDataset dataset = this.plot.getDataset(index);
761 if (dataset != null) {
762 int seriesCount = dataset.getSeriesCount();
763 for (int i = 0; i < seriesCount; i++) {
764 if (isSeriesVisibleInLegend(i)) {
765 LegendItem item = getLegendItem(index, i);
766 if (item != null) {
767 result.add(item);
768 }
769 }
770 }
771
772 }
773 return result;
774 }
775
776 /**
777 * Returns a default legend item for the specified series. Subclasses
778 * should override this method to generate customised items.
779 *
780 * @param datasetIndex the dataset index (zero-based).
781 * @param series the series index (zero-based).
782 *
783 * @return A legend item for the series.
784 */
785 public LegendItem getLegendItem(int datasetIndex, int series) {
786 LegendItem result = null;
787 XYPlot xyplot = getPlot();
788 if (xyplot != null) {
789 XYDataset dataset = xyplot.getDataset(datasetIndex);
790 if (dataset != null) {
791 String label = this.legendItemLabelGenerator.generateLabel(
792 dataset, series);
793 String description = label;
794 String toolTipText = null;
795 if (getLegendItemToolTipGenerator() != null) {
796 toolTipText = getLegendItemToolTipGenerator().generateLabel(
797 dataset, series);
798 }
799 String urlText = null;
800 if (getLegendItemURLGenerator() != null) {
801 urlText = getLegendItemURLGenerator().generateLabel(
802 dataset, series);
803 }
804 Shape shape = lookupLegendShape(series);
805 Paint paint = lookupSeriesPaint(series);
806 Paint outlinePaint = lookupSeriesOutlinePaint(series);
807 Stroke outlineStroke = lookupSeriesOutlineStroke(series);
808 result = new LegendItem(label, description, toolTipText,
809 urlText, shape, paint, outlineStroke, outlinePaint);
810 Paint labelPaint = lookupLegendTextPaint(series);
811 result.setLabelFont(lookupLegendTextFont(series));
812 if (labelPaint != null) {
813 result.setLabelPaint(labelPaint);
814 }
815 result.setSeriesKey(dataset.getSeriesKey(series));
816 result.setSeriesIndex(series);
817 result.setDataset(dataset);
818 result.setDatasetIndex(datasetIndex);
819 }
820 }
821 return result;
822 }
823
824 /**
825 * Fills a band between two values on the axis. This can be used to color
826 * bands between the grid lines.
827 *
828 * @param g2 the graphics device.
829 * @param plot the plot.
830 * @param axis the domain axis.
831 * @param dataArea the data area.
832 * @param start the start value.
833 * @param end the end value.
834 */
835 public void fillDomainGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis,
836 Rectangle2D dataArea, double start, double end) {
837
838 double x1 = axis.valueToJava2D(start, dataArea,
839 plot.getDomainAxisEdge());
840 double x2 = axis.valueToJava2D(end, dataArea,
841 plot.getDomainAxisEdge());
842 Rectangle2D band;
843 if (plot.getOrientation() == PlotOrientation.VERTICAL) {
844 band = new Rectangle2D.Double(Math.min(x1, x2), dataArea.getMinY(),
845 Math.abs(x2 - x1), dataArea.getWidth());
846 }
847 else {
848 band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(x1, x2),
849 dataArea.getWidth(), Math.abs(x2 - x1));
850 }
851 Paint paint = plot.getDomainTickBandPaint();
852
853 if (paint != null) {
854 g2.setPaint(paint);
855 g2.fill(band);
856 }
857
858 }
859
860 /**
861 * Fills a band between two values on the range axis. This can be used to
862 * color bands between the grid lines.
863 *
864 * @param g2 the graphics device.
865 * @param plot the plot.
866 * @param axis the range axis.
867 * @param dataArea the data area.
868 * @param start the start value.
869 * @param end the end value.
870 */
871 public void fillRangeGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis,
872 Rectangle2D dataArea, double start, double end) {
873
874 double y1 = axis.valueToJava2D(start, dataArea,
875 plot.getRangeAxisEdge());
876 double y2 = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge());
877 Rectangle2D band;
878 if (plot.getOrientation() == PlotOrientation.VERTICAL) {
879 band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(y1, y2),
880 dataArea.getWidth(), Math.abs(y2 - y1));
881 }
882 else {
883 band = new Rectangle2D.Double(Math.min(y1, y2), dataArea.getMinY(),
884 Math.abs(y2 - y1), dataArea.getHeight());
885 }
886 Paint paint = plot.getRangeTickBandPaint();
887
888 if (paint != null) {
889 g2.setPaint(paint);
890 g2.fill(band);
891 }
892
893 }
894
895 /**
896 * Draws a grid line against the range axis.
897 *
898 * @param g2 the graphics device.
899 * @param plot the plot.
900 * @param axis the value axis.
901 * @param dataArea the area for plotting data (not yet adjusted for any
902 * 3D effect).
903 * @param value the value at which the grid line should be drawn.
904 */
905 public void drawDomainGridLine(Graphics2D g2,
906 XYPlot plot,
907 ValueAxis axis,
908 Rectangle2D dataArea,
909 double value) {
910
911 Range range = axis.getRange();
912 if (!range.contains(value)) {
913 return;
914 }
915
916 PlotOrientation orientation = plot.getOrientation();
917 double v = axis.valueToJava2D(value, dataArea,
918 plot.getDomainAxisEdge());
919 Line2D line = null;
920 if (orientation == PlotOrientation.HORIZONTAL) {
921 line = new Line2D.Double(dataArea.getMinX(), v,
922 dataArea.getMaxX(), v);
923 }
924 else if (orientation == PlotOrientation.VERTICAL) {
925 line = new Line2D.Double(v, dataArea.getMinY(), v,
926 dataArea.getMaxY());
927 }
928
929 Paint paint = plot.getDomainGridlinePaint();
930 Stroke stroke = plot.getDomainGridlineStroke();
931 g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
932 g2.setStroke(stroke != null ? stroke : Plot.DEFAULT_OUTLINE_STROKE);
933 g2.draw(line);
934
935 }
936
937 /**
938 * Draws a line perpendicular to the domain axis.
939 *
940 * @param g2 the graphics device.
941 * @param plot the plot.
942 * @param axis the value axis.
943 * @param dataArea the area for plotting data (not yet adjusted for any 3D
944 * effect).
945 * @param value the value at which the grid line should be drawn.
946 * @param paint the paint (<code>null</code> not permitted).
947 * @param stroke the stroke (<code>null</code> not permitted).
948 *
949 * @since 1.0.5
950 */
951 public void drawDomainLine(Graphics2D g2, XYPlot plot, ValueAxis axis,
952 Rectangle2D dataArea, double value, Paint paint, Stroke stroke) {
953
954 Range range = axis.getRange();
955 if (!range.contains(value)) {
956 return;
957 }
958
959 PlotOrientation orientation = plot.getOrientation();
960 Line2D line = null;
961 double v = axis.valueToJava2D(value, dataArea,
962 plot.getDomainAxisEdge());
963 if (orientation == PlotOrientation.HORIZONTAL) {
964 line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(),
965 v);
966 }
967 else if (orientation == PlotOrientation.VERTICAL) {
968 line = new Line2D.Double(v, dataArea.getMinY(), v,
969 dataArea.getMaxY());
970 }
971
972 g2.setPaint(paint);
973 g2.setStroke(stroke);
974 g2.draw(line);
975
976 }
977
978 /**
979 * Draws a line perpendicular to the range axis.
980 *
981 * @param g2 the graphics device.
982 * @param plot the plot.
983 * @param axis the value axis.
984 * @param dataArea the area for plotting data (not yet adjusted for any 3D
985 * effect).
986 * @param value the value at which the grid line should be drawn.
987 * @param paint the paint.
988 * @param stroke the stroke.
989 */
990 public void drawRangeLine(Graphics2D g2,
991 XYPlot plot,
992 ValueAxis axis,
993 Rectangle2D dataArea,
994 double value,
995 Paint paint,
996 Stroke stroke) {
997
998 Range range = axis.getRange();
999 if (!range.contains(value)) {
1000 return;
1001 }
1002
1003 PlotOrientation orientation = plot.getOrientation();
1004 Line2D line = null;
1005 double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge());
1006 if (orientation == PlotOrientation.HORIZONTAL) {
1007 line = new Line2D.Double(v, dataArea.getMinY(), v,
1008 dataArea.getMaxY());
1009 }
1010 else if (orientation == PlotOrientation.VERTICAL) {
1011 line = new Line2D.Double(dataArea.getMinX(), v,
1012 dataArea.getMaxX(), v);
1013 }
1014
1015 g2.setPaint(paint);
1016 g2.setStroke(stroke);
1017 g2.draw(line);
1018
1019 }
1020
1021 /**
1022 * Draws a vertical line on the chart to represent a 'range marker'.
1023 *
1024 * @param g2 the graphics device.
1025 * @param plot the plot.
1026 * @param domainAxis the domain axis.
1027 * @param marker the marker line.
1028 * @param dataArea the axis data area.
1029 */
1030 public void drawDomainMarker(Graphics2D g2,
1031 XYPlot plot,
1032 ValueAxis domainAxis,
1033 Marker marker,
1034 Rectangle2D dataArea) {
1035
1036 if (marker instanceof ValueMarker) {
1037 ValueMarker vm = (ValueMarker) marker;
1038 double value = vm.getValue();
1039 Range range = domainAxis.getRange();
1040 if (!range.contains(value)) {
1041 return;
1042 }
1043
1044 double v = domainAxis.valueToJava2D(value, dataArea,
1045 plot.getDomainAxisEdge());
1046
1047 PlotOrientation orientation = plot.getOrientation();
1048 Line2D line = null;
1049 if (orientation == PlotOrientation.HORIZONTAL) {
1050 line = new Line2D.Double(dataArea.getMinX(), v,
1051 dataArea.getMaxX(), v);
1052 }
1053 else if (orientation == PlotOrientation.VERTICAL) {
1054 line = new Line2D.Double(v, dataArea.getMinY(), v,
1055 dataArea.getMaxY());
1056 }
1057
1058 final Composite originalComposite = g2.getComposite();
1059 g2.setComposite(AlphaComposite.getInstance(
1060 AlphaComposite.SRC_OVER, marker.getAlpha()));
1061 g2.setPaint(marker.getPaint());
1062 g2.setStroke(marker.getStroke());
1063 g2.draw(line);
1064
1065 String label = marker.getLabel();
1066 RectangleAnchor anchor = marker.getLabelAnchor();
1067 if (label != null) {
1068 Font labelFont = marker.getLabelFont();
1069 g2.setFont(labelFont);
1070 g2.setPaint(marker.getLabelPaint());
1071 Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
1072 g2, orientation, dataArea, line.getBounds2D(),
1073 marker.getLabelOffset(),
1074 LengthAdjustmentType.EXPAND, anchor);
1075 TextUtilities.drawAlignedString(label, g2,
1076 (float) coordinates.getX(), (float) coordinates.getY(),
1077 marker.getLabelTextAnchor());
1078 }
1079 g2.setComposite(originalComposite);
1080 }
1081 else if (marker instanceof IntervalMarker) {
1082 IntervalMarker im = (IntervalMarker) marker;
1083 double start = im.getStartValue();
1084 double end = im.getEndValue();
1085 Range range = domainAxis.getRange();
1086 if (!(range.intersects(start, end))) {
1087 return;
1088 }
1089
1090 double start2d = domainAxis.valueToJava2D(start, dataArea,
1091 plot.getDomainAxisEdge());
1092 double end2d = domainAxis.valueToJava2D(end, dataArea,
1093 plot.getDomainAxisEdge());
1094 double low = Math.min(start2d, end2d);
1095 double high = Math.max(start2d, end2d);
1096
1097 PlotOrientation orientation = plot.getOrientation();
1098 Rectangle2D rect = null;
1099 if (orientation == PlotOrientation.HORIZONTAL) {
1100 // clip top and bottom bounds to data area
1101 low = Math.max(low, dataArea.getMinY());
1102 high = Math.min(high, dataArea.getMaxY());
1103 rect = new Rectangle2D.Double(dataArea.getMinX(),
1104 low, dataArea.getWidth(),
1105 high - low);
1106 }
1107 else if (orientation == PlotOrientation.VERTICAL) {
1108 // clip left and right bounds to data area
1109 low = Math.max(low, dataArea.getMinX());
1110 high = Math.min(high, dataArea.getMaxX());
1111 rect = new Rectangle2D.Double(low,
1112 dataArea.getMinY(), high - low,
1113 dataArea.getHeight());
1114 }
1115
1116 final Composite originalComposite = g2.getComposite();
1117 g2.setComposite(AlphaComposite.getInstance(
1118 AlphaComposite.SRC_OVER, marker.getAlpha()));
1119 Paint p = marker.getPaint();
1120 if (p instanceof GradientPaint) {
1121 GradientPaint gp = (GradientPaint) p;
1122 GradientPaintTransformer t = im.getGradientPaintTransformer();
1123 if (t != null) {
1124 gp = t.transform(gp, rect);
1125 }
1126 g2.setPaint(gp);
1127 }
1128 else {
1129 g2.setPaint(p);
1130 }
1131 g2.fill(rect);
1132
1133 // now draw the outlines, if visible...
1134 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) {
1135 if (orientation == PlotOrientation.VERTICAL) {
1136 Line2D line = new Line2D.Double();
1137 double y0 = dataArea.getMinY();
1138 double y1 = dataArea.getMaxY();
1139 g2.setPaint(im.getOutlinePaint());
1140 g2.setStroke(im.getOutlineStroke());
1141 if (range.contains(start)) {
1142 line.setLine(start2d, y0, start2d, y1);
1143 g2.draw(line);
1144 }
1145 if (range.contains(end)) {
1146 line.setLine(end2d, y0, end2d, y1);
1147 g2.draw(line);
1148 }
1149 }
1150 else { // PlotOrientation.HORIZONTAL
1151 Line2D line = new Line2D.Double();
1152 double x0 = dataArea.getMinX();
1153 double x1 = dataArea.getMaxX();
1154 g2.setPaint(im.getOutlinePaint());
1155 g2.setStroke(im.getOutlineStroke());
1156 if (range.contains(start)) {
1157 line.setLine(x0, start2d, x1, start2d);
1158 g2.draw(line);
1159 }
1160 if (range.contains(end)) {
1161 line.setLine(x0, end2d, x1, end2d);
1162 g2.draw(line);
1163 }
1164 }
1165 }
1166
1167 String label = marker.getLabel();
1168 RectangleAnchor anchor = marker.getLabelAnchor();
1169 if (label != null) {
1170 Font labelFont = marker.getLabelFont();
1171 g2.setFont(labelFont);
1172 g2.setPaint(marker.getLabelPaint());
1173 Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
1174 g2, orientation, dataArea, rect,
1175 marker.getLabelOffset(), marker.getLabelOffsetType(),
1176 anchor);
1177 TextUtilities.drawAlignedString(label, g2,
1178 (float) coordinates.getX(), (float) coordinates.getY(),
1179 marker.getLabelTextAnchor());
1180 }
1181 g2.setComposite(originalComposite);
1182
1183 }
1184
1185 }
1186
1187 /**
1188 * Calculates the (x, y) coordinates for drawing a marker label.
1189 *
1190 * @param g2 the graphics device.
1191 * @param orientation the plot orientation.
1192 * @param dataArea the data area.
1193 * @param markerArea the rectangle surrounding the marker area.
1194 * @param markerOffset the marker label offset.
1195 * @param labelOffsetType the label offset type.
1196 * @param anchor the label anchor.
1197 *
1198 * @return The coordinates for drawing the marker label.
1199 */
1200 protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2,
1201 PlotOrientation orientation,
1202 Rectangle2D dataArea,
1203 Rectangle2D markerArea,
1204 RectangleInsets markerOffset,
1205 LengthAdjustmentType labelOffsetType,
1206 RectangleAnchor anchor) {
1207
1208 Rectangle2D anchorRect = null;
1209 if (orientation == PlotOrientation.HORIZONTAL) {
1210 anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1211 LengthAdjustmentType.CONTRACT, labelOffsetType);
1212 }
1213 else if (orientation == PlotOrientation.VERTICAL) {
1214 anchorRect = markerOffset.createAdjustedRectangle(markerArea,
1215 labelOffsetType, LengthAdjustmentType.CONTRACT);
1216 }
1217 return RectangleAnchor.coordinates(anchorRect, anchor);
1218
1219 }
1220
1221 /**
1222 * Draws a horizontal line across the chart to represent a 'range marker'.
1223 *
1224 * @param g2 the graphics device.
1225 * @param plot the plot.
1226 * @param rangeAxis the range axis.
1227 * @param marker the marker line.
1228 * @param dataArea the axis data area.
1229 */
1230 public void drawRangeMarker(Graphics2D g2,
1231 XYPlot plot,
1232 ValueAxis rangeAxis,
1233 Marker marker,
1234 Rectangle2D dataArea) {
1235
1236 if (marker instanceof ValueMarker) {
1237 ValueMarker vm = (ValueMarker) marker;
1238 double value = vm.getValue();
1239 Range range = rangeAxis.getRange();
1240 if (!range.contains(value)) {
1241 return;
1242 }
1243
1244 double v = rangeAxis.valueToJava2D(value, dataArea,
1245 plot.getRangeAxisEdge());
1246 PlotOrientation orientation = plot.getOrientation();
1247 Line2D line = null;
1248 if (orientation == PlotOrientation.HORIZONTAL) {
1249 line = new Line2D.Double(v, dataArea.getMinY(), v,
1250 dataArea.getMaxY());
1251 }
1252 else if (orientation == PlotOrientation.VERTICAL) {
1253 line = new Line2D.Double(dataArea.getMinX(), v,
1254 dataArea.getMaxX(), v);
1255 }
1256
1257 final Composite originalComposite = g2.getComposite();
1258 g2.setComposite(AlphaComposite.getInstance(
1259 AlphaComposite.SRC_OVER, marker.getAlpha()));
1260 g2.setPaint(marker.getPaint());
1261 g2.setStroke(marker.getStroke());
1262 g2.draw(line);
1263
1264 String label = marker.getLabel();
1265 RectangleAnchor anchor = marker.getLabelAnchor();
1266 if (label != null) {
1267 Font labelFont = marker.getLabelFont();
1268 g2.setFont(labelFont);
1269 g2.setPaint(marker.getLabelPaint());
1270 Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
1271 g2, orientation, dataArea, line.getBounds2D(),
1272 marker.getLabelOffset(),
1273 LengthAdjustmentType.EXPAND, anchor);
1274 TextUtilities.drawAlignedString(label, g2,
1275 (float) coordinates.getX(), (float) coordinates.getY(),
1276 marker.getLabelTextAnchor());
1277 }
1278 g2.setComposite(originalComposite);
1279 }
1280 else if (marker instanceof IntervalMarker) {
1281 IntervalMarker im = (IntervalMarker) marker;
1282 double start = im.getStartValue();
1283 double end = im.getEndValue();
1284 Range range = rangeAxis.getRange();
1285 if (!(range.intersects(start, end))) {
1286 return;
1287 }
1288
1289 double start2d = rangeAxis.valueToJava2D(start, dataArea,
1290 plot.getRangeAxisEdge());
1291 double end2d = rangeAxis.valueToJava2D(end, dataArea,
1292 plot.getRangeAxisEdge());
1293 double low = Math.min(start2d, end2d);
1294 double high = Math.max(start2d, end2d);
1295
1296 PlotOrientation orientation = plot.getOrientation();
1297 Rectangle2D rect = null;
1298 if (orientation == PlotOrientation.HORIZONTAL) {
1299 // clip left and right bounds to data area
1300 low = Math.max(low, dataArea.getMinX());
1301 high = Math.min(high, dataArea.getMaxX());
1302 rect = new Rectangle2D.Double(low,
1303 dataArea.getMinY(), high - low,
1304 dataArea.getHeight());
1305 }
1306 else if (orientation == PlotOrientation.VERTICAL) {
1307 // clip top and bottom bounds to data area
1308 low = Math.max(low, dataArea.getMinY());
1309 high = Math.min(high, dataArea.getMaxY());
1310 rect = new Rectangle2D.Double(dataArea.getMinX(),
1311 low, dataArea.getWidth(),
1312 high - low);
1313 }
1314
1315 final Composite originalComposite = g2.getComposite();
1316 g2.setComposite(AlphaComposite.getInstance(
1317 AlphaComposite.SRC_OVER, marker.getAlpha()));
1318 Paint p = marker.getPaint();
1319 if (p instanceof GradientPaint) {
1320 GradientPaint gp = (GradientPaint) p;
1321 GradientPaintTransformer t = im.getGradientPaintTransformer();
1322 if (t != null) {
1323 gp = t.transform(gp, rect);
1324 }
1325 g2.setPaint(gp);
1326 }
1327 else {
1328 g2.setPaint(p);
1329 }
1330 g2.fill(rect);
1331
1332 // now draw the outlines, if visible...
1333 if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) {
1334 if (orientation == PlotOrientation.VERTICAL) {
1335 Line2D line = new Line2D.Double();
1336 double x0 = dataArea.getMinX();
1337 double x1 = dataArea.getMaxX();
1338 g2.setPaint(im.getOutlinePaint());
1339 g2.setStroke(im.getOutlineStroke());
1340 if (range.contains(start)) {
1341 line.setLine(x0, start2d, x1, start2d);
1342 g2.draw(line);
1343 }
1344 if (range.contains(end)) {
1345 line.setLine(x0, end2d, x1, end2d);
1346 g2.draw(line);
1347 }
1348 }
1349 else { // PlotOrientation.HORIZONTAL
1350 Line2D line = new Line2D.Double();
1351 double y0 = dataArea.getMinY();
1352 double y1 = dataArea.getMaxY();
1353 g2.setPaint(im.getOutlinePaint());
1354 g2.setStroke(im.getOutlineStroke());
1355 if (range.contains(start)) {
1356 line.setLine(start2d, y0, start2d, y1);
1357 g2.draw(line);
1358 }
1359 if (range.contains(end)) {
1360 line.setLine(end2d, y0, end2d, y1);
1361 g2.draw(line);
1362 }
1363 }
1364 }
1365
1366 String label = marker.getLabel();
1367 RectangleAnchor anchor = marker.getLabelAnchor();
1368 if (label != null) {
1369 Font labelFont = marker.getLabelFont();
1370