0001: /* ===========================================================
0002: * JFreeChart : a free chart library for the Java(tm) platform
0003: * ===========================================================
0004: *
0005: * (C) Copyright 2000-2007, by Object Refinery Limited and Contributors.
0006: *
0007: * Project Info: http://www.jfree.org/jfreechart/index.html
0008: *
0009: * This library is free software; you can redistribute it and/or modify it
0010: * under the terms of the GNU Lesser General Public License as published by
0011: * the Free Software Foundation; either version 2.1 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but
0015: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
0017: * License for more details.
0018: *
0019: * You should have received a copy of the GNU Lesser General Public
0020: * License along with this library; if not, write to the Free Software
0021: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
0022: * USA.
0023: *
0024: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0025: * in the United States and other countries.]
0026: *
0027: * ---------------------------
0028: * AbstractXYItemRenderer.java
0029: * ---------------------------
0030: * (C) Copyright 2002-2007, by Object Refinery Limited and Contributors.
0031: *
0032: * Original Author: David Gilbert (for Object Refinery Limited);
0033: * Contributor(s): Richard Atkinson;
0034: * Focus Computer Services Limited;
0035: * Tim Bardzil;
0036: * Sergei Ivanov;
0037: *
0038: * $Id: AbstractXYItemRenderer.java,v 1.26.2.19 2007/06/12 08:39:30 mungady Exp $
0039: *
0040: * Changes:
0041: * --------
0042: * 15-Mar-2002 : Version 1 (DG);
0043: * 09-Apr-2002 : Added a getToolTipGenerator() method reflecting the change in
0044: * the XYItemRenderer interface (DG);
0045: * 05-Aug-2002 : Added a urlGenerator member variable to support HTML image
0046: * maps (RA);
0047: * 20-Aug-2002 : Added property change events for the tooltip and URL
0048: * generators (DG);
0049: * 22-Aug-2002 : Moved property change support into AbstractRenderer class (DG);
0050: * 23-Sep-2002 : Fixed errors reported by Checkstyle tool (DG);
0051: * 18-Nov-2002 : Added methods for drawing grid lines (DG);
0052: * 17-Jan-2003 : Moved plot classes into a separate package (DG);
0053: * 25-Mar-2003 : Implemented Serializable (DG);
0054: * 01-May-2003 : Modified initialise() return type and drawItem() method
0055: * signature (DG);
0056: * 15-May-2003 : Modified to take into account the plot orientation (DG);
0057: * 21-May-2003 : Added labels to markers (DG);
0058: * 05-Jun-2003 : Added domain and range grid bands (sponsored by Focus Computer
0059: * Services Ltd) (DG);
0060: * 27-Jul-2003 : Added getRangeType() to support stacked XY area charts (RA);
0061: * 31-Jul-2003 : Deprecated all but the default constructor (DG);
0062: * 13-Aug-2003 : Implemented Cloneable (DG);
0063: * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
0064: * 29-Oct-2003 : Added workaround for font alignment in PDF output (DG);
0065: * 05-Nov-2003 : Fixed marker rendering bug (833623) (DG);
0066: * 11-Feb-2004 : Updated labelling for markers (DG);
0067: * 25-Feb-2004 : Added updateCrosshairValues() method. Moved deprecated code
0068: * to bottom of source file (DG);
0069: * 16-Apr-2004 : Added support for IntervalMarker in drawRangeMarker() method
0070: * - thanks to Tim Bardzil (DG);
0071: * 05-May-2004 : Fixed bug (948310) where interval markers extend beyond axis
0072: * range (DG);
0073: * 03-Jun-2004 : Fixed more bugs in drawing interval markers (DG);
0074: * 26-Aug-2004 : Added the addEntity() method (DG);
0075: * 29-Sep-2004 : Added annotation support (with layers) (DG);
0076: * 30-Sep-2004 : Moved drawRotatedString() from RefineryUtilities -->
0077: * TextUtilities (DG);
0078: * 06-Oct-2004 : Added findDomainBounds() method and renamed
0079: * getRangeExtent() --> findRangeBounds() (DG);
0080: * 07-Jan-2005 : Removed deprecated code (DG);
0081: * 27-Jan-2005 : Modified getLegendItem() to omit hidden series (DG);
0082: * 24-Feb-2005 : Added getLegendItems() method (DG);
0083: * 08-Mar-2005 : Fixed positioning of marker labels (DG);
0084: * 20-Apr-2005 : Renamed XYLabelGenerator --> XYItemLabelGenerator and
0085: * added generators for legend labels, tooltips and URLs (DG);
0086: * 01-Jun-2005 : Handle one dimension of the marker label adjustment
0087: * automatically (DG);
0088: * ------------- JFREECHART 1.0.x ---------------------------------------------
0089: * 20-Jul-2006 : Set dataset and series indices in LegendItem (DG);
0090: * 24-Oct-2006 : Respect alpha setting in markers (see patch 1567843 by Sergei
0091: * Ivanov) (DG);
0092: * 24-Oct-2006 : Added code to draw outlines for interval markers (DG);
0093: * 24-Nov-2006 : Fixed cloning for legend item generators (DG);
0094: * 06-Feb-2007 : Added new updateCrosshairValues() method that takes into
0095: * account multiple axis plots (see bug 1086307) (DG);
0096: * 20-Feb-2007 : Fixed equals() method implementation (DG);
0097: * 01-Mar-2007 : Fixed interval marker drawing (patch 1670686 thanks to
0098: * Sergei Ivanov) (DG);
0099: * 22-Mar-2007 : Modified the tool tip generator look up (DG);
0100: * 23-Mar-2007 : Added drawDomainLine() method (DG);
0101: * 20-Apr-2007 : Updated getLegendItem() for renderer change, and deprecated
0102: * itemLabelGenerator and toolTipGenerator override fields (DG);
0103: * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
0104: *
0105: */
0106:
0107: package org.jfree.chart.renderer.xy;
0108:
0109: import java.awt.AlphaComposite;
0110: import java.awt.Composite;
0111: import java.awt.Font;
0112: import java.awt.GradientPaint;
0113: import java.awt.Graphics2D;
0114: import java.awt.Paint;
0115: import java.awt.Shape;
0116: import java.awt.Stroke;
0117: import java.awt.geom.Ellipse2D;
0118: import java.awt.geom.Line2D;
0119: import java.awt.geom.Point2D;
0120: import java.awt.geom.Rectangle2D;
0121: import java.io.Serializable;
0122: import java.util.Iterator;
0123: import java.util.List;
0124:
0125: import org.jfree.chart.LegendItem;
0126: import org.jfree.chart.LegendItemCollection;
0127: import org.jfree.chart.annotations.XYAnnotation;
0128: import org.jfree.chart.axis.ValueAxis;
0129: import org.jfree.chart.entity.EntityCollection;
0130: import org.jfree.chart.entity.XYItemEntity;
0131: import org.jfree.chart.event.RendererChangeEvent;
0132: import org.jfree.chart.labels.ItemLabelPosition;
0133: import org.jfree.chart.labels.StandardXYSeriesLabelGenerator;
0134: import org.jfree.chart.labels.XYItemLabelGenerator;
0135: import org.jfree.chart.labels.XYSeriesLabelGenerator;
0136: import org.jfree.chart.labels.XYToolTipGenerator;
0137: import org.jfree.chart.plot.CrosshairState;
0138: import org.jfree.chart.plot.DrawingSupplier;
0139: import org.jfree.chart.plot.IntervalMarker;
0140: import org.jfree.chart.plot.Marker;
0141: import org.jfree.chart.plot.Plot;
0142: import org.jfree.chart.plot.PlotOrientation;
0143: import org.jfree.chart.plot.PlotRenderingInfo;
0144: import org.jfree.chart.plot.ValueMarker;
0145: import org.jfree.chart.plot.XYPlot;
0146: import org.jfree.chart.renderer.AbstractRenderer;
0147: import org.jfree.chart.urls.XYURLGenerator;
0148: import org.jfree.data.Range;
0149: import org.jfree.data.general.DatasetUtilities;
0150: import org.jfree.data.xy.XYDataset;
0151: import org.jfree.text.TextUtilities;
0152: import org.jfree.ui.GradientPaintTransformer;
0153: import org.jfree.ui.Layer;
0154: import org.jfree.ui.LengthAdjustmentType;
0155: import org.jfree.ui.RectangleAnchor;
0156: import org.jfree.ui.RectangleInsets;
0157: import org.jfree.util.ObjectList;
0158: import org.jfree.util.ObjectUtilities;
0159: import org.jfree.util.PublicCloneable;
0160:
0161: /**
0162: * A base class that can be used to create new {@link XYItemRenderer}
0163: * implementations.
0164: */
0165: public abstract class AbstractXYItemRenderer extends AbstractRenderer
0166: implements XYItemRenderer, Cloneable, Serializable {
0167:
0168: /** For serialization. */
0169: private static final long serialVersionUID = 8019124836026607990L;
0170:
0171: /** The plot. */
0172: private XYPlot plot;
0173:
0174: /**
0175: * The item label generator for ALL series.
0176: *
0177: * @deprecated This field is redundant, use itemLabelGeneratorList and
0178: * baseItemLabelGenerator instead. Deprecated as of version 1.0.6.
0179: */
0180: private XYItemLabelGenerator itemLabelGenerator;
0181:
0182: /** A list of item label generators (one per series). */
0183: private ObjectList itemLabelGeneratorList;
0184:
0185: /** The base item label generator. */
0186: private XYItemLabelGenerator baseItemLabelGenerator;
0187:
0188: /**
0189: * The tool tip generator for ALL series.
0190: *
0191: * @deprecated This field is redundant, use tooltipGeneratorList and
0192: * baseToolTipGenerator instead. Deprecated as of version 1.0.6.
0193: */
0194: private XYToolTipGenerator toolTipGenerator;
0195:
0196: /** A list of tool tip generators (one per series). */
0197: private ObjectList toolTipGeneratorList;
0198:
0199: /** The base tool tip generator. */
0200: private XYToolTipGenerator baseToolTipGenerator;
0201:
0202: /** The URL text generator. */
0203: private XYURLGenerator urlGenerator;
0204:
0205: /**
0206: * Annotations to be drawn in the background layer ('underneath' the data
0207: * items).
0208: */
0209: private List backgroundAnnotations;
0210:
0211: /**
0212: * Annotations to be drawn in the foreground layer ('on top' of the data
0213: * items).
0214: */
0215: private List foregroundAnnotations;
0216:
0217: /** The default radius for the entity 'hotspot' */
0218: private int defaultEntityRadius;
0219:
0220: /** The legend item label generator. */
0221: private XYSeriesLabelGenerator legendItemLabelGenerator;
0222:
0223: /** The legend item tool tip generator. */
0224: private XYSeriesLabelGenerator legendItemToolTipGenerator;
0225:
0226: /** The legend item URL generator. */
0227: private XYSeriesLabelGenerator legendItemURLGenerator;
0228:
0229: /**
0230: * Creates a renderer where the tooltip generator and the URL generator are
0231: * both <code>null</code>.
0232: */
0233: protected AbstractXYItemRenderer() {
0234: super ();
0235: this .itemLabelGenerator = null;
0236: this .itemLabelGeneratorList = new ObjectList();
0237: this .toolTipGenerator = null;
0238: this .toolTipGeneratorList = new ObjectList();
0239: this .urlGenerator = null;
0240: this .backgroundAnnotations = new java.util.ArrayList();
0241: this .foregroundAnnotations = new java.util.ArrayList();
0242: this .defaultEntityRadius = 3;
0243: this .legendItemLabelGenerator = new StandardXYSeriesLabelGenerator(
0244: "{0}");
0245: }
0246:
0247: /**
0248: * Returns the number of passes through the data that the renderer requires
0249: * in order to draw the chart. Most charts will require a single pass, but
0250: * some require two passes.
0251: *
0252: * @return The pass count.
0253: */
0254: public int getPassCount() {
0255: return 1;
0256: }
0257:
0258: /**
0259: * Returns the plot that the renderer is assigned to.
0260: *
0261: * @return The plot (possibly <code>null</code>).
0262: */
0263: public XYPlot getPlot() {
0264: return this .plot;
0265: }
0266:
0267: /**
0268: * Sets the plot that the renderer is assigned to.
0269: *
0270: * @param plot the plot (<code>null</code> permitted).
0271: */
0272: public void setPlot(XYPlot plot) {
0273: this .plot = plot;
0274: }
0275:
0276: /**
0277: * Initialises the renderer and returns a state object that should be
0278: * passed to all subsequent calls to the drawItem() method.
0279: * <P>
0280: * This method will be called before the first item is rendered, giving the
0281: * renderer an opportunity to initialise any state information it wants to
0282: * maintain. The renderer can do nothing if it chooses.
0283: *
0284: * @param g2 the graphics device.
0285: * @param dataArea the area inside the axes.
0286: * @param plot the plot.
0287: * @param data the data.
0288: * @param info an optional info collection object to return data back to
0289: * the caller.
0290: *
0291: * @return The renderer state (never <code>null</code>).
0292: */
0293: public XYItemRendererState initialise(Graphics2D g2,
0294: Rectangle2D dataArea, XYPlot plot, XYDataset data,
0295: PlotRenderingInfo info) {
0296:
0297: XYItemRendererState state = new XYItemRendererState(info);
0298: return state;
0299:
0300: }
0301:
0302: // ITEM LABEL GENERATOR
0303:
0304: /**
0305: * Returns the label generator for a data item. This implementation simply
0306: * passes control to the {@link #getSeriesItemLabelGenerator(int)} method.
0307: * If, for some reason, you want a different generator for individual
0308: * items, you can override this method.
0309: *
0310: * @param series the series index (zero based).
0311: * @param item the item index (zero based).
0312: *
0313: * @return The generator (possibly <code>null</code>).
0314: */
0315: public XYItemLabelGenerator getItemLabelGenerator(int series,
0316: int item) {
0317: // return the generator for ALL series, if there is one...
0318: if (this .itemLabelGenerator != null) {
0319: return this .itemLabelGenerator;
0320: }
0321:
0322: // otherwise look up the generator table
0323: XYItemLabelGenerator generator = (XYItemLabelGenerator) this .itemLabelGeneratorList
0324: .get(series);
0325: if (generator == null) {
0326: generator = this .baseItemLabelGenerator;
0327: }
0328: return generator;
0329: }
0330:
0331: /**
0332: * Returns the item label generator for a series.
0333: *
0334: * @param series the series index (zero based).
0335: *
0336: * @return The generator (possibly <code>null</code>).
0337: */
0338: public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) {
0339: return (XYItemLabelGenerator) this .itemLabelGeneratorList
0340: .get(series);
0341: }
0342:
0343: /**
0344: * Returns the item label generator override.
0345: *
0346: * @return The generator (possibly <code>null</code>).
0347: *
0348: * @since 1.0.5
0349: *
0350: * @see #setItemLabelGenerator(XYItemLabelGenerator)
0351: *
0352: * @deprecated As of version 1.0.6, this override setting should not be
0353: * used. You can use the base setting instead
0354: * ({@link #getBaseItemLabelGenerator()}).
0355: */
0356: public XYItemLabelGenerator getItemLabelGenerator() {
0357: return this .itemLabelGenerator;
0358: }
0359:
0360: /**
0361: * Sets the item label generator for ALL series and sends a
0362: * {@link RendererChangeEvent} to all registered listeners.
0363: *
0364: * @param generator the generator (<code>null</code> permitted).
0365: *
0366: * @see #getItemLabelGenerator()
0367: *
0368: * @deprecated As of version 1.0.6, this override setting should not be
0369: * used. You can use the base setting instead
0370: * ({@link #setBaseItemLabelGenerator(XYItemLabelGenerator)}).
0371: */
0372: public void setItemLabelGenerator(XYItemLabelGenerator generator) {
0373: this .itemLabelGenerator = generator;
0374: notifyListeners(new RendererChangeEvent(this ));
0375: }
0376:
0377: /**
0378: * Sets the item label generator for a series and sends a
0379: * {@link RendererChangeEvent} to all registered listeners.
0380: *
0381: * @param series the series index (zero based).
0382: * @param generator the generator (<code>null</code> permitted).
0383: */
0384: public void setSeriesItemLabelGenerator(int series,
0385: XYItemLabelGenerator generator) {
0386: this .itemLabelGeneratorList.set(series, generator);
0387: notifyListeners(new RendererChangeEvent(this ));
0388: }
0389:
0390: /**
0391: * Returns the base item label generator.
0392: *
0393: * @return The generator (possibly <code>null</code>).
0394: */
0395: public XYItemLabelGenerator getBaseItemLabelGenerator() {
0396: return this .baseItemLabelGenerator;
0397: }
0398:
0399: /**
0400: * Sets the base item label generator and sends a
0401: * {@link RendererChangeEvent} to all registered listeners.
0402: *
0403: * @param generator the generator (<code>null</code> permitted).
0404: */
0405: public void setBaseItemLabelGenerator(XYItemLabelGenerator generator) {
0406: this .baseItemLabelGenerator = generator;
0407: notifyListeners(new RendererChangeEvent(this ));
0408: }
0409:
0410: // TOOL TIP GENERATOR
0411:
0412: /**
0413: * Returns the tool tip generator for a data item. If, for some reason,
0414: * you want a different generator for individual items, you can override
0415: * this method.
0416: *
0417: * @param series the series index (zero based).
0418: * @param item the item index (zero based).
0419: *
0420: * @return The generator (possibly <code>null</code>).
0421: */
0422: public XYToolTipGenerator getToolTipGenerator(int series, int item) {
0423: // return the generator for ALL series, if there is one...
0424: if (this .toolTipGenerator != null) {
0425: return this .toolTipGenerator;
0426: }
0427:
0428: // otherwise look up the generator table
0429: XYToolTipGenerator generator = (XYToolTipGenerator) this .toolTipGeneratorList
0430: .get(series);
0431: if (generator == null) {
0432: generator = this .baseToolTipGenerator;
0433: }
0434: return generator;
0435: }
0436:
0437: /**
0438: * Returns the override tool tip generator.
0439: *
0440: * @return The tool tip generator (possible <code>null</code>).
0441: *
0442: * @since 1.0.5
0443: *
0444: * @see #setToolTipGenerator(XYToolTipGenerator)
0445: *
0446: * @deprecated As of version 1.0.6, this override setting should not be
0447: * used. You can use the base setting instead
0448: * ({@link #getBaseToolTipGenerator()}).
0449: */
0450: public XYToolTipGenerator getToolTipGenerator() {
0451: return this .toolTipGenerator;
0452: }
0453:
0454: /**
0455: * Sets the tool tip generator for ALL series and sends a
0456: * {@link RendererChangeEvent} to all registered listeners.
0457: *
0458: * @param generator the generator (<code>null</code> permitted).
0459: *
0460: * @see #getToolTipGenerator()
0461: *
0462: * @deprecated As of version 1.0.6, this override setting should not be
0463: * used. You can use the base setting instead
0464: * ({@link #setBaseToolTipGenerator(XYToolTipGenerator)}).
0465: */
0466: public void setToolTipGenerator(XYToolTipGenerator generator) {
0467: this .toolTipGenerator = generator;
0468: notifyListeners(new RendererChangeEvent(this ));
0469: }
0470:
0471: /**
0472: * Returns the tool tip generator for a series.
0473: *
0474: * @param series the series index (zero based).
0475: *
0476: * @return The generator (possibly <code>null</code>).
0477: */
0478: public XYToolTipGenerator getSeriesToolTipGenerator(int series) {
0479: return (XYToolTipGenerator) this .toolTipGeneratorList
0480: .get(series);
0481: }
0482:
0483: /**
0484: * Sets the tool tip generator for a series and sends a
0485: * {@link RendererChangeEvent} to all registered listeners.
0486: *
0487: * @param series the series index (zero based).
0488: * @param generator the generator (<code>null</code> permitted).
0489: */
0490: public void setSeriesToolTipGenerator(int series,
0491: XYToolTipGenerator generator) {
0492: this .toolTipGeneratorList.set(series, generator);
0493: notifyListeners(new RendererChangeEvent(this ));
0494: }
0495:
0496: /**
0497: * Returns the base tool tip generator.
0498: *
0499: * @return The generator (possibly <code>null</code>).
0500: *
0501: * @see #setBaseToolTipGenerator(XYToolTipGenerator)
0502: */
0503: public XYToolTipGenerator getBaseToolTipGenerator() {
0504: return this .baseToolTipGenerator;
0505: }
0506:
0507: /**
0508: * Sets the base tool tip generator and sends a {@link RendererChangeEvent}
0509: * to all registered listeners.
0510: *
0511: * @param generator the generator (<code>null</code> permitted).
0512: *
0513: * @see #getBaseToolTipGenerator()
0514: */
0515: public void setBaseToolTipGenerator(XYToolTipGenerator generator) {
0516: this .baseToolTipGenerator = generator;
0517: notifyListeners(new RendererChangeEvent(this ));
0518: }
0519:
0520: // URL GENERATOR
0521:
0522: /**
0523: * Returns the URL generator for HTML image maps.
0524: *
0525: * @return The URL generator (possibly <code>null</code>).
0526: */
0527: public XYURLGenerator getURLGenerator() {
0528: return this .urlGenerator;
0529: }
0530:
0531: /**
0532: * Sets the URL generator for HTML image maps.
0533: *
0534: * @param urlGenerator the URL generator (<code>null</code> permitted).
0535: */
0536: public void setURLGenerator(XYURLGenerator urlGenerator) {
0537: this .urlGenerator = urlGenerator;
0538: notifyListeners(new RendererChangeEvent(this ));
0539: }
0540:
0541: /**
0542: * Adds an annotation and sends a {@link RendererChangeEvent} to all
0543: * registered listeners. The annotation is added to the foreground
0544: * layer.
0545: *
0546: * @param annotation the annotation (<code>null</code> not permitted).
0547: */
0548: public void addAnnotation(XYAnnotation annotation) {
0549: // defer argument checking
0550: addAnnotation(annotation, Layer.FOREGROUND);
0551: }
0552:
0553: /**
0554: * Adds an annotation to the specified layer.
0555: *
0556: * @param annotation the annotation (<code>null</code> not permitted).
0557: * @param layer the layer (<code>null</code> not permitted).
0558: */
0559: public void addAnnotation(XYAnnotation annotation, Layer layer) {
0560: if (annotation == null) {
0561: throw new IllegalArgumentException(
0562: "Null 'annotation' argument.");
0563: }
0564: if (layer.equals(Layer.FOREGROUND)) {
0565: this .foregroundAnnotations.add(annotation);
0566: notifyListeners(new RendererChangeEvent(this ));
0567: } else if (layer.equals(Layer.BACKGROUND)) {
0568: this .backgroundAnnotations.add(annotation);
0569: notifyListeners(new RendererChangeEvent(this ));
0570: } else {
0571: // should never get here
0572: throw new RuntimeException("Unknown layer.");
0573: }
0574: }
0575:
0576: /**
0577: * Removes the specified annotation and sends a {@link RendererChangeEvent}
0578: * to all registered listeners.
0579: *
0580: * @param annotation the annotation to remove (<code>null</code> not
0581: * permitted).
0582: *
0583: * @return A boolean to indicate whether or not the annotation was
0584: * successfully removed.
0585: */
0586: public boolean removeAnnotation(XYAnnotation annotation) {
0587: boolean removed = this .foregroundAnnotations.remove(annotation);
0588: removed = removed
0589: & this .backgroundAnnotations.remove(annotation);
0590: notifyListeners(new RendererChangeEvent(this ));
0591: return removed;
0592: }
0593:
0594: /**
0595: * Removes all annotations and sends a {@link RendererChangeEvent}
0596: * to all registered listeners.
0597: */
0598: public void removeAnnotations() {
0599: this .foregroundAnnotations.clear();
0600: this .backgroundAnnotations.clear();
0601: notifyListeners(new RendererChangeEvent(this ));
0602: }
0603:
0604: /**
0605: * Returns the radius of the circle used for the default entity area
0606: * when no area is specified.
0607: *
0608: * @return A radius.
0609: */
0610: public int getDefaultEntityRadius() {
0611: return this .defaultEntityRadius;
0612: }
0613:
0614: /**
0615: * Sets the radius of the circle used for the default entity area
0616: * when no area is specified.
0617: *
0618: * @param radius the radius.
0619: */
0620: public void setDefaultEntityRadius(int radius) {
0621: this .defaultEntityRadius = radius;
0622: }
0623:
0624: /**
0625: * Returns the legend item label generator.
0626: *
0627: * @return The label generator (never <code>null</code>).
0628: *
0629: * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator)
0630: */
0631: public XYSeriesLabelGenerator getLegendItemLabelGenerator() {
0632: return this .legendItemLabelGenerator;
0633: }
0634:
0635: /**
0636: * Sets the legend item label generator and sends a
0637: * {@link RendererChangeEvent} to all registered listeners.
0638: *
0639: * @param generator the generator (<code>null</code> not permitted).
0640: *
0641: * @see #getLegendItemLabelGenerator()
0642: */
0643: public void setLegendItemLabelGenerator(
0644: XYSeriesLabelGenerator generator) {
0645: if (generator == null) {
0646: throw new IllegalArgumentException(
0647: "Null 'generator' argument.");
0648: }
0649: this .legendItemLabelGenerator = generator;
0650: notifyListeners(new RendererChangeEvent(this ));
0651: }
0652:
0653: /**
0654: * Returns the legend item tool tip generator.
0655: *
0656: * @return The tool tip generator (possibly <code>null</code>).
0657: *
0658: * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator)
0659: */
0660: public XYSeriesLabelGenerator getLegendItemToolTipGenerator() {
0661: return this .legendItemToolTipGenerator;
0662: }
0663:
0664: /**
0665: * Sets the legend item tool tip generator and sends a
0666: * {@link RendererChangeEvent} to all registered listeners.
0667: *
0668: * @param generator the generator (<code>null</code> permitted).
0669: *
0670: * @see #getLegendItemToolTipGenerator()
0671: */
0672: public void setLegendItemToolTipGenerator(
0673: XYSeriesLabelGenerator generator) {
0674: this .legendItemToolTipGenerator = generator;
0675: notifyListeners(new RendererChangeEvent(this ));
0676: }
0677:
0678: /**
0679: * Returns the legend item URL generator.
0680: *
0681: * @return The URL generator (possibly <code>null</code>).
0682: *
0683: * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator)
0684: */
0685: public XYSeriesLabelGenerator getLegendItemURLGenerator() {
0686: return this .legendItemURLGenerator;
0687: }
0688:
0689: /**
0690: * Sets the legend item URL generator and sends a
0691: * {@link RendererChangeEvent} to all registered listeners.
0692: *
0693: * @param generator the generator (<code>null</code> permitted).
0694: *
0695: * @see #getLegendItemURLGenerator()
0696: */
0697: public void setLegendItemURLGenerator(
0698: XYSeriesLabelGenerator generator) {
0699: this .legendItemURLGenerator = generator;
0700: notifyListeners(new RendererChangeEvent(this ));
0701: }
0702:
0703: /**
0704: * Returns the lower and upper bounds (range) of the x-values in the
0705: * specified dataset.
0706: *
0707: * @param dataset the dataset (<code>null</code> permitted).
0708: *
0709: * @return The range (<code>null</code> if the dataset is <code>null</code>
0710: * or empty).
0711: */
0712: public Range findDomainBounds(XYDataset dataset) {
0713: if (dataset != null) {
0714: return DatasetUtilities.findDomainBounds(dataset, false);
0715: } else {
0716: return null;
0717: }
0718: }
0719:
0720: /**
0721: * Returns the range of values the renderer requires to display all the
0722: * items from the specified dataset.
0723: *
0724: * @param dataset the dataset (<code>null</code> permitted).
0725: *
0726: * @return The range (<code>null</code> if the dataset is <code>null</code>
0727: * or empty).
0728: */
0729: public Range findRangeBounds(XYDataset dataset) {
0730: if (dataset != null) {
0731: return DatasetUtilities.findRangeBounds(dataset, false);
0732: } else {
0733: return null;
0734: }
0735: }
0736:
0737: /**
0738: * Returns a (possibly empty) collection of legend items for the series
0739: * that this renderer is responsible for drawing.
0740: *
0741: * @return The legend item collection (never <code>null</code>).
0742: */
0743: public LegendItemCollection getLegendItems() {
0744: if (this .plot == null) {
0745: return new LegendItemCollection();
0746: }
0747: LegendItemCollection result = new LegendItemCollection();
0748: int index = this .plot.getIndexOf(this );
0749: XYDataset dataset = this .plot.getDataset(index);
0750: if (dataset != null) {
0751: int seriesCount = dataset.getSeriesCount();
0752: for (int i = 0; i < seriesCount; i++) {
0753: if (isSeriesVisibleInLegend(i)) {
0754: LegendItem item = getLegendItem(index, i);
0755: if (item != null) {
0756: result.add(item);
0757: }
0758: }
0759: }
0760:
0761: }
0762: return result;
0763: }
0764:
0765: /**
0766: * Returns a default legend item for the specified series. Subclasses
0767: * should override this method to generate customised items.
0768: *
0769: * @param datasetIndex the dataset index (zero-based).
0770: * @param series the series index (zero-based).
0771: *
0772: * @return A legend item for the series.
0773: */
0774: public LegendItem getLegendItem(int datasetIndex, int series) {
0775: LegendItem result = null;
0776: XYPlot xyplot = getPlot();
0777: if (xyplot != null) {
0778: XYDataset dataset = xyplot.getDataset(datasetIndex);
0779: if (dataset != null) {
0780: String label = this .legendItemLabelGenerator
0781: .generateLabel(dataset, series);
0782: String description = label;
0783: String toolTipText = null;
0784: if (getLegendItemToolTipGenerator() != null) {
0785: toolTipText = getLegendItemToolTipGenerator()
0786: .generateLabel(dataset, series);
0787: }
0788: String urlText = null;
0789: if (getLegendItemURLGenerator() != null) {
0790: urlText = getLegendItemURLGenerator()
0791: .generateLabel(dataset, series);
0792: }
0793: Shape shape = lookupSeriesShape(series);
0794: Paint paint = lookupSeriesPaint(series);
0795: Paint outlinePaint = lookupSeriesOutlinePaint(series);
0796: Stroke outlineStroke = lookupSeriesOutlineStroke(series);
0797: result = new LegendItem(label, description,
0798: toolTipText, urlText, shape, paint,
0799: outlineStroke, outlinePaint);
0800: result.setSeriesKey(dataset.getSeriesKey(series));
0801: result.setSeriesIndex(series);
0802: result.setDataset(dataset);
0803: result.setDatasetIndex(datasetIndex);
0804: }
0805: }
0806: return result;
0807: }
0808:
0809: /**
0810: * Fills a band between two values on the axis. This can be used to color
0811: * bands between the grid lines.
0812: *
0813: * @param g2 the graphics device.
0814: * @param plot the plot.
0815: * @param axis the domain axis.
0816: * @param dataArea the data area.
0817: * @param start the start value.
0818: * @param end the end value.
0819: */
0820: public void fillDomainGridBand(Graphics2D g2, XYPlot plot,
0821: ValueAxis axis, Rectangle2D dataArea, double start,
0822: double end) {
0823:
0824: double x1 = axis.valueToJava2D(start, dataArea, plot
0825: .getDomainAxisEdge());
0826: double x2 = axis.valueToJava2D(end, dataArea, plot
0827: .getDomainAxisEdge());
0828: // TODO: need to change the next line to take account of plot
0829: // orientation...
0830: Rectangle2D band = new Rectangle2D.Double(x1, dataArea
0831: .getMinY(), x2 - x1, dataArea.getMaxY()
0832: - dataArea.getMinY());
0833: Paint paint = plot.getDomainTickBandPaint();
0834:
0835: if (paint != null) {
0836: g2.setPaint(paint);
0837: g2.fill(band);
0838: }
0839:
0840: }
0841:
0842: /**
0843: * Fills a band between two values on the range axis. This can be used to
0844: * color bands between the grid lines.
0845: *
0846: * @param g2 the graphics device.
0847: * @param plot the plot.
0848: * @param axis the range axis.
0849: * @param dataArea the data area.
0850: * @param start the start value.
0851: * @param end the end value.
0852: */
0853: public void fillRangeGridBand(Graphics2D g2, XYPlot plot,
0854: ValueAxis axis, Rectangle2D dataArea, double start,
0855: double end) {
0856:
0857: double y1 = axis.valueToJava2D(start, dataArea, plot
0858: .getRangeAxisEdge());
0859: double y2 = axis.valueToJava2D(end, dataArea, plot
0860: .getRangeAxisEdge());
0861: // TODO: need to change the next line to take account of the plot
0862: // orientation
0863: Rectangle2D band = new Rectangle2D.Double(dataArea.getMinX(),
0864: y2, dataArea.getWidth(), y1 - y2);
0865: Paint paint = plot.getRangeTickBandPaint();
0866:
0867: if (paint != null) {
0868: g2.setPaint(paint);
0869: g2.fill(band);
0870: }
0871:
0872: }
0873:
0874: /**
0875: * Draws a grid line against the range axis.
0876: *
0877: * @param g2 the graphics device.
0878: * @param plot the plot.
0879: * @param axis the value axis.
0880: * @param dataArea the area for plotting data (not yet adjusted for any
0881: * 3D effect).
0882: * @param value the value at which the grid line should be drawn.
0883: */
0884: public void drawDomainGridLine(Graphics2D g2, XYPlot plot,
0885: ValueAxis axis, Rectangle2D dataArea, double value) {
0886:
0887: Range range = axis.getRange();
0888: if (!range.contains(value)) {
0889: return;
0890: }
0891:
0892: PlotOrientation orientation = plot.getOrientation();
0893: double v = axis.valueToJava2D(value, dataArea, plot
0894: .getDomainAxisEdge());
0895: Line2D line = null;
0896: if (orientation == PlotOrientation.HORIZONTAL) {
0897: line = new Line2D.Double(dataArea.getMinX(), v, dataArea
0898: .getMaxX(), v);
0899: } else if (orientation == PlotOrientation.VERTICAL) {
0900: line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea
0901: .getMaxY());
0902: }
0903:
0904: Paint paint = plot.getDomainGridlinePaint();
0905: Stroke stroke = plot.getDomainGridlineStroke();
0906: g2.setPaint(paint != null ? paint : Plot.DEFAULT_OUTLINE_PAINT);
0907: g2.setStroke(stroke != null ? stroke
0908: : Plot.DEFAULT_OUTLINE_STROKE);
0909: g2.draw(line);
0910:
0911: }
0912:
0913: /**
0914: * Draws a line perpendicular to the domain axis.
0915: *
0916: * @param g2 the graphics device.
0917: * @param plot the plot.
0918: * @param axis the value axis.
0919: * @param dataArea the area for plotting data (not yet adjusted for any 3D
0920: * effect).
0921: * @param value the value at which the grid line should be drawn.
0922: * @param paint the paint.
0923: * @param stroke the stroke.
0924: *
0925: * @since 1.0.5
0926: */
0927: public void drawDomainLine(Graphics2D g2, XYPlot plot,
0928: ValueAxis axis, Rectangle2D dataArea, double value,
0929: Paint paint, Stroke stroke) {
0930:
0931: Range range = axis.getRange();
0932: if (!range.contains(value)) {
0933: return;
0934: }
0935:
0936: PlotOrientation orientation = plot.getOrientation();
0937: Line2D line = null;
0938: double v = axis.valueToJava2D(value, dataArea, plot
0939: .getDomainAxisEdge());
0940: if (orientation == PlotOrientation.HORIZONTAL) {
0941: line = new Line2D.Double(dataArea.getMinX(), v, dataArea
0942: .getMaxX(), v);
0943: } else if (orientation == PlotOrientation.VERTICAL) {
0944: line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea
0945: .getMaxY());
0946: }
0947:
0948: g2.setPaint(paint);
0949: g2.setStroke(stroke);
0950: g2.draw(line);
0951:
0952: }
0953:
0954: /**
0955: * Draws a line perpendicular to the range axis.
0956: *
0957: * @param g2 the graphics device.
0958: * @param plot the plot.
0959: * @param axis the value axis.
0960: * @param dataArea the area for plotting data (not yet adjusted for any 3D
0961: * effect).
0962: * @param value the value at which the grid line should be drawn.
0963: * @param paint the paint.
0964: * @param stroke the stroke.
0965: */
0966: public void drawRangeLine(Graphics2D g2, XYPlot plot,
0967: ValueAxis axis, Rectangle2D dataArea, double value,
0968: Paint paint, Stroke stroke) {
0969:
0970: Range range = axis.getRange();
0971: if (!range.contains(value)) {
0972: return;
0973: }
0974:
0975: PlotOrientation orientation = plot.getOrientation();
0976: Line2D line = null;
0977: double v = axis.valueToJava2D(value, dataArea, plot
0978: .getRangeAxisEdge());
0979: if (orientation == PlotOrientation.HORIZONTAL) {
0980: line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea
0981: .getMaxY());
0982: } else if (orientation == PlotOrientation.VERTICAL) {
0983: line = new Line2D.Double(dataArea.getMinX(), v, dataArea
0984: .getMaxX(), v);
0985: }
0986:
0987: g2.setPaint(paint);
0988: g2.setStroke(stroke);
0989: g2.draw(line);
0990:
0991: }
0992:
0993: /**
0994: * Draws a vertical line on the chart to represent a 'range marker'.
0995: *
0996: * @param g2 the graphics device.
0997: * @param plot the plot.
0998: * @param domainAxis the domain axis.
0999: * @param marker the marker line.
1000: * @param dataArea the axis data area.
1001: */
1002: public void drawDomainMarker(Graphics2D g2, XYPlot plot,
1003: ValueAxis domainAxis, Marker marker, Rectangle2D dataArea) {
1004:
1005: if (marker instanceof ValueMarker) {
1006: ValueMarker vm = (ValueMarker) marker;
1007: double value = vm.getValue();
1008: Range range = domainAxis.getRange();
1009: if (!range.contains(value)) {
1010: return;
1011: }
1012:
1013: double v = domainAxis.valueToJava2D(value, dataArea, plot
1014: .getDomainAxisEdge());
1015:
1016: PlotOrientation orientation = plot.getOrientation();
1017: Line2D line = null;
1018: if (orientation == PlotOrientation.HORIZONTAL) {
1019: line = new Line2D.Double(dataArea.getMinX(), v,
1020: dataArea.getMaxX(), v);
1021: } else if (orientation == PlotOrientation.VERTICAL) {
1022: line = new Line2D.Double(v, dataArea.getMinY(), v,
1023: dataArea.getMaxY());
1024: }
1025:
1026: final Composite originalComposite = g2.getComposite();
1027: g2.setComposite(AlphaComposite.getInstance(
1028: AlphaComposite.SRC_OVER, marker.getAlpha()));
1029: g2.setPaint(marker.getPaint());
1030: g2.setStroke(marker.getStroke());
1031: g2.draw(line);
1032:
1033: String label = marker.getLabel();
1034: RectangleAnchor anchor = marker.getLabelAnchor();
1035: if (label != null) {
1036: Font labelFont = marker.getLabelFont();
1037: g2.setFont(labelFont);
1038: g2.setPaint(marker.getLabelPaint());
1039: Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
1040: g2, orientation, dataArea, line.getBounds2D(),
1041: marker.getLabelOffset(),
1042: LengthAdjustmentType.EXPAND, anchor);
1043: TextUtilities.drawAlignedString(label, g2,
1044: (float) coordinates.getX(), (float) coordinates
1045: .getY(), marker.getLabelTextAnchor());
1046: }
1047: g2.setComposite(originalComposite);
1048: } else if (marker instanceof IntervalMarker) {
1049: IntervalMarker im = (IntervalMarker) marker;
1050: double start = im.getStartValue();
1051: double end = im.getEndValue();
1052: Range range = domainAxis.getRange();
1053: if (!(range.intersects(start, end))) {
1054: return;
1055: }
1056:
1057: double start2d = domainAxis.valueToJava2D(start, dataArea,
1058: plot.getDomainAxisEdge());
1059: double end2d = domainAxis.valueToJava2D(end, dataArea, plot
1060: .getDomainAxisEdge());
1061: double low = Math.min(start2d, end2d);
1062: double high = Math.max(start2d, end2d);
1063:
1064: PlotOrientation orientation = plot.getOrientation();
1065: Rectangle2D rect = null;
1066: if (orientation == PlotOrientation.HORIZONTAL) {
1067: // clip top and bottom bounds to data area
1068: low = Math.max(low, dataArea.getMinY());
1069: high = Math.min(high, dataArea.getMaxY());
1070: rect = new Rectangle2D.Double(dataArea.getMinX(), low,
1071: dataArea.getWidth(), high - low);
1072: } else if (orientation == PlotOrientation.VERTICAL) {
1073: // clip left and right bounds to data area
1074: low = Math.max(low, dataArea.getMinX());
1075: high = Math.min(high, dataArea.getMaxX());
1076: rect = new Rectangle2D.Double(low, dataArea.getMinY(),
1077: high - low, dataArea.getHeight());
1078: }
1079:
1080: final Composite originalComposite = g2.getComposite();
1081: g2.setComposite(AlphaComposite.getInstance(
1082: AlphaComposite.SRC_OVER, marker.getAlpha()));
1083: Paint p = marker.getPaint();
1084: if (p instanceof GradientPaint) {
1085: GradientPaint gp = (GradientPaint) p;
1086: GradientPaintTransformer t = im
1087: .getGradientPaintTransformer();
1088: if (t != null) {
1089: gp = t.transform(gp, rect);
1090: }
1091: g2.setPaint(gp);
1092: } else {
1093: g2.setPaint(p);
1094: }
1095: g2.fill(rect);
1096:
1097: // now draw the outlines, if visible...
1098: if (im.getOutlinePaint() != null
1099: && im.getOutlineStroke() != null) {
1100: if (orientation == PlotOrientation.VERTICAL) {
1101: Line2D line = new Line2D.Double();
1102: double y0 = dataArea.getMinY();
1103: double y1 = dataArea.getMaxY();
1104: g2.setPaint(im.getOutlinePaint());
1105: g2.setStroke(im.getOutlineStroke());
1106: if (range.contains(start)) {
1107: line.setLine(start2d, y0, start2d, y1);
1108: g2.draw(line);
1109: }
1110: if (range.contains(end)) {
1111: line.setLine(end2d, y0, end2d, y1);
1112: g2.draw(line);
1113: }
1114: } else { // PlotOrientation.HORIZONTAL
1115: Line2D line = new Line2D.Double();
1116: double x0 = dataArea.getMinX();
1117: double x1 = dataArea.getMaxX();
1118: g2.setPaint(im.getOutlinePaint());
1119: g2.setStroke(im.getOutlineStroke());
1120: if (range.contains(start)) {
1121: line.setLine(x0, start2d, x1, start2d);
1122: g2.draw(line);
1123: }
1124: if (range.contains(end)) {
1125: line.setLine(x0, end2d, x1, end2d);
1126: g2.draw(line);
1127: }
1128: }
1129: }
1130:
1131: String label = marker.getLabel();
1132: RectangleAnchor anchor = marker.getLabelAnchor();
1133: if (label != null) {
1134: Font labelFont = marker.getLabelFont();
1135: g2.setFont(labelFont);
1136: g2.setPaint(marker.getLabelPaint());
1137: Point2D coordinates = calculateDomainMarkerTextAnchorPoint(
1138: g2, orientation, dataArea, rect, marker
1139: .getLabelOffset(), marker
1140: .getLabelOffsetType(), anchor);
1141: TextUtilities.drawAlignedString(label, g2,
1142: (float) coordinates.getX(), (float) coordinates
1143: .getY(), marker.getLabelTextAnchor());
1144: }
1145: g2.setComposite(originalComposite);
1146:
1147: }
1148:
1149: }
1150:
1151: /**
1152: * Calculates the (x, y) coordinates for drawing a marker label.
1153: *
1154: * @param g2 the graphics device.
1155: * @param orientation the plot orientation.
1156: * @param dataArea the data area.
1157: * @param markerArea the rectangle surrounding the marker area.
1158: * @param markerOffset the marker label offset.
1159: * @param labelOffsetType the label offset type.
1160: * @param anchor the label anchor.
1161: *
1162: * @return The coordinates for drawing the marker label.
1163: */
1164: protected Point2D calculateDomainMarkerTextAnchorPoint(
1165: Graphics2D g2, PlotOrientation orientation,
1166: Rectangle2D dataArea, Rectangle2D markerArea,
1167: RectangleInsets markerOffset,
1168: LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) {
1169:
1170: Rectangle2D anchorRect = null;
1171: if (orientation == PlotOrientation.HORIZONTAL) {
1172: anchorRect = markerOffset.createAdjustedRectangle(
1173: markerArea, LengthAdjustmentType.CONTRACT,
1174: labelOffsetType);
1175: } else if (orientation == PlotOrientation.VERTICAL) {
1176: anchorRect = markerOffset.createAdjustedRectangle(
1177: markerArea, labelOffsetType,
1178: LengthAdjustmentType.CONTRACT);
1179: }
1180: return RectangleAnchor.coordinates(anchorRect, anchor);
1181:
1182: }
1183:
1184: /**
1185: * Draws a horizontal line across the chart to represent a 'range marker'.
1186: *
1187: * @param g2 the graphics device.
1188: * @param plot the plot.
1189: * @param rangeAxis the range axis.
1190: * @param marker the marker line.
1191: * @param dataArea the axis data area.
1192: */
1193: public void drawRangeMarker(Graphics2D g2, XYPlot plot,
1194: ValueAxis rangeAxis, Marker marker, Rectangle2D dataArea) {
1195:
1196: if (marker instanceof ValueMarker) {
1197: ValueMarker vm = (ValueMarker) marker;
1198: double value = vm.getValue();
1199: Range range = rangeAxis.getRange();
1200: if (!range.contains(value)) {
1201: return;
1202: }
1203:
1204: double v = rangeAxis.valueToJava2D(value, dataArea, plot
1205: .getRangeAxisEdge());
1206: PlotOrientation orientation = plot.getOrientation();
1207: Line2D line = null;
1208: if (orientation == PlotOrientation.HORIZONTAL) {
1209: line = new Line2D.Double(v, dataArea.getMinY(), v,
1210: dataArea.getMaxY());
1211: } else if (orientation == PlotOrientation.VERTICAL) {
1212: line = new Line2D.Double(dataArea.getMinX(), v,
1213: dataArea.getMaxX(), v);
1214: }
1215:
1216: final Composite originalComposite = g2.getComposite();
1217: g2.setComposite(AlphaComposite.getInstance(
1218: AlphaComposite.SRC_OVER, marker.getAlpha()));
1219: g2.setPaint(marker.getPaint());
1220: g2.setStroke(marker.getStroke());
1221: g2.draw(line);
1222:
1223: String label = marker.getLabel();
1224: RectangleAnchor anchor = marker.getLabelAnchor();
1225: if (label != null) {
1226: Font labelFont = marker.getLabelFont();
1227: g2.setFont(labelFont);
1228: g2.setPaint(marker.getLabelPaint());
1229: Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
1230: g2, orientation, dataArea, line.getBounds2D(),
1231: marker.getLabelOffset(),
1232: LengthAdjustmentType.EXPAND, anchor);
1233: TextUtilities.drawAlignedString(label, g2,
1234: (float) coordinates.getX(), (float) coordinates
1235: .getY(), marker.getLabelTextAnchor());
1236: }
1237: g2.setComposite(originalComposite);
1238: } else if (marker instanceof IntervalMarker) {
1239: IntervalMarker im = (IntervalMarker) marker;
1240: double start = im.getStartValue();
1241: double end = im.getEndValue();
1242: Range range = rangeAxis.getRange();
1243: if (!(range.intersects(start, end))) {
1244: return;
1245: }
1246:
1247: double start2d = rangeAxis.valueToJava2D(start, dataArea,
1248: plot.getRangeAxisEdge());
1249: double end2d = rangeAxis.valueToJava2D(end, dataArea, plot
1250: .getRangeAxisEdge());
1251: double low = Math.min(start2d, end2d);
1252: double high = Math.max(start2d, end2d);
1253:
1254: PlotOrientation orientation = plot.getOrientation();
1255: Rectangle2D rect = null;
1256: if (orientation == PlotOrientation.HORIZONTAL) {
1257: // clip left and right bounds to data area
1258: low = Math.max(low, dataArea.getMinX());
1259: high = Math.min(high, dataArea.getMaxX());
1260: rect = new Rectangle2D.Double(low, dataArea.getMinY(),
1261: high - low, dataArea.getHeight());
1262: } else if (orientation == PlotOrientation.VERTICAL) {
1263: // clip top and bottom bounds to data area
1264: low = Math.max(low, dataArea.getMinY());
1265: high = Math.min(high, dataArea.getMaxY());
1266: rect = new Rectangle2D.Double(dataArea.getMinX(), low,
1267: dataArea.getWidth(), high - low);
1268: }
1269:
1270: final Composite originalComposite = g2.getComposite();
1271: g2.setComposite(AlphaComposite.getInstance(
1272: AlphaComposite.SRC_OVER, marker.getAlpha()));
1273: Paint p = marker.getPaint();
1274: if (p instanceof GradientPaint) {
1275: GradientPaint gp = (GradientPaint) p;
1276: GradientPaintTransformer t = im
1277: .getGradientPaintTransformer();
1278: if (t != null) {
1279: gp = t.transform(gp, rect);
1280: }
1281: g2.setPaint(gp);
1282: } else {
1283: g2.setPaint(p);
1284: }
1285: g2.fill(rect);
1286:
1287: // now draw the outlines, if visible...
1288: if (im.getOutlinePaint() != null
1289: && im.getOutlineStroke() != null) {
1290: if (orientation == PlotOrientation.VERTICAL) {
1291: Line2D line = new Line2D.Double();
1292: double x0 = dataArea.getMinX();
1293: double x1 = dataArea.getMaxX();
1294: g2.setPaint(im.getOutlinePaint());
1295: g2.setStroke(im.getOutlineStroke());
1296: if (range.contains(start)) {
1297: line.setLine(x0, start2d, x1, start2d);
1298: g2.draw(line);
1299: }
1300: if (range.contains(end)) {
1301: line.setLine(x0, end2d, x1, end2d);
1302: g2.draw(line);
1303: }
1304: } else { // PlotOrientation.HORIZONTAL
1305: Line2D line = new Line2D.Double();
1306: double y0 = dataArea.getMinY();
1307: double y1 = dataArea.getMaxY();
1308: g2.setPaint(im.getOutlinePaint());
1309: g2.setStroke(im.getOutlineStroke());
1310: if (range.contains(start)) {
1311: line.setLine(start2d, y0, start2d, y1);
1312: g2.draw(line);
1313: }
1314: if (range.contains(end)) {
1315: line.setLine(end2d, y0, end2d, y1);
1316: g2.draw(line);
1317: }
1318: }
1319: }
1320:
1321: String label = marker.getLabel();
1322: RectangleAnchor anchor = marker.getLabelAnchor();
1323: if (label != null) {
1324: Font labelFont = marker.getLabelFont();
1325: g2.setFont(labelFont);
1326: g2.setPaint(marker.getLabelPaint());
1327: Point2D coordinates = calculateRangeMarkerTextAnchorPoint(
1328: g2, orientation, dataArea, rect, marker
1329: .getLabelOffset(), marker
1330: .getLabelOffsetType(), anchor);
1331: TextUtilities.drawAlignedString(label, g2,
1332: (float) coordinates.getX(), (float) coordinates
1333: .getY(), marker.getLabelTextAnchor());
1334: }
1335: g2.setComposite(originalComposite);
1336: }
1337: }
1338:
1339: /**
1340: * Calculates the (x, y) coordinates for drawing a marker label.
1341: *
1342: * @param g2 the graphics device.
1343: * @param orientation the plot orientation.
1344: * @param dataArea the data area.
1345: * @param markerArea the marker area.
1346: * @param markerOffset the marker offset.
1347: * @param anchor the label anchor.
1348: *
1349: * @return The coordinates for drawing the marker label.
1350: */
1351: private Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2,
1352: PlotOrientation orientation, Rectangle2D dataArea,
1353: Rectangle2D markerArea, RectangleInsets markerOffset,
1354: LengthAdjustmentType labelOffsetForRange,
1355: RectangleAnchor anchor) {
1356:
1357: Rectangle2D anchorRect = null;
1358: if (orientation == PlotOrientation.HORIZONTAL) {
1359: anchorRect = markerOffset.createAdjustedRectangle(
1360: markerArea, labelOffsetForRange,
1361: LengthAdjustmentType.CONTRACT);
1362: } else if (orientation == PlotOrientation.VERTICAL) {
1363: anchorRect = markerOffset.createAdjustedRectangle(
1364: markerArea, LengthAdjustmentType.CONTRACT,
1365: labelOffsetForRange);
1366: }
1367: return RectangleAnchor.coordinates(anchorRect, anchor);
1368:
1369: }
1370:
1371: /**
1372: * Returns a clone of the renderer.
1373: *
1374: * @return A clone.
1375: *
1376: * @throws CloneNotSupportedException if the renderer does not support
1377: * cloning.
1378: */
1379: protected Object clone() throws CloneNotSupportedException {
1380: AbstractXYItemRenderer clone = (AbstractXYItemRenderer) super
1381: .clone();
1382: // 'plot' : just retain reference, not a deep copy
1383:
1384: if (this .itemLabelGenerator != null
1385: && this .itemLabelGenerator instanceof PublicCloneable) {
1386: PublicCloneable pc = (PublicCloneable) this .itemLabelGenerator;
1387: clone.itemLabelGenerator = (XYItemLabelGenerator) pc
1388: .clone();
1389: }
1390: clone.itemLabelGeneratorList = (ObjectList) this .itemLabelGeneratorList
1391: .clone();
1392: if (this .baseItemLabelGenerator != null
1393: && this .baseItemLabelGenerator instanceof PublicCloneable) {
1394: PublicCloneable pc = (PublicCloneable) this .baseItemLabelGenerator;
1395: clone.baseItemLabelGenerator = (XYItemLabelGenerator) pc
1396: .clone();
1397: }
1398:
1399: if (this .toolTipGenerator != null
1400: && this .toolTipGenerator instanceof PublicCloneable) {
1401: PublicCloneable pc = (PublicCloneable) this .toolTipGenerator;
1402: clone.toolTipGenerator = (XYToolTipGenerator) pc.clone();
1403: }
1404: clone.toolTipGeneratorList = (ObjectList) this .toolTipGeneratorList
1405: .clone();
1406: if (this .baseToolTipGenerator != null
1407: && this .baseToolTipGenerator instanceof PublicCloneable) {
1408: PublicCloneable pc = (PublicCloneable) this .baseToolTipGenerator;
1409: clone.baseToolTipGenerator = (XYToolTipGenerator) pc
1410: .clone();
1411: }
1412:
1413: if (clone.legendItemLabelGenerator instanceof PublicCloneable) {
1414: clone.legendItemLabelGenerator = (XYSeriesLabelGenerator) ObjectUtilities
1415: .clone(this .legendItemLabelGenerator);
1416: }
1417: if (clone.legendItemToolTipGenerator instanceof PublicCloneable) {
1418: clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) ObjectUtilities
1419: .clone(this .legendItemToolTipGenerator);
1420: }
1421: if (clone.legendItemURLGenerator instanceof PublicCloneable) {
1422: clone.legendItemURLGenerator = (XYSeriesLabelGenerator) ObjectUtilities
1423: .clone(this .legendItemURLGenerator);
1424: }
1425:
1426: clone.foregroundAnnotations = (List) ObjectUtilities
1427: .deepClone(this .foregroundAnnotations);
1428: clone.backgroundAnnotations = (List) ObjectUtilities
1429: .deepClone(this .backgroundAnnotations);
1430:
1431: if (clone.legendItemLabelGenerator instanceof PublicCloneable) {
1432: clone.legendItemLabelGenerator = (XYSeriesLabelGenerator) ObjectUtilities
1433: .clone(this .legendItemLabelGenerator);
1434: }
1435: if (clone.legendItemToolTipGenerator instanceof PublicCloneable) {
1436: clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) ObjectUtilities
1437: .clone(this .legendItemToolTipGenerator);
1438: }
1439: if (clone.legendItemURLGenerator instanceof PublicCloneable) {
1440: clone.legendItemURLGenerator = (XYSeriesLabelGenerator) ObjectUtilities
1441: .clone(this .legendItemURLGenerator);
1442: }
1443:
1444: return clone;
1445: }
1446:
1447: /**
1448: * Tests this renderer for equality with another object.
1449: *
1450: * @param obj the object (<code>null</code> permitted).
1451: *
1452: * @return <code>true</code> or <code>false</code>.
1453: */
1454: public boolean equals(Object obj) {
1455: if (obj == this ) {
1456: return true;
1457: }
1458: if (!(obj instanceof AbstractXYItemRenderer)) {
1459: return false;
1460: }
1461: AbstractXYItemRenderer that = (AbstractXYItemRenderer) obj;
1462: if (!ObjectUtilities.equal(this .itemLabelGenerator,
1463: that.itemLabelGenerator)) {
1464: return false;
1465: }
1466: if (!this .itemLabelGeneratorList
1467: .equals(that.itemLabelGeneratorList)) {
1468: return false;
1469: }
1470: if (!ObjectUtilities.equal(this .baseItemLabelGenerator,
1471: that.baseItemLabelGenerator)) {
1472: return false;
1473: }
1474: if (!ObjectUtilities.equal(this .toolTipGenerator,
1475: that.toolTipGenerator)) {
1476: return false;
1477: }
1478: if (!this .toolTipGeneratorList
1479: .equals(that.toolTipGeneratorList)) {
1480: return false;
1481: }
1482: if (!ObjectUtilities.equal(this .baseToolTipGenerator,
1483: that.baseToolTipGenerator)) {
1484: return false;
1485: }
1486: if (!ObjectUtilities
1487: .equal(this .urlGenerator, that.urlGenerator)) {
1488: return false;
1489: }
1490: if (!this .foregroundAnnotations
1491: .equals(that.foregroundAnnotations)) {
1492: return false;
1493: }
1494: if (!this .backgroundAnnotations
1495: .equals(that.backgroundAnnotations)) {
1496: return false;
1497: }
1498: if (this .defaultEntityRadius != that.defaultEntityRadius) {
1499: return false;
1500: }
1501: if (!ObjectUtilities.equal(this .legendItemLabelGenerator,
1502: that.legendItemLabelGenerator)) {
1503: return false;
1504: }
1505: if (!ObjectUtilities.equal(this .legendItemToolTipGenerator,
1506: that.legendItemToolTipGenerator)) {
1507: return false;
1508: }
1509: if (!ObjectUtilities.equal(this .legendItemURLGenerator,
1510: that.legendItemURLGenerator)) {
1511: return false;
1512: }
1513: return super .equals(obj);
1514: }
1515:
1516: /**
1517: * Returns the drawing supplier from the plot.
1518: *
1519: * @return The drawing supplier (possibly <code>null</code>).
1520: */
1521: public DrawingSupplier getDrawingSupplier() {
1522: DrawingSupplier result = null;
1523: XYPlot p = getPlot();
1524: if (p != null) {
1525: result = p.getDrawingSupplier();
1526: }
1527: return result;
1528: }
1529:
1530: /**
1531: * Considers the current (x, y) coordinate and updates the crosshair point
1532: * if it meets the criteria (usually means the (x, y) coordinate is the
1533: * closest to the anchor point so far).
1534: *
1535: * @param crosshairState the crosshair state (<code>null</code> permitted,
1536: * but the method does nothing in that case).
1537: * @param x the x-value (in data space).
1538: * @param y the y-value (in data space).
1539: * @param transX the x-value translated to Java2D space.
1540: * @param transY the y-value translated to Java2D space.
1541: * @param orientation the plot orientation (<code>null</code> not
1542: * permitted).
1543: *
1544: * @deprecated Use {@link #updateCrosshairValues(CrosshairState, double,
1545: * double, int, int, double, double, PlotOrientation)} -- see bug
1546: * report 1086307.
1547: */
1548: protected void updateCrosshairValues(CrosshairState crosshairState,
1549: double x, double y, double transX, double transY,
1550: PlotOrientation orientation) {
1551: updateCrosshairValues(crosshairState, x, y, 0, 0, transX,
1552: transY, orientation);
1553: }
1554:
1555: /**
1556: * Considers the current (x, y) coordinate and updates the crosshair point
1557: * if it meets the criteria (usually means the (x, y) coordinate is the
1558: * closest to the anchor point so far).
1559: *
1560: * @param crosshairState the crosshair state (<code>null</code> permitted,
1561: * but the method does nothing in that case).
1562: * @param x the x-value (in data space).
1563: * @param y the y-value (in data space).
1564: * @param domainAxisIndex the index of the domain axis for the point.
1565: * @param rangeAxisIndex the index of the range axis for the point.
1566: * @param transX the x-value translated to Java2D space.
1567: * @param transY the y-value translated to Java2D space.
1568: * @param orientation the plot orientation (<code>null</code> not
1569: * permitted).
1570: *
1571: * @since 1.0.4
1572: */
1573: protected void updateCrosshairValues(CrosshairState crosshairState,
1574: double x, double y, int domainAxisIndex,
1575: int rangeAxisIndex, double transX, double transY,
1576: PlotOrientation orientation) {
1577:
1578: if (orientation == null) {
1579: throw new IllegalArgumentException(
1580: "Null 'orientation' argument.");
1581: }
1582:
1583: if (crosshairState != null) {
1584: // do we need to update the crosshair values?
1585: if (this .plot.isDomainCrosshairLockedOnData()) {
1586: if (this .plot.isRangeCrosshairLockedOnData()) {
1587: // both axes
1588: crosshairState.updateCrosshairPoint(x, y,
1589: domainAxisIndex, rangeAxisIndex, transX,
1590: transY, orientation);
1591: } else {
1592: // just the domain axis...
1593: crosshairState.updateCrosshairX(x, domainAxisIndex);
1594: }
1595: } else {
1596: if (this .plot.isRangeCrosshairLockedOnData()) {
1597: // just the range axis...
1598: crosshairState.updateCrosshairY(y, rangeAxisIndex);
1599: }
1600: }
1601: }
1602:
1603: }
1604:
1605: /**
1606: * Draws an item label.
1607: *
1608: * @param g2 the graphics device.
1609: * @param orientation the orientation.
1610: * @param dataset the dataset.
1611: * @param series the series index (zero-based).
1612: * @param item the item index (zero-based).
1613: * @param x the x coordinate (in Java2D space).
1614: * @param y the y coordinate (in Java2D space).
1615: * @param negative indicates a negative value (which affects the item
1616: * label position).
1617: */
1618: protected void drawItemLabel(Graphics2D g2,
1619: PlotOrientation orientation, XYDataset dataset, int series,
1620: int item, double x, double y, boolean negative) {
1621:
1622: XYItemLabelGenerator generator = getItemLabelGenerator(series,
1623: item);
1624: if (generator != null) {
1625: Font labelFont = getItemLabelFont(series, item);
1626: Paint paint = getItemLabelPaint(series, item);
1627: g2.setFont(labelFont);
1628: g2.setPaint(paint);
1629: String label = generator.generateLabel(dataset, series,
1630: item);
1631:
1632: // get the label position..
1633: ItemLabelPosition position = null;
1634: if (!negative) {
1635: position = getPositiveItemLabelPosition(series, item);
1636: } else {
1637: position = getNegativeItemLabelPosition(series, item);
1638: }
1639:
1640: // work out the label anchor point...
1641: Point2D anchorPoint = calculateLabelAnchorPoint(position
1642: .getItemLabelAnchor(), x, y, orientation);
1643: TextUtilities.drawRotatedString(label, g2,
1644: (float) anchorPoint.getX(), (float) anchorPoint
1645: .getY(), position.getTextAnchor(), position
1646: .getAngle(), position.getRotationAnchor());
1647: }
1648:
1649: }
1650:
1651: /**
1652: * Draws all the annotations for the specified layer.
1653: *
1654: * @param g2 the graphics device.
1655: * @param dataArea the data area.
1656: * @param domainAxis the domain axis.
1657: * @param rangeAxis the range axis.
1658: * @param layer the layer.
1659: * @param info the plot rendering info.
1660: */
1661: public void drawAnnotations(Graphics2D g2, Rectangle2D dataArea,
1662: ValueAxis domainAxis, ValueAxis rangeAxis, Layer layer,
1663: PlotRenderingInfo info) {
1664:
1665: Iterator iterator = null;
1666: if (layer.equals(Layer.FOREGROUND)) {
1667: iterator = this .foregroundAnnotations.iterator();
1668: } else if (layer.equals(Layer.BACKGROUND)) {
1669: iterator = this .backgroundAnnotations.iterator();
1670: } else {
1671: // should not get here
1672: throw new RuntimeException("Unknown layer.");
1673: }
1674: while (iterator.hasNext()) {
1675: XYAnnotation annotation = (XYAnnotation) iterator.next();
1676: annotation.draw(g2, this .plot, dataArea, domainAxis,
1677: rangeAxis, 0, info);
1678: }
1679:
1680: }
1681:
1682: /**
1683: * Adds an entity to the collection.
1684: *
1685: * @param entities the entity collection being populated.
1686: * @param area the entity area (if <code>null</code> a default will be
1687: * used).
1688: * @param dataset the dataset.
1689: * @param series the series.
1690: * @param item the item.
1691: * @param entityX the entity's center x-coordinate in user space.
1692: * @param entityY the entity's center y-coordinate in user space.
1693: */
1694: protected void addEntity(EntityCollection entities, Shape area,
1695: XYDataset dataset, int series, int item, double entityX,
1696: double entityY) {
1697: if (!getItemCreateEntity(series, item)) {
1698: return;
1699: }
1700: if (area == null) {
1701: area = new Ellipse2D.Double(entityX
1702: - this .defaultEntityRadius, entityY
1703: - this .defaultEntityRadius,
1704: this .defaultEntityRadius * 2,
1705: this .defaultEntityRadius * 2);
1706: }
1707: String tip = null;
1708: XYToolTipGenerator generator = getToolTipGenerator(series, item);
1709: if (generator != null) {
1710: tip = generator.generateToolTip(dataset, series, item);
1711: }
1712: String url = null;
1713: if (getURLGenerator() != null) {
1714: url = getURLGenerator().generateURL(dataset, series, item);
1715: }
1716: XYItemEntity entity = new XYItemEntity(area, dataset, series,
1717: item, tip, url);
1718: entities.add(entity);
1719: }
1720:
1721: }
|