001: /* ===========================================================
002: * JFreeChart : a free chart library for the Java(tm) platform
003: * ===========================================================
004: *
005: * (C) Copyright 2000-2006, 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: * LegendGraphic.java
029: * ------------------
030: * (C) Copyright 2004-2006, by Object Refinery Limited.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: LegendGraphic.java,v 1.9.2.4 2006/12/13 11:23:38 mungady Exp $
036: *
037: * Changes
038: * -------
039: * 26-Oct-2004 : Version 1 (DG);
040: * 21-Jan-2005 : Modified return type of RectangleAnchor.coordinates()
041: * method (DG);
042: * 20-Apr-2005 : Added new draw() method (DG);
043: * 13-May-2005 : Fixed to respect margin, border and padding settings (DG);
044: * 01-Sep-2005 : Implemented PublicCloneable (DG);
045: * ------------- JFREECHART 1.0.x ---------------------------------------------
046: * 13-Dec-2006 : Added fillPaintTransformer attribute, so legend graphics can
047: * display gradient paint correctly, updated equals() and
048: * corrected clone() (DG);
049: *
050: */
051:
052: package org.jfree.chart.title;
053:
054: import java.awt.GradientPaint;
055: import java.awt.Graphics2D;
056: import java.awt.Paint;
057: import java.awt.Shape;
058: import java.awt.Stroke;
059: import java.awt.geom.Point2D;
060: import java.awt.geom.Rectangle2D;
061: import java.io.IOException;
062: import java.io.ObjectInputStream;
063: import java.io.ObjectOutputStream;
064:
065: import org.jfree.chart.block.AbstractBlock;
066: import org.jfree.chart.block.Block;
067: import org.jfree.chart.block.LengthConstraintType;
068: import org.jfree.chart.block.RectangleConstraint;
069: import org.jfree.io.SerialUtilities;
070: import org.jfree.ui.GradientPaintTransformer;
071: import org.jfree.ui.RectangleAnchor;
072: import org.jfree.ui.Size2D;
073: import org.jfree.ui.StandardGradientPaintTransformer;
074: import org.jfree.util.ObjectUtilities;
075: import org.jfree.util.PaintUtilities;
076: import org.jfree.util.PublicCloneable;
077: import org.jfree.util.ShapeUtilities;
078:
079: /**
080: * The graphical item within a legend item.
081: */
082: public class LegendGraphic extends AbstractBlock implements Block,
083: PublicCloneable {
084:
085: /**
086: * A flag that controls whether or not the shape is visible - see also
087: * lineVisible.
088: */
089: private boolean shapeVisible;
090:
091: /**
092: * The shape to display. To allow for accurate positioning, the center
093: * of the shape should be at (0, 0).
094: */
095: private transient Shape shape;
096:
097: /**
098: * Defines the location within the block to which the shape will be aligned.
099: */
100: private RectangleAnchor shapeLocation;
101:
102: /**
103: * Defines the point on the shape's bounding rectangle that will be
104: * aligned to the drawing location when the shape is rendered.
105: */
106: private RectangleAnchor shapeAnchor;
107:
108: /** A flag that controls whether or not the shape is filled. */
109: private boolean shapeFilled;
110:
111: /** The fill paint for the shape. */
112: private transient Paint fillPaint;
113:
114: /**
115: * The fill paint transformer (used if the fillPaint is an instance of
116: * GradientPaint).
117: *
118: * @since 1.0.4
119: */
120: private GradientPaintTransformer fillPaintTransformer;
121:
122: /** A flag that controls whether or not the shape outline is visible. */
123: private boolean shapeOutlineVisible;
124:
125: /** The outline paint for the shape. */
126: private transient Paint outlinePaint;
127:
128: /** The outline stroke for the shape. */
129: private transient Stroke outlineStroke;
130:
131: /**
132: * A flag that controls whether or not the line is visible - see also
133: * shapeVisible.
134: */
135: private boolean lineVisible;
136:
137: /** The line. */
138: private transient Shape line;
139:
140: /** The line stroke. */
141: private transient Stroke lineStroke;
142:
143: /** The line paint. */
144: private transient Paint linePaint;
145:
146: /**
147: * Creates a new legend graphic.
148: *
149: * @param shape the shape (<code>null</code> not permitted).
150: * @param fillPaint the fill paint (<code>null</code> not permitted).
151: */
152: public LegendGraphic(Shape shape, Paint fillPaint) {
153: if (shape == null) {
154: throw new IllegalArgumentException("Null 'shape' argument.");
155: }
156: if (fillPaint == null) {
157: throw new IllegalArgumentException(
158: "Null 'fillPaint' argument.");
159: }
160: this .shapeVisible = true;
161: this .shape = shape;
162: this .shapeAnchor = RectangleAnchor.CENTER;
163: this .shapeLocation = RectangleAnchor.CENTER;
164: this .shapeFilled = true;
165: this .fillPaint = fillPaint;
166: this .fillPaintTransformer = new StandardGradientPaintTransformer();
167: setPadding(2.0, 2.0, 2.0, 2.0);
168: }
169:
170: /**
171: * Returns a flag that controls whether or not the shape
172: * is visible.
173: *
174: * @return A boolean.
175: */
176: public boolean isShapeVisible() {
177: return this .shapeVisible;
178: }
179:
180: /**
181: * Sets a flag that controls whether or not the shape is
182: * visible.
183: *
184: * @param visible the flag.
185: */
186: public void setShapeVisible(boolean visible) {
187: this .shapeVisible = visible;
188: }
189:
190: /**
191: * Returns the shape.
192: *
193: * @return The shape.
194: */
195: public Shape getShape() {
196: return this .shape;
197: }
198:
199: /**
200: * Sets the shape.
201: *
202: * @param shape the shape.
203: */
204: public void setShape(Shape shape) {
205: this .shape = shape;
206: }
207:
208: /**
209: * Returns a flag that controls whether or not the shapes
210: * are filled.
211: *
212: * @return A boolean.
213: */
214: public boolean isShapeFilled() {
215: return this .shapeFilled;
216: }
217:
218: /**
219: * Sets a flag that controls whether or not the shape is
220: * filled.
221: *
222: * @param filled the flag.
223: */
224: public void setShapeFilled(boolean filled) {
225: this .shapeFilled = filled;
226: }
227:
228: /**
229: * Returns the paint used to fill the shape.
230: *
231: * @return The fill paint.
232: */
233: public Paint getFillPaint() {
234: return this .fillPaint;
235: }
236:
237: /**
238: * Sets the paint used to fill the shape.
239: *
240: * @param paint the paint.
241: */
242: public void setFillPaint(Paint paint) {
243: this .fillPaint = paint;
244: }
245:
246: /**
247: * Returns the transformer used when the fill paint is an instance of
248: * <code>GradientPaint</code>.
249: *
250: * @return The transformer (never <code>null</code>).
251: *
252: * @since 1.0.4.
253: */
254: public GradientPaintTransformer getFillPaintTransformer() {
255: return this .fillPaintTransformer;
256: }
257:
258: /**
259: * Sets the transformer used when the fill paint is an instance of
260: * <code>GradientPaint</code>.
261: *
262: * @param transformer the transformer (<code>null</code> not permitted).
263: *
264: * @since 1.0.4
265: */
266: public void setFillPaintTransformer(
267: GradientPaintTransformer transformer) {
268: if (transformer == null) {
269: throw new IllegalArgumentException(
270: "Null 'transformer' argument.");
271: }
272: this .fillPaintTransformer = transformer;
273: }
274:
275: /**
276: * Returns a flag that controls whether the shape outline is visible.
277: *
278: * @return A boolean.
279: */
280: public boolean isShapeOutlineVisible() {
281: return this .shapeOutlineVisible;
282: }
283:
284: /**
285: * Sets a flag that controls whether or not the shape outline
286: * is visible.
287: *
288: * @param visible the flag.
289: */
290: public void setShapeOutlineVisible(boolean visible) {
291: this .shapeOutlineVisible = visible;
292: }
293:
294: /**
295: * Returns the outline paint.
296: *
297: * @return The paint.
298: */
299: public Paint getOutlinePaint() {
300: return this .outlinePaint;
301: }
302:
303: /**
304: * Sets the outline paint.
305: *
306: * @param paint the paint.
307: */
308: public void setOutlinePaint(Paint paint) {
309: this .outlinePaint = paint;
310: }
311:
312: /**
313: * Returns the outline stroke.
314: *
315: * @return The stroke.
316: */
317: public Stroke getOutlineStroke() {
318: return this .outlineStroke;
319: }
320:
321: /**
322: * Sets the outline stroke.
323: *
324: * @param stroke the stroke.
325: */
326: public void setOutlineStroke(Stroke stroke) {
327: this .outlineStroke = stroke;
328: }
329:
330: /**
331: * Returns the shape anchor.
332: *
333: * @return The shape anchor.
334: */
335: public RectangleAnchor getShapeAnchor() {
336: return this .shapeAnchor;
337: }
338:
339: /**
340: * Sets the shape anchor. This defines a point on the shapes bounding
341: * rectangle that will be used to align the shape to a location.
342: *
343: * @param anchor the anchor (<code>null</code> not permitted).
344: */
345: public void setShapeAnchor(RectangleAnchor anchor) {
346: if (anchor == null) {
347: throw new IllegalArgumentException(
348: "Null 'anchor' argument.");
349: }
350: this .shapeAnchor = anchor;
351: }
352:
353: /**
354: * Returns the shape location.
355: *
356: * @return The shape location.
357: */
358: public RectangleAnchor getShapeLocation() {
359: return this .shapeLocation;
360: }
361:
362: /**
363: * Sets the shape location. This defines a point within the drawing
364: * area that will be used to align the shape to.
365: *
366: * @param location the location (<code>null</code> not permitted).
367: */
368: public void setShapeLocation(RectangleAnchor location) {
369: if (location == null) {
370: throw new IllegalArgumentException(
371: "Null 'location' argument.");
372: }
373: this .shapeLocation = location;
374: }
375:
376: /**
377: * Returns the flag that controls whether or not the line is visible.
378: *
379: * @return A boolean.
380: */
381: public boolean isLineVisible() {
382: return this .lineVisible;
383: }
384:
385: /**
386: * Sets the flag that controls whether or not the line is visible.
387: *
388: * @param visible the flag.
389: */
390: public void setLineVisible(boolean visible) {
391: this .lineVisible = visible;
392: }
393:
394: /**
395: * Returns the line centered about (0, 0).
396: *
397: * @return The line.
398: */
399: public Shape getLine() {
400: return this .line;
401: }
402:
403: /**
404: * Sets the line. A Shape is used here, because then you can use Line2D,
405: * GeneralPath or any other Shape to represent the line.
406: *
407: * @param line the line.
408: */
409: public void setLine(Shape line) {
410: this .line = line;
411: }
412:
413: /**
414: * Returns the line paint.
415: *
416: * @return The paint.
417: */
418: public Paint getLinePaint() {
419: return this .linePaint;
420: }
421:
422: /**
423: * Sets the line paint.
424: *
425: * @param paint the paint.
426: */
427: public void setLinePaint(Paint paint) {
428: this .linePaint = paint;
429: }
430:
431: /**
432: * Returns the line stroke.
433: *
434: * @return The stroke.
435: */
436: public Stroke getLineStroke() {
437: return this .lineStroke;
438: }
439:
440: /**
441: * Sets the line stroke.
442: *
443: * @param stroke the stroke.
444: */
445: public void setLineStroke(Stroke stroke) {
446: this .lineStroke = stroke;
447: }
448:
449: /**
450: * Arranges the contents of the block, within the given constraints, and
451: * returns the block size.
452: *
453: * @param g2 the graphics device.
454: * @param constraint the constraint (<code>null</code> not permitted).
455: *
456: * @return The block size (in Java2D units, never <code>null</code>).
457: */
458: public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
459: RectangleConstraint contentConstraint = toContentConstraint(constraint);
460: LengthConstraintType w = contentConstraint
461: .getWidthConstraintType();
462: LengthConstraintType h = contentConstraint
463: .getHeightConstraintType();
464: Size2D contentSize = null;
465: if (w == LengthConstraintType.NONE) {
466: if (h == LengthConstraintType.NONE) {
467: contentSize = arrangeNN(g2);
468: } else if (h == LengthConstraintType.RANGE) {
469: throw new RuntimeException("Not yet implemented.");
470: } else if (h == LengthConstraintType.FIXED) {
471: throw new RuntimeException("Not yet implemented.");
472: }
473: } else if (w == LengthConstraintType.RANGE) {
474: if (h == LengthConstraintType.NONE) {
475: throw new RuntimeException("Not yet implemented.");
476: } else if (h == LengthConstraintType.RANGE) {
477: throw new RuntimeException("Not yet implemented.");
478: } else if (h == LengthConstraintType.FIXED) {
479: throw new RuntimeException("Not yet implemented.");
480: }
481: } else if (w == LengthConstraintType.FIXED) {
482: if (h == LengthConstraintType.NONE) {
483: throw new RuntimeException("Not yet implemented.");
484: } else if (h == LengthConstraintType.RANGE) {
485: throw new RuntimeException("Not yet implemented.");
486: } else if (h == LengthConstraintType.FIXED) {
487: contentSize = new Size2D(contentConstraint.getWidth(),
488: contentConstraint.getHeight());
489: }
490: }
491: return new Size2D(calculateTotalWidth(contentSize.getWidth()),
492: calculateTotalHeight(contentSize.getHeight()));
493: }
494:
495: /**
496: * Performs the layout with no constraint, so the content size is
497: * determined by the bounds of the shape and/or line drawn to represent
498: * the series.
499: *
500: * @param g2 the graphics device.
501: *
502: * @return The content size.
503: */
504: protected Size2D arrangeNN(Graphics2D g2) {
505: Rectangle2D contentSize = new Rectangle2D.Double();
506: if (this .line != null) {
507: contentSize.setRect(this .line.getBounds2D());
508: }
509: if (this .shape != null) {
510: contentSize = contentSize.createUnion(this .shape
511: .getBounds2D());
512: }
513: return new Size2D(contentSize.getWidth(), contentSize
514: .getHeight());
515: }
516:
517: /**
518: * Draws the graphic item within the specified area.
519: *
520: * @param g2 the graphics device.
521: * @param area the area.
522: */
523: public void draw(Graphics2D g2, Rectangle2D area) {
524:
525: area = trimMargin(area);
526: drawBorder(g2, area);
527: area = trimBorder(area);
528: area = trimPadding(area);
529:
530: if (this .lineVisible) {
531: Point2D location = RectangleAnchor.coordinates(area,
532: this .shapeLocation);
533: Shape aLine = ShapeUtilities.createTranslatedShape(
534: getLine(), this .shapeAnchor, location.getX(),
535: location.getY());
536: g2.setPaint(this .linePaint);
537: g2.setStroke(this .lineStroke);
538: g2.draw(aLine);
539: }
540:
541: if (this .shapeVisible) {
542: Point2D location = RectangleAnchor.coordinates(area,
543: this .shapeLocation);
544:
545: Shape s = ShapeUtilities.createTranslatedShape(this .shape,
546: this .shapeAnchor, location.getX(), location.getY());
547: if (this .shapeFilled) {
548: Paint p = this .fillPaint;
549: if (p instanceof GradientPaint) {
550: GradientPaint gp = (GradientPaint) this .fillPaint;
551: p = this .fillPaintTransformer.transform(gp, s);
552: }
553: g2.setPaint(p);
554: g2.fill(s);
555: }
556: if (this .shapeOutlineVisible) {
557: g2.setPaint(this .outlinePaint);
558: g2.setStroke(this .outlineStroke);
559: g2.draw(s);
560: }
561: }
562:
563: }
564:
565: /**
566: * Draws the block within the specified area.
567: *
568: * @param g2 the graphics device.
569: * @param area the area.
570: * @param params ignored (<code>null</code> permitted).
571: *
572: * @return Always <code>null</code>.
573: */
574: public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
575: draw(g2, area);
576: return null;
577: }
578:
579: /**
580: * Tests this <code>LegendGraphic</code> instance for equality with an
581: * arbitrary object.
582: *
583: * @param obj the object (<code>null</code> permitted).
584: *
585: * @return A boolean.
586: */
587: public boolean equals(Object obj) {
588: if (!(obj instanceof LegendGraphic)) {
589: return false;
590: }
591: LegendGraphic that = (LegendGraphic) obj;
592: if (this .shapeVisible != that.shapeVisible) {
593: return false;
594: }
595: if (!ShapeUtilities.equal(this .shape, that.shape)) {
596: return false;
597: }
598: if (this .shapeFilled != that.shapeFilled) {
599: return false;
600: }
601: if (!PaintUtilities.equal(this .fillPaint, that.fillPaint)) {
602: return false;
603: }
604: if (!ObjectUtilities.equal(this .fillPaintTransformer,
605: that.fillPaintTransformer)) {
606: return false;
607: }
608: if (this .shapeOutlineVisible != that.shapeOutlineVisible) {
609: return false;
610: }
611: if (!PaintUtilities.equal(this .outlinePaint, that.outlinePaint)) {
612: return false;
613: }
614: if (!ObjectUtilities.equal(this .outlineStroke,
615: that.outlineStroke)) {
616: return false;
617: }
618: if (this .shapeAnchor != that.shapeAnchor) {
619: return false;
620: }
621: if (this .shapeLocation != that.shapeLocation) {
622: return false;
623: }
624: if (this .lineVisible != that.lineVisible) {
625: return false;
626: }
627: if (!ShapeUtilities.equal(this .line, that.line)) {
628: return false;
629: }
630: if (!PaintUtilities.equal(this .linePaint, that.linePaint)) {
631: return false;
632: }
633: if (!ObjectUtilities.equal(this .lineStroke, that.lineStroke)) {
634: return false;
635: }
636: return super .equals(obj);
637: }
638:
639: /**
640: * Returns a hash code for this instance.
641: *
642: * @return A hash code.
643: */
644: public int hashCode() {
645: int result = 193;
646: result = 37 * result + ObjectUtilities.hashCode(this .fillPaint);
647: // FIXME: use other fields too
648: return result;
649: }
650:
651: /**
652: * Returns a clone of this <code>LegendGraphic</code> instance.
653: *
654: * @return A clone of this <code>LegendGraphic</code> instance.
655: *
656: * @throws CloneNotSupportedException if there is a problem cloning.
657: */
658: public Object clone() throws CloneNotSupportedException {
659: LegendGraphic clone = (LegendGraphic) super .clone();
660: clone.shape = ShapeUtilities.clone(this .shape);
661: clone.line = ShapeUtilities.clone(this .line);
662: return clone;
663: }
664:
665: /**
666: * Provides serialization support.
667: *
668: * @param stream the output stream.
669: *
670: * @throws IOException if there is an I/O error.
671: */
672: private void writeObject(ObjectOutputStream stream)
673: throws IOException {
674: stream.defaultWriteObject();
675: SerialUtilities.writeShape(this .shape, stream);
676: SerialUtilities.writePaint(this .fillPaint, stream);
677: SerialUtilities.writePaint(this .outlinePaint, stream);
678: SerialUtilities.writeStroke(this .outlineStroke, stream);
679: SerialUtilities.writeShape(this .line, stream);
680: SerialUtilities.writePaint(this .linePaint, stream);
681: SerialUtilities.writeStroke(this .lineStroke, stream);
682: }
683:
684: /**
685: * Provides serialization support.
686: *
687: * @param stream the input stream.
688: *
689: * @throws IOException if there is an I/O error.
690: * @throws ClassNotFoundException if there is a classpath problem.
691: */
692: private void readObject(ObjectInputStream stream)
693: throws IOException, ClassNotFoundException {
694: stream.defaultReadObject();
695: this.shape = SerialUtilities.readShape(stream);
696: this.fillPaint = SerialUtilities.readPaint(stream);
697: this.outlinePaint = SerialUtilities.readPaint(stream);
698: this.outlineStroke = SerialUtilities.readStroke(stream);
699: this.line = SerialUtilities.readShape(stream);
700: this.linePaint = SerialUtilities.readPaint(stream);
701: this.lineStroke = SerialUtilities.readStroke(stream);
702: }
703:
704: }
|