Source for org.jfree.chart.title.LegendGraphic

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ------------------
  28:  * LegendGraphic.java
  29:  * ------------------
  30:  * (C) Copyright 2004, 2005, by Object Refinery Limited.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: LegendGraphic.java,v 1.9.2.3 2005/11/24 09:57:19 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 26-Oct-2004 : Version 1 (DG);
  40:  * 21-Jan-2005 : Modified return type of RectangleAnchor.coordinates() 
  41:  *               method (DG);
  42:  * 20-Apr-2005 : Added new draw() method (DG);
  43:  * 13-May-2005 : Fixed to respect margin, border and padding settings (DG);
  44:  * 01-Sep-2005 : Implemented PublicCloneable (DG);
  45:  * 
  46:  */
  47: 
  48: package org.jfree.chart.title;
  49: 
  50: import java.awt.Graphics2D;
  51: import java.awt.Paint;
  52: import java.awt.Shape;
  53: import java.awt.Stroke;
  54: import java.awt.geom.Point2D;
  55: import java.awt.geom.Rectangle2D;
  56: import java.io.IOException;
  57: import java.io.ObjectInputStream;
  58: import java.io.ObjectOutputStream;
  59: 
  60: import org.jfree.chart.block.AbstractBlock;
  61: import org.jfree.chart.block.Block;
  62: import org.jfree.chart.block.LengthConstraintType;
  63: import org.jfree.chart.block.RectangleConstraint;
  64: import org.jfree.io.SerialUtilities;
  65: import org.jfree.ui.RectangleAnchor;
  66: import org.jfree.ui.Size2D;
  67: import org.jfree.util.ObjectUtilities;
  68: import org.jfree.util.PaintUtilities;
  69: import org.jfree.util.PublicCloneable;
  70: import org.jfree.util.ShapeUtilities;
  71: 
  72: /**
  73:  * The graphical item within a legend item.
  74:  */
  75: public class LegendGraphic extends AbstractBlock 
  76:                            implements Block, PublicCloneable {
  77:     
  78:     /** 
  79:      * A flag that controls whether or not the shape is visible - see also 
  80:      * lineVisible. 
  81:      */
  82:     private boolean shapeVisible;
  83:     
  84:     /** 
  85:      * The shape to display.  To allow for accurate positioning, the center
  86:      * of the shape should be at (0, 0). 
  87:      */
  88:     private transient Shape shape;
  89:     
  90:     /**
  91:      * Defines the location within the block to which the shape will be aligned.
  92:      */
  93:     private RectangleAnchor shapeLocation;
  94:     
  95:     /** 
  96:      * Defines the point on the shape's bounding rectangle that will be 
  97:      * aligned to the drawing location when the shape is rendered.
  98:      */
  99:     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: }