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