001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2007, 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: * LineAndShapeRenderer.java
029: * -------------------------
030: * (C) Copyright 2001-2007, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): Mark Watson (www.markwatson.com);
034: * Jeremy Bowman;
035: * Richard Atkinson;
036: * Christian W. Zuckschwerdt;
037: *
038: * $Id: LineAndShapeRenderer.java,v 1.18.2.10 2007/05/18 10:28:27 mungady Exp $
039: *
040: * Changes
041: * -------
042: * 23-Oct-2001 : Version 1 (DG);
043: * 15-Nov-2001 : Modified to allow for null data values (DG);
044: * 16-Jan-2002 : Renamed HorizontalCategoryItemRenderer.java
045: * --> CategoryItemRenderer.java (DG);
046: * 05-Feb-2002 : Changed return type of the drawCategoryItem method from void
047: * to Shape, as part of the tooltips implementation (DG);
048: * 11-May-2002 : Support for value label drawing (JB);
049: * 29-May-2002 : Now extends AbstractCategoryItemRenderer (DG);
050: * 25-Jun-2002 : Removed redundant import (DG);
051: * 05-Aug-2002 : Small modification to drawCategoryItem method to support URLs
052: * for HTML image maps (RA);
053: * 26-Sep-2002 : Fixed errors reported by Checkstyle (DG);
054: * 11-Oct-2002 : Added new constructor to incorporate tool tip and URL
055: * generators (DG);
056: * 24-Oct-2002 : Amendments for changes in CategoryDataset interface and
057: * CategoryToolTipGenerator interface (DG);
058: * 05-Nov-2002 : Base dataset is now TableDataset not CategoryDataset (DG);
059: * 06-Nov-2002 : Renamed drawCategoryItem() --> drawItem() and now using axis
060: * for category spacing (DG);
061: * 17-Jan-2003 : Moved plot classes to a separate package (DG);
062: * 10-Apr-2003 : Changed CategoryDataset to KeyedValues2DDataset in drawItem()
063: * method (DG);
064: * 12-May-2003 : Modified to take into account the plot orientation (DG);
065: * 29-Jul-2003 : Amended code that doesn't compile with JDK 1.2.2 (DG);
066: * 30-Jul-2003 : Modified entity constructor (CZ);
067: * 22-Sep-2003 : Fixed cloning (DG);
068: * 10-Feb-2004 : Small change to drawItem() method to make cut-and-paste
069: * override easier (DG);
070: * 16-Jun-2004 : Fixed bug (id=972454) with label positioning on horizontal
071: * charts (DG);
072: * 15-Oct-2004 : Updated equals() method (DG);
073: * 05-Nov-2004 : Modified drawItem() signature (DG);
074: * 11-Nov-2004 : Now uses ShapeUtilities class to translate shapes (DG);
075: * 27-Jan-2005 : Changed attribute names, modified constructor and removed
076: * constants (DG);
077: * 01-Feb-2005 : Removed unnecessary constants (DG);
078: * 15-Mar-2005 : Fixed bug 1163897, concerning outlines for shapes (DG);
079: * 13-Apr-2005 : Check flags that control series visibility (DG);
080: * 20-Apr-2005 : Use generators for legend labels, tooltips and URLs (DG);
081: * 09-Jun-2005 : Use addItemEntity() method (DG);
082: * ------------- JFREECHART 1.0.x ---------------------------------------------
083: * 25-May-2006 : Added check to drawItem() to detect when both the line and
084: * the shape are not visible (DG);
085: * 20-Apr-2007 : Updated getLegendItem() for renderer change (DG);
086: * 17-May-2007 : Set datasetIndex and seriesIndex in getLegendItem() (DG);
087: * 18-May-2007 : Set dataset and seriesKey for LegendItem (DG);
088: *
089: */
090:
091: package org.jfree.chart.renderer.category;
092:
093: import java.awt.Graphics2D;
094: import java.awt.Paint;
095: import java.awt.Shape;
096: import java.awt.Stroke;
097: import java.awt.geom.Line2D;
098: import java.awt.geom.Rectangle2D;
099: import java.io.Serializable;
100:
101: import org.jfree.chart.LegendItem;
102: import org.jfree.chart.axis.CategoryAxis;
103: import org.jfree.chart.axis.ValueAxis;
104: import org.jfree.chart.entity.EntityCollection;
105: import org.jfree.chart.event.RendererChangeEvent;
106: import org.jfree.chart.plot.CategoryPlot;
107: import org.jfree.chart.plot.PlotOrientation;
108: import org.jfree.data.category.CategoryDataset;
109: import org.jfree.util.BooleanList;
110: import org.jfree.util.BooleanUtilities;
111: import org.jfree.util.ObjectUtilities;
112: import org.jfree.util.PublicCloneable;
113: import org.jfree.util.ShapeUtilities;
114:
115: /**
116: * A renderer that draws shapes for each data item, and lines between data
117: * items (for use with the {@link CategoryPlot} class).
118: */
119: public class LineAndShapeRenderer extends AbstractCategoryItemRenderer
120: implements Cloneable, PublicCloneable, Serializable {
121:
122: /** For serialization. */
123: private static final long serialVersionUID = -197749519869226398L;
124:
125: /** A flag that controls whether or not lines are visible for ALL series. */
126: private Boolean linesVisible;
127:
128: /**
129: * A table of flags that control (per series) whether or not lines are
130: * visible.
131: */
132: private BooleanList seriesLinesVisible;
133:
134: /**
135: * A flag indicating whether or not lines are drawn between non-null
136: * points.
137: */
138: private boolean baseLinesVisible;
139:
140: /**
141: * A flag that controls whether or not shapes are visible for ALL series.
142: */
143: private Boolean shapesVisible;
144:
145: /**
146: * A table of flags that control (per series) whether or not shapes are
147: * visible.
148: */
149: private BooleanList seriesShapesVisible;
150:
151: /** The default value returned by the getShapeVisible() method. */
152: private boolean baseShapesVisible;
153:
154: /** A flag that controls whether or not shapes are filled for ALL series. */
155: private Boolean shapesFilled;
156:
157: /**
158: * A table of flags that control (per series) whether or not shapes are
159: * filled.
160: */
161: private BooleanList seriesShapesFilled;
162:
163: /** The default value returned by the getShapeFilled() method. */
164: private boolean baseShapesFilled;
165:
166: /**
167: * A flag that controls whether the fill paint is used for filling
168: * shapes.
169: */
170: private boolean useFillPaint;
171:
172: /** A flag that controls whether outlines are drawn for shapes. */
173: private boolean drawOutlines;
174:
175: /**
176: * A flag that controls whether the outline paint is used for drawing shape
177: * outlines - if not, the regular series paint is used.
178: */
179: private boolean useOutlinePaint;
180:
181: /**
182: * Creates a renderer with both lines and shapes visible by default.
183: */
184: public LineAndShapeRenderer() {
185: this (true, true);
186: }
187:
188: /**
189: * Creates a new renderer with lines and/or shapes visible.
190: *
191: * @param lines draw lines?
192: * @param shapes draw shapes?
193: */
194: public LineAndShapeRenderer(boolean lines, boolean shapes) {
195: super ();
196: this .linesVisible = null;
197: this .seriesLinesVisible = new BooleanList();
198: this .baseLinesVisible = lines;
199: this .shapesVisible = null;
200: this .seriesShapesVisible = new BooleanList();
201: this .baseShapesVisible = shapes;
202: this .shapesFilled = null;
203: this .seriesShapesFilled = new BooleanList();
204: this .baseShapesFilled = true;
205: this .useFillPaint = false;
206: this .drawOutlines = true;
207: this .useOutlinePaint = false;
208: }
209:
210: // LINES VISIBLE
211:
212: /**
213: * Returns the flag used to control whether or not the line for an item is
214: * visible.
215: *
216: * @param series the series index (zero-based).
217: * @param item the item index (zero-based).
218: *
219: * @return A boolean.
220: */
221: public boolean getItemLineVisible(int series, int item) {
222: Boolean flag = this .linesVisible;
223: if (flag == null) {
224: flag = getSeriesLinesVisible(series);
225: }
226: if (flag != null) {
227: return flag.booleanValue();
228: } else {
229: return this .baseLinesVisible;
230: }
231: }
232:
233: /**
234: * Returns a flag that controls whether or not lines are drawn for ALL
235: * series. If this flag is <code>null</code>, then the "per series"
236: * settings will apply.
237: *
238: * @return A flag (possibly <code>null</code>).
239: */
240: public Boolean getLinesVisible() {
241: return this .linesVisible;
242: }
243:
244: /**
245: * Sets a flag that controls whether or not lines are drawn between the
246: * items in ALL series, and sends a {@link RendererChangeEvent} to all
247: * registered listeners. You need to set this to <code>null</code> if you
248: * want the "per series" settings to apply.
249: *
250: * @param visible the flag (<code>null</code> permitted).
251: */
252: public void setLinesVisible(Boolean visible) {
253: this .linesVisible = visible;
254: notifyListeners(new RendererChangeEvent(this ));
255: }
256:
257: /**
258: * Sets a flag that controls whether or not lines are drawn between the
259: * items in ALL series, and sends a {@link RendererChangeEvent} to all
260: * registered listeners.
261: *
262: * @param visible the flag.
263: */
264: public void setLinesVisible(boolean visible) {
265: setLinesVisible(BooleanUtilities.valueOf(visible));
266: }
267:
268: /**
269: * Returns the flag used to control whether or not the lines for a series
270: * are visible.
271: *
272: * @param series the series index (zero-based).
273: *
274: * @return The flag (possibly <code>null</code>).
275: */
276: public Boolean getSeriesLinesVisible(int series) {
277: return this .seriesLinesVisible.getBoolean(series);
278: }
279:
280: /**
281: * Sets the 'lines visible' flag for a series.
282: *
283: * @param series the series index (zero-based).
284: * @param flag the flag (<code>null</code> permitted).
285: */
286: public void setSeriesLinesVisible(int series, Boolean flag) {
287: this .seriesLinesVisible.setBoolean(series, flag);
288: notifyListeners(new RendererChangeEvent(this ));
289: }
290:
291: /**
292: * Sets the 'lines visible' flag for a series.
293: *
294: * @param series the series index (zero-based).
295: * @param visible the flag.
296: */
297: public void setSeriesLinesVisible(int series, boolean visible) {
298: setSeriesLinesVisible(series, BooleanUtilities.valueOf(visible));
299: }
300:
301: /**
302: * Returns the base 'lines visible' attribute.
303: *
304: * @return The base flag.
305: */
306: public boolean getBaseLinesVisible() {
307: return this .baseLinesVisible;
308: }
309:
310: /**
311: * Sets the base 'lines visible' flag.
312: *
313: * @param flag the flag.
314: */
315: public void setBaseLinesVisible(boolean flag) {
316: this .baseLinesVisible = flag;
317: notifyListeners(new RendererChangeEvent(this ));
318: }
319:
320: // SHAPES VISIBLE
321:
322: /**
323: * Returns the flag used to control whether or not the shape for an item is
324: * visible.
325: *
326: * @param series the series index (zero-based).
327: * @param item the item index (zero-based).
328: *
329: * @return A boolean.
330: */
331: public boolean getItemShapeVisible(int series, int item) {
332: Boolean flag = this .shapesVisible;
333: if (flag == null) {
334: flag = getSeriesShapesVisible(series);
335: }
336: if (flag != null) {
337: return flag.booleanValue();
338: } else {
339: return this .baseShapesVisible;
340: }
341: }
342:
343: /**
344: * Returns the flag that controls whether the shapes are visible for the
345: * items in ALL series.
346: *
347: * @return The flag (possibly <code>null</code>).
348: */
349: public Boolean getShapesVisible() {
350: return this .shapesVisible;
351: }
352:
353: /**
354: * Sets the 'shapes visible' for ALL series and sends a
355: * {@link RendererChangeEvent} to all registered listeners.
356: *
357: * @param visible the flag (<code>null</code> permitted).
358: */
359: public void setShapesVisible(Boolean visible) {
360: this .shapesVisible = visible;
361: notifyListeners(new RendererChangeEvent(this ));
362: }
363:
364: /**
365: * Sets the 'shapes visible' for ALL series and sends a
366: * {@link RendererChangeEvent} to all registered listeners.
367: *
368: * @param visible the flag.
369: */
370: public void setShapesVisible(boolean visible) {
371: setShapesVisible(BooleanUtilities.valueOf(visible));
372: }
373:
374: /**
375: * Returns the flag used to control whether or not the shapes for a series
376: * are visible.
377: *
378: * @param series the series index (zero-based).
379: *
380: * @return A boolean.
381: */
382: public Boolean getSeriesShapesVisible(int series) {
383: return this .seriesShapesVisible.getBoolean(series);
384: }
385:
386: /**
387: * Sets the 'shapes visible' flag for a series and sends a
388: * {@link RendererChangeEvent} to all registered listeners.
389: *
390: * @param series the series index (zero-based).
391: * @param visible the flag.
392: */
393: public void setSeriesShapesVisible(int series, boolean visible) {
394: setSeriesShapesVisible(series, BooleanUtilities
395: .valueOf(visible));
396: }
397:
398: /**
399: * Sets the 'shapes visible' flag for a series and sends a
400: * {@link RendererChangeEvent} to all registered listeners.
401: *
402: * @param series the series index (zero-based).
403: * @param flag the flag.
404: */
405: public void setSeriesShapesVisible(int series, Boolean flag) {
406: this .seriesShapesVisible.setBoolean(series, flag);
407: notifyListeners(new RendererChangeEvent(this ));
408: }
409:
410: /**
411: * Returns the base 'shape visible' attribute.
412: *
413: * @return The base flag.
414: */
415: public boolean getBaseShapesVisible() {
416: return this .baseShapesVisible;
417: }
418:
419: /**
420: * Sets the base 'shapes visible' flag.
421: *
422: * @param flag the flag.
423: */
424: public void setBaseShapesVisible(boolean flag) {
425: this .baseShapesVisible = flag;
426: notifyListeners(new RendererChangeEvent(this ));
427: }
428:
429: /**
430: * Returns <code>true</code> if outlines should be drawn for shapes, and
431: * <code>false</code> otherwise.
432: *
433: * @return A boolean.
434: */
435: public boolean getDrawOutlines() {
436: return this .drawOutlines;
437: }
438:
439: /**
440: * Sets the flag that controls whether outlines are drawn for
441: * shapes, and sends a {@link RendererChangeEvent} to all registered
442: * listeners.
443: * <P>
444: * In some cases, shapes look better if they do NOT have an outline, but
445: * this flag allows you to set your own preference.
446: *
447: * @param flag the flag.
448: */
449: public void setDrawOutlines(boolean flag) {
450: this .drawOutlines = flag;
451: notifyListeners(new RendererChangeEvent(this ));
452: }
453:
454: /**
455: * Returns the flag that controls whether the outline paint is used for
456: * shape outlines. If not, the regular series paint is used.
457: *
458: * @return A boolean.
459: */
460: public boolean getUseOutlinePaint() {
461: return this .useOutlinePaint;
462: }
463:
464: /**
465: * Sets the flag that controls whether the outline paint is used for shape
466: * outlines.
467: *
468: * @param use the flag.
469: */
470: public void setUseOutlinePaint(boolean use) {
471: this .useOutlinePaint = use;
472: }
473:
474: // SHAPES FILLED
475:
476: /**
477: * Returns the flag used to control whether or not the shape for an item
478: * is filled. The default implementation passes control to the
479: * <code>getSeriesShapesFilled</code> method. You can override this method
480: * if you require different behaviour.
481: *
482: * @param series the series index (zero-based).
483: * @param item the item index (zero-based).
484: *
485: * @return A boolean.
486: */
487: public boolean getItemShapeFilled(int series, int item) {
488: return getSeriesShapesFilled(series);
489: }
490:
491: /**
492: * Returns the flag used to control whether or not the shapes for a series
493: * are filled.
494: *
495: * @param series the series index (zero-based).
496: *
497: * @return A boolean.
498: */
499: public boolean getSeriesShapesFilled(int series) {
500:
501: // return the overall setting, if there is one...
502: if (this .shapesFilled != null) {
503: return this .shapesFilled.booleanValue();
504: }
505:
506: // otherwise look up the paint table
507: Boolean flag = this .seriesShapesFilled.getBoolean(series);
508: if (flag != null) {
509: return flag.booleanValue();
510: } else {
511: return this .baseShapesFilled;
512: }
513:
514: }
515:
516: /**
517: * Returns the flag that controls whether or not shapes are filled for
518: * ALL series.
519: *
520: * @return A Boolean.
521: */
522: public Boolean getShapesFilled() {
523: return this .shapesFilled;
524: }
525:
526: /**
527: * Sets the 'shapes filled' for ALL series.
528: *
529: * @param filled the flag.
530: */
531: public void setShapesFilled(boolean filled) {
532: if (filled) {
533: setShapesFilled(Boolean.TRUE);
534: } else {
535: setShapesFilled(Boolean.FALSE);
536: }
537: }
538:
539: /**
540: * Sets the 'shapes filled' for ALL series.
541: *
542: * @param filled the flag (<code>null</code> permitted).
543: */
544: public void setShapesFilled(Boolean filled) {
545: this .shapesFilled = filled;
546: }
547:
548: /**
549: * Sets the 'shapes filled' flag for a series.
550: *
551: * @param series the series index (zero-based).
552: * @param filled the flag.
553: */
554: public void setSeriesShapesFilled(int series, Boolean filled) {
555: this .seriesShapesFilled.setBoolean(series, filled);
556: }
557:
558: /**
559: * Sets the 'shapes filled' flag for a series.
560: *
561: * @param series the series index (zero-based).
562: * @param filled the flag.
563: */
564: public void setSeriesShapesFilled(int series, boolean filled) {
565: this .seriesShapesFilled.setBoolean(series, BooleanUtilities
566: .valueOf(filled));
567: }
568:
569: /**
570: * Returns the base 'shape filled' attribute.
571: *
572: * @return The base flag.
573: */
574: public boolean getBaseShapesFilled() {
575: return this .baseShapesFilled;
576: }
577:
578: /**
579: * Sets the base 'shapes filled' flag.
580: *
581: * @param flag the flag.
582: */
583: public void setBaseShapesFilled(boolean flag) {
584: this .baseShapesFilled = flag;
585: }
586:
587: /**
588: * Returns <code>true</code> if the renderer should use the fill paint
589: * setting to fill shapes, and <code>false</code> if it should just
590: * use the regular paint.
591: *
592: * @return A boolean.
593: */
594: public boolean getUseFillPaint() {
595: return this .useFillPaint;
596: }
597:
598: /**
599: * Sets the flag that controls whether the fill paint is used to fill
600: * shapes, and sends a {@link RendererChangeEvent} to all
601: * registered listeners.
602: *
603: * @param flag the flag.
604: */
605: public void setUseFillPaint(boolean flag) {
606: this .useFillPaint = flag;
607: notifyListeners(new RendererChangeEvent(this ));
608: }
609:
610: /**
611: * Returns a legend item for a series.
612: *
613: * @param datasetIndex the dataset index (zero-based).
614: * @param series the series index (zero-based).
615: *
616: * @return The legend item.
617: */
618: public LegendItem getLegendItem(int datasetIndex, int series) {
619:
620: CategoryPlot cp = getPlot();
621: if (cp == null) {
622: return null;
623: }
624:
625: if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) {
626: CategoryDataset dataset = cp.getDataset(datasetIndex);
627: String label = getLegendItemLabelGenerator().generateLabel(
628: dataset, series);
629: String description = label;
630: String toolTipText = null;
631: if (getLegendItemToolTipGenerator() != null) {
632: toolTipText = getLegendItemToolTipGenerator()
633: .generateLabel(dataset, series);
634: }
635: String urlText = null;
636: if (getLegendItemURLGenerator() != null) {
637: urlText = getLegendItemURLGenerator().generateLabel(
638: dataset, series);
639: }
640: Shape shape = lookupSeriesShape(series);
641: Paint paint = lookupSeriesPaint(series);
642: Paint fillPaint = (this .useFillPaint ? getItemFillPaint(
643: series, 0) : paint);
644: boolean shapeOutlineVisible = this .drawOutlines;
645: Paint outlinePaint = (this .useOutlinePaint ? getItemOutlinePaint(
646: series, 0)
647: : paint);
648: Stroke outlineStroke = lookupSeriesOutlineStroke(series);
649: boolean lineVisible = getItemLineVisible(series, 0);
650: boolean shapeVisible = getItemShapeVisible(series, 0);
651: LegendItem result = new LegendItem(label, description,
652: toolTipText, urlText, shapeVisible, shape,
653: getItemShapeFilled(series, 0), fillPaint,
654: shapeOutlineVisible, outlinePaint, outlineStroke,
655: lineVisible,
656: new Line2D.Double(-7.0, 0.0, 7.0, 0.0),
657: getItemStroke(series, 0), getItemPaint(series, 0));
658: result.setDataset(dataset);
659: result.setDatasetIndex(datasetIndex);
660: result.setSeriesKey(dataset.getRowKey(series));
661: result.setSeriesIndex(series);
662: return result;
663: }
664: return null;
665:
666: }
667:
668: /**
669: * This renderer uses two passes to draw the data.
670: *
671: * @return The pass count (<code>2</code> for this renderer).
672: */
673: public int getPassCount() {
674: return 2;
675: }
676:
677: /**
678: * Draw a single data item.
679: *
680: * @param g2 the graphics device.
681: * @param state the renderer state.
682: * @param dataArea the area in which the data is drawn.
683: * @param plot the plot.
684: * @param domainAxis the domain axis.
685: * @param rangeAxis the range axis.
686: * @param dataset the dataset.
687: * @param row the row index (zero-based).
688: * @param column the column index (zero-based).
689: * @param pass the pass index.
690: */
691: public void drawItem(Graphics2D g2,
692: CategoryItemRendererState state, Rectangle2D dataArea,
693: CategoryPlot plot, CategoryAxis domainAxis,
694: ValueAxis rangeAxis, CategoryDataset dataset, int row,
695: int column, int pass) {
696:
697: // do nothing if item is not visible
698: if (!getItemVisible(row, column)) {
699: return;
700: }
701:
702: // do nothing if both the line and shape are not visible
703: if (!getItemLineVisible(row, column)
704: && !getItemShapeVisible(row, column)) {
705: return;
706: }
707:
708: // nothing is drawn for null...
709: Number v = dataset.getValue(row, column);
710: if (v == null) {
711: return;
712: }
713:
714: PlotOrientation orientation = plot.getOrientation();
715:
716: // current data point...
717: double x1 = domainAxis.getCategoryMiddle(column,
718: getColumnCount(), dataArea, plot.getDomainAxisEdge());
719: double value = v.doubleValue();
720: double y1 = rangeAxis.valueToJava2D(value, dataArea, plot
721: .getRangeAxisEdge());
722:
723: if (pass == 0 && getItemLineVisible(row, column)) {
724: if (column != 0) {
725: Number previousValue = dataset
726: .getValue(row, column - 1);
727: if (previousValue != null) {
728: // previous data point...
729: double previous = previousValue.doubleValue();
730: double x0 = domainAxis.getCategoryMiddle(
731: column - 1, getColumnCount(), dataArea,
732: plot.getDomainAxisEdge());
733: double y0 = rangeAxis.valueToJava2D(previous,
734: dataArea, plot.getRangeAxisEdge());
735:
736: Line2D line = null;
737: if (orientation == PlotOrientation.HORIZONTAL) {
738: line = new Line2D.Double(y0, x0, y1, x1);
739: } else if (orientation == PlotOrientation.VERTICAL) {
740: line = new Line2D.Double(x0, y0, x1, y1);
741: }
742: g2.setPaint(getItemPaint(row, column));
743: g2.setStroke(getItemStroke(row, column));
744: g2.draw(line);
745: }
746: }
747: }
748:
749: if (pass == 1) {
750: Shape shape = getItemShape(row, column);
751: if (orientation == PlotOrientation.HORIZONTAL) {
752: shape = ShapeUtilities.createTranslatedShape(shape, y1,
753: x1);
754: } else if (orientation == PlotOrientation.VERTICAL) {
755: shape = ShapeUtilities.createTranslatedShape(shape, x1,
756: y1);
757: }
758:
759: if (getItemShapeVisible(row, column)) {
760: if (getItemShapeFilled(row, column)) {
761: if (this .useFillPaint) {
762: g2.setPaint(getItemFillPaint(row, column));
763: } else {
764: g2.setPaint(getItemPaint(row, column));
765: }
766: g2.fill(shape);
767: }
768: if (this .drawOutlines) {
769: if (this .useOutlinePaint) {
770: g2.setPaint(getItemOutlinePaint(row, column));
771: } else {
772: g2.setPaint(getItemPaint(row, column));
773: }
774: g2.setStroke(getItemOutlineStroke(row, column));
775: g2.draw(shape);
776: }
777: }
778:
779: // draw the item label if there is one...
780: if (isItemLabelVisible(row, column)) {
781: if (orientation == PlotOrientation.HORIZONTAL) {
782: drawItemLabel(g2, orientation, dataset, row,
783: column, y1, x1, (value < 0.0));
784: } else if (orientation == PlotOrientation.VERTICAL) {
785: drawItemLabel(g2, orientation, dataset, row,
786: column, x1, y1, (value < 0.0));
787: }
788: }
789:
790: // add an item entity, if this information is being collected
791: EntityCollection entities = state.getEntityCollection();
792: if (entities != null) {
793: addItemEntity(entities, dataset, row, column, shape);
794: }
795: }
796:
797: }
798:
799: /**
800: * Tests this renderer for equality with an arbitrary object.
801: *
802: * @param obj the object (<code>null</code> permitted).
803: *
804: * @return A boolean.
805: */
806: public boolean equals(Object obj) {
807:
808: if (obj == this ) {
809: return true;
810: }
811: if (!(obj instanceof LineAndShapeRenderer)) {
812: return false;
813: }
814:
815: LineAndShapeRenderer that = (LineAndShapeRenderer) obj;
816: if (this .baseLinesVisible != that.baseLinesVisible) {
817: return false;
818: }
819: if (!ObjectUtilities.equal(this .seriesLinesVisible,
820: that.seriesLinesVisible)) {
821: return false;
822: }
823: if (!ObjectUtilities
824: .equal(this .linesVisible, that.linesVisible)) {
825: return false;
826: }
827: if (this .baseShapesVisible != that.baseShapesVisible) {
828: return false;
829: }
830: if (!ObjectUtilities.equal(this .seriesShapesVisible,
831: that.seriesShapesVisible)) {
832: return false;
833: }
834: if (!ObjectUtilities.equal(this .shapesVisible,
835: that.shapesVisible)) {
836: return false;
837: }
838: if (!ObjectUtilities
839: .equal(this .shapesFilled, that.shapesFilled)) {
840: return false;
841: }
842: if (!ObjectUtilities.equal(this .seriesShapesFilled,
843: that.seriesShapesFilled)) {
844: return false;
845: }
846: if (this .baseShapesFilled != that.baseShapesFilled) {
847: return false;
848: }
849: if (this .useOutlinePaint != that.useOutlinePaint) {
850: return false;
851: }
852: if (!super .equals(obj)) {
853: return false;
854: }
855: return true;
856: }
857:
858: /**
859: * Returns an independent copy of the renderer.
860: *
861: * @return A clone.
862: *
863: * @throws CloneNotSupportedException should not happen.
864: */
865: public Object clone() throws CloneNotSupportedException {
866: LineAndShapeRenderer clone = (LineAndShapeRenderer) super
867: .clone();
868: clone.seriesLinesVisible = (BooleanList) this .seriesLinesVisible
869: .clone();
870: clone.seriesShapesVisible = (BooleanList) this .seriesLinesVisible
871: .clone();
872: clone.seriesShapesFilled = (BooleanList) this.seriesShapesFilled
873: .clone();
874: return clone;
875: }
876:
877: }
|