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
083 implements Block, 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("Null 'fillPaint' argument.");
158 }
159 this.shapeVisible = true;
160 this.shape = shape;
161 this.shapeAnchor = RectangleAnchor.CENTER;
162 this.shapeLocation = RectangleAnchor.CENTER;
163 this.shapeFilled = true;
164 this.fillPaint = fillPaint;
165 this.fillPaintTransformer = new StandardGradientPaintTransformer();
166 setPadding(2.0, 2.0, 2.0, 2.0);
167 }
168
169 /**
170 * Returns a flag that controls whether or not the shape
171 * is visible.
172 *
173 * @return A boolean.
174 */
175 public boolean isShapeVisible() {
176 return this.shapeVisible;
177 }
178
179 /**
180 * Sets a flag that controls whether or not the shape is
181 * visible.
182 *
183 * @param visible the flag.
184 */
185 public void setShapeVisible(boolean visible) {
186 this.shapeVisible = visible;
187 }
188
189 /**
190 * Returns the shape.
191 *
192 * @return The shape.
193 */
194 public Shape getShape() {
195 return this.shape;
196 }
197
198 /**
199 * Sets the shape.
200 *
201 * @param shape the shape.
202 */
203 public void setShape(Shape shape) {
204 this.shape = shape;
205 }
206
207 /**
208 * Returns a flag that controls whether or not the shapes
209 * are filled.
210 *
211 * @return A boolean.
212 */
213 public boolean isShapeFilled() {
214 return this.shapeFilled;
215 }
216
217 /**
218 * Sets a flag that controls whether or not the shape is
219 * filled.
220 *
221 * @param filled the flag.
222 */
223 public void setShapeFilled(boolean filled) {
224 this.shapeFilled = filled;
225 }
226
227 /**
228 * Returns the paint used to fill the shape.
229 *
230 * @return The fill paint.
231 */
232 public Paint getFillPaint() {
233 return this.fillPaint;
234 }
235
236 /**
237 * Sets the paint used to fill the shape.
238 *
239 * @param paint the paint.
240 */
241 public void setFillPaint(Paint paint) {
242 this.fillPaint = paint;
243 }
244
245 /**
246 * Returns the transformer used when the fill paint is an instance of
247 * <code>GradientPaint</code>.
248 *
249 * @return The transformer (never <code>null</code>).
250 *
251 * @since 1.0.4.
252 */
253 public GradientPaintTransformer getFillPaintTransformer() {
254 return this.fillPaintTransformer;
255 }
256
257 /**
258 * Sets the transformer used when the fill paint is an instance of
259 * <code>GradientPaint</code>.
260 *
261 * @param transformer the transformer (<code>null</code> not permitted).
262 *
263 * @since 1.0.4
264 */
265 public void setFillPaintTransformer(GradientPaintTransformer transformer) {
266 if (transformer == null) {
267 throw new IllegalArgumentException("Null 'transformer' argument.");
268 }
269 this.fillPaintTransformer = transformer;
270 }
271
272 /**
273 * Returns a flag that controls whether the shape outline is visible.
274 *
275 * @return A boolean.
276 */
277 public boolean isShapeOutlineVisible() {
278 return this.shapeOutlineVisible;
279 }
280
281 /**
282 * Sets a flag that controls whether or not the shape outline
283 * is visible.
284 *
285 * @param visible the flag.
286 */
287 public void setShapeOutlineVisible(boolean visible) {
288 this.shapeOutlineVisible = visible;
289 }
290
291 /**
292 * Returns the outline paint.
293 *
294 * @return The paint.
295 */
296 public Paint getOutlinePaint() {
297 return this.outlinePaint;
298 }
299
300 /**
301 * Sets the outline paint.
302 *
303 * @param paint the paint.
304 */
305 public void setOutlinePaint(Paint paint) {
306 this.outlinePaint = paint;
307 }
308
309 /**
310 * Returns the outline stroke.
311 *
312 * @return The stroke.
313 */
314 public Stroke getOutlineStroke() {
315 return this.outlineStroke;
316 }
317
318 /**
319 * Sets the outline stroke.
320 *
321 * @param stroke the stroke.
322 */
323 public void setOutlineStroke(Stroke stroke) {
324 this.outlineStroke = stroke;
325 }
326
327 /**
328 * Returns the shape anchor.
329 *
330 * @return The shape anchor.
331 */
332 public RectangleAnchor getShapeAnchor() {
333 return this.shapeAnchor;
334 }
335
336 /**
337 * Sets the shape anchor. This defines a point on the shapes bounding
338 * rectangle that will be used to align the shape to a location.
339 *
340 * @param anchor the anchor (<code>null</code> not permitted).
341 */
342 public void setShapeAnchor(RectangleAnchor anchor) {
343 if (anchor == null) {
344 throw new IllegalArgumentException("Null 'anchor' argument.");
345 }
346 this.shapeAnchor = anchor;
347 }
348
349 /**
350 * Returns the shape location.
351 *
352 * @return The shape location.
353 */
354 public RectangleAnchor getShapeLocation() {
355 return this.shapeLocation;
356 }
357
358 /**
359 * Sets the shape location. This defines a point within the drawing
360 * area that will be used to align the shape to.
361 *
362 * @param location the location (<code>null</code> not permitted).
363 */
364 public void setShapeLocation(RectangleAnchor location) {
365 if (location == null) {
366 throw new IllegalArgumentException("Null 'location' argument.");
367 }
368 this.shapeLocation = location;
369 }
370
371 /**
372 * Returns the flag that controls whether or not the line is visible.
373 *
374 * @return A boolean.
375 */
376 public boolean isLineVisible() {
377 return this.lineVisible;
378 }
379
380 /**
381 * Sets the flag that controls whether or not the line is visible.
382 *
383 * @param visible the flag.
384 */
385 public void setLineVisible(boolean visible) {
386 this.lineVisible = visible;
387 }
388
389 /**
390 * Returns the line centered about (0, 0).
391 *
392 * @return The line.
393 */
394 public Shape getLine() {
395 return this.line;
396 }
397
398 /**
399 * Sets the line. A Shape is used here, because then you can use Line2D,
400 * GeneralPath or any other Shape to represent the line.
401 *
402 * @param line the line.
403 */
404 public void setLine(Shape line) {
405 this.line = line;
406 }
407
408 /**
409 * Returns the line paint.
410 *
411 * @return The paint.
412 */
413 public Paint getLinePaint() {
414 return this.linePaint;
415 }
416
417 /**
418 * Sets the line paint.
419 *
420 * @param paint the paint.
421 */
422 public void setLinePaint(Paint paint) {
423 this.linePaint = paint;
424 }
425
426 /**
427 * Returns the line stroke.
428 *
429 * @return The stroke.
430 */
431 public Stroke getLineStroke() {
432 return this.lineStroke;
433 }
434
435 /**
436 * Sets the line stroke.
437 *
438 * @param stroke the stroke.
439 */
440 public void setLineStroke(Stroke stroke) {
441 this.lineStroke = stroke;
442 }
443
444 /**
445 * Arranges the contents of the block, within the given constraints, and
446 * returns the block size.
447 *
448 * @param g2 the graphics device.
449 * @param constraint the constraint (<code>null</code> not permitted).
450 *
451 * @return The block size (in Java2D units, never <code>null</code>).
452 */
453 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
454 RectangleConstraint contentConstraint = toContentConstraint(constraint);
455 LengthConstraintType w = contentConstraint.getWidthConstraintType();
456 LengthConstraintType h = contentConstraint.getHeightConstraintType();
457 Size2D contentSize = null;
458 if (w == LengthConstraintType.NONE) {
459 if (h == LengthConstraintType.NONE) {
460 contentSize = arrangeNN(g2);
461 }
462 else if (h == LengthConstraintType.RANGE) {
463 throw new RuntimeException("Not yet implemented.");
464 }
465 else if (h == LengthConstraintType.FIXED) {
466 throw new RuntimeException("Not yet implemented.");
467 }
468 }
469 else if (w == LengthConstraintType.RANGE) {
470 if (h == LengthConstraintType.NONE) {
471 throw new RuntimeException("Not yet implemented.");
472 }
473 else if (h == LengthConstraintType.RANGE) {
474 throw new RuntimeException("Not yet implemented.");
475 }
476 else if (h == LengthConstraintType.FIXED) {
477 throw new RuntimeException("Not yet implemented.");
478 }
479 }
480 else if (w == LengthConstraintType.FIXED) {
481 if (h == LengthConstraintType.NONE) {
482 throw new RuntimeException("Not yet implemented.");
483 }
484 else if (h == LengthConstraintType.RANGE) {
485 throw new RuntimeException("Not yet implemented.");
486 }
487 else if (h == LengthConstraintType.FIXED) {
488 contentSize = new Size2D(
489 contentConstraint.getWidth(),
490 contentConstraint.getHeight()
491 );
492 }
493 }
494 return new Size2D(
495 calculateTotalWidth(contentSize.getWidth()),
496 calculateTotalHeight(contentSize.getHeight())
497 );
498 }
499
500 /**
501 * Performs the layout with no constraint, so the content size is
502 * determined by the bounds of the shape and/or line drawn to represent
503 * the series.
504 *
505 * @param g2 the graphics device.
506 *
507 * @return The content size.
508 */
509 protected Size2D arrangeNN(Graphics2D g2) {
510 Rectangle2D contentSize = new Rectangle2D.Double();
511 if (this.line != null) {
512 contentSize.setRect(this.line.getBounds2D());
513 }
514 if (this.shape != null) {
515 contentSize = contentSize.createUnion(this.shape.getBounds2D());
516 }
517 return new Size2D(contentSize.getWidth(), contentSize.getHeight());
518 }
519
520 /**
521 * Draws the graphic item within the specified area.
522 *
523 * @param g2 the graphics device.
524 * @param area the area.
525 */
526 public void draw(Graphics2D g2, Rectangle2D area) {
527
528 area = trimMargin(area);
529 drawBorder(g2, area);
530 area = trimBorder(area);
531 area = trimPadding(area);
532
533 if (this.lineVisible) {
534 Point2D location = RectangleAnchor.coordinates(
535 area, this.shapeLocation
536 );
537 Shape aLine = ShapeUtilities.createTranslatedShape(
538 getLine(), this.shapeAnchor, location.getX(), location.getY()
539 );
540 g2.setPaint(this.linePaint);
541 g2.setStroke(this.lineStroke);
542 g2.draw(aLine);
543 }
544
545 if (this.shapeVisible) {
546 Point2D location = RectangleAnchor.coordinates(
547 area, this.shapeLocation
548 );
549
550 Shape s = ShapeUtilities.createTranslatedShape(
551 this.shape, this.shapeAnchor, location.getX(), location.getY()
552 );
553 if (this.shapeFilled) {
554 Paint p = this.fillPaint;
555 if (p instanceof GradientPaint) {
556 GradientPaint gp = (GradientPaint) this.fillPaint;
557 p = this.fillPaintTransformer.transform(gp, s);
558 }
559 g2.setPaint(p);
560 g2.fill(s);
561 }
562 if (this.shapeOutlineVisible) {
563 g2.setPaint(this.outlinePaint);
564 g2.setStroke(this.outlineStroke);
565 g2.draw(s);
566 }
567 }
568
569 }
570
571 /**
572 * Draws the block within the specified area.
573 *
574 * @param g2 the graphics device.
575 * @param area the area.
576 * @param params ignored (<code>null</code> permitted).
577 *
578 * @return Always <code>null</code>.
579 */
580 public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
581 draw(g2, area);
582 return null;
583 }
584
585 /**
586 * Tests this <code>LegendGraphic</code> instance for equality with an
587 * arbitrary object.
588 *
589 * @param obj the object (<code>null</code> permitted).
590 *
591 * @return A boolean.
592 */
593 public boolean equals(Object obj) {
594 if (!(obj instanceof LegendGraphic)) {
595 return false;
596 }
597 LegendGraphic that = (LegendGraphic) obj;
598 if (this.shapeVisible != that.shapeVisible) {
599 return false;
600 }
601 if (!ShapeUtilities.equal(this.shape, that.shape)) {
602 return false;
603 }
604 if (this.shapeFilled != that.shapeFilled) {
605 return false;
606 }
607 if (!PaintUtilities.equal(this.fillPaint, that.fillPaint)) {
608 return false;
609 }
610 if (!ObjectUtilities.equal(this.fillPaintTransformer,
611 that.fillPaintTransformer)) {
612 return false;
613 }
614 if (this.shapeOutlineVisible != that.shapeOutlineVisible) {
615 return false;
616 }
617 if (!PaintUtilities.equal(this.outlinePaint, that.outlinePaint)) {
618 return false;
619 }
620 if (!ObjectUtilities.equal(this.outlineStroke, that.outlineStroke)) {
621 return false;
622 }
623 if (this.shapeAnchor != that.shapeAnchor) {
624 return false;
625 }
626 if (this.shapeLocation != that.shapeLocation) {
627 return false;
628 }
629 if (this.lineVisible != that.lineVisible) {
630 return false;
631 }
632 if (!ShapeUtilities.equal(this.line, that.line)) {
633 return false;
634 }
635 if (!PaintUtilities.equal(this.linePaint, that.linePaint)) {
636 return false;
637 }
638 if (!ObjectUtilities.equal(this.lineStroke, that.lineStroke)) {
639 return false;
640 }
641 return super.equals(obj);
642 }
643
644 /**
645 * Returns a hash code for this instance.
646 *
647 * @return A hash code.
648 */
649 public int hashCode() {
650 int result = 193;
651 result = 37 * result + ObjectUtilities.hashCode(this.fillPaint);
652 // FIXME: use other fields too
653 return result;
654 }
655
656 /**
657 * Returns a clone of this <code>LegendGraphic</code> instance.
658 *
659 * @return A clone of this <code>LegendGraphic</code> instance.
660 *
661 * @throws CloneNotSupportedException if there is a problem cloning.
662 */
663 public Object clone() throws CloneNotSupportedException {
664 LegendGraphic clone = (LegendGraphic) super.clone();
665 clone.shape = ShapeUtilities.clone(this.shape);
666 clone.line = ShapeUtilities.clone(this.line);
667 return clone;
668 }
669
670 /**
671 * Provides serialization support.
672 *
673 * @param stream the output stream.
674 *
675 * @throws IOException if there is an I/O error.
676 */
677 private void writeObject(ObjectOutputStream stream) throws IOException {
678 stream.defaultWriteObject();
679 SerialUtilities.writeShape(this.shape, stream);
680 SerialUtilities.writePaint(this.fillPaint, stream);
681 SerialUtilities.writePaint(this.outlinePaint, stream);
682 SerialUtilities.writeStroke(this.outlineStroke, stream);
683 SerialUtilities.writeShape(this.line, stream);
684 SerialUtilities.writePaint(this.linePaint, stream);
685 SerialUtilities.writeStroke(this.lineStroke, stream);
686 }
687
688 /**
689 * Provides serialization support.
690 *
691 * @param stream the input stream.
692 *
693 * @throws IOException if there is an I/O error.
694 * @throws ClassNotFoundException if there is a classpath problem.
695 */
696 private void readObject(ObjectInputStream stream)
697 throws IOException, ClassNotFoundException
698 {
699 stream.defaultReadObject();
700 this.shape = SerialUtilities.readShape(stream);
701 this.fillPaint = SerialUtilities.readPaint(stream);
702 this.outlinePaint = SerialUtilities.readPaint(stream);
703 this.outlineStroke = SerialUtilities.readStroke(stream);
704 this.line = SerialUtilities.readShape(stream);
705 this.linePaint = SerialUtilities.readPaint(stream);
706 this.lineStroke = SerialUtilities.readStroke(stream);
707 }
708
709 }