1 55 56 package org.jfree.chart; 57 58 import java.awt.BasicStroke ; 59 import java.awt.Color ; 60 import java.awt.Font ; 61 import java.awt.FontMetrics ; 62 import java.awt.Graphics2D ; 63 import java.awt.Paint ; 64 import java.awt.Shape ; 65 import java.awt.Stroke ; 66 import java.awt.font.LineMetrics ; 67 import java.awt.geom.AffineTransform ; 68 import java.awt.geom.Line2D ; 69 import java.awt.geom.Point2D ; 70 import java.awt.geom.Rectangle2D ; 71 import java.io.IOException ; 72 import java.io.ObjectInputStream ; 73 import java.io.ObjectOutputStream ; 74 import java.io.Serializable ; 75 76 import org.jfree.chart.entity.EntityCollection; 77 import org.jfree.chart.entity.LegendItemEntity; 78 import org.jfree.chart.event.LegendChangeEvent; 79 import org.jfree.io.SerialUtilities; 80 import org.jfree.ui.RefineryUtilities; 81 import org.jfree.ui.TextAnchor; 82 import org.jfree.util.ObjectUtils; 83 84 90 public class StandardLegend extends Legend implements Serializable { 91 92 93 public static final Spacer DEFAULT_OUTER_GAP = new Spacer(Spacer.ABSOLUTE, 3, 3, 3, 3); 94 95 96 public static final Spacer DEFAULT_INNER_GAP = new Spacer(Spacer.ABSOLUTE, 2, 2, 2, 2); 97 98 99 public static final Stroke DEFAULT_OUTLINE_STROKE = new BasicStroke (); 100 101 102 public static final Paint DEFAULT_OUTLINE_PAINT = Color.gray; 103 104 105 public static final Paint DEFAULT_BACKGROUND_PAINT = Color.white; 106 107 108 public static final Font DEFAULT_TITLE_FONT = new Font ("SansSerif", Font.BOLD, 11); 109 110 111 public static final Font DEFAULT_ITEM_FONT = new Font ("SansSerif", Font.PLAIN, 10); 112 113 114 private Spacer outerGap; 115 116 117 private transient Stroke outlineStroke; 118 119 120 private transient Paint outlinePaint; 121 122 123 private transient Paint backgroundPaint; 124 125 126 private Spacer innerGap; 127 128 129 private String title; 130 131 132 private Font titleFont; 133 134 135 private Font itemFont; 136 137 138 private transient Paint itemPaint; 139 140 141 private boolean outlineShapes; 142 143 144 private transient Stroke shapeOutlineStroke = new BasicStroke (0.5f); 145 146 147 private transient Paint shapeOutlinePaint = Color.lightGray; 148 149 150 private boolean displaySeriesShapes; 151 152 153 private boolean displaySeriesLines; 154 155 160 public StandardLegend(JFreeChart chart) { 161 162 super(chart); 163 this.outerGap = DEFAULT_OUTER_GAP; 164 this.innerGap = DEFAULT_INNER_GAP; 165 this.backgroundPaint = DEFAULT_BACKGROUND_PAINT; 166 this.outlineStroke = DEFAULT_OUTLINE_STROKE; 167 this.outlinePaint = DEFAULT_OUTLINE_PAINT; 168 this.title = null; 169 this.titleFont = DEFAULT_TITLE_FONT; 170 this.itemFont = DEFAULT_ITEM_FONT; 171 this.itemPaint = Color.black; 172 this.displaySeriesShapes = false; 173 this.displaySeriesLines = false; 174 175 } 176 177 184 public Spacer getOuterGap() { 185 return this.outerGap; 186 } 187 188 194 public void setOuterGap(Spacer outerGap) { 195 if (outerGap == null) { 196 throw new NullPointerException ("StandardLegend.setOuterGap(..): Null argument."); 197 } 198 this.outerGap = outerGap; 199 notifyListeners(new LegendChangeEvent(this)); 200 } 201 202 203 210 public Spacer getInnerGap() { 211 return innerGap; 212 } 213 214 220 public void setInnerGap(Spacer innerGap) { 221 if (innerGap == null) { 222 throw new NullPointerException ("StandardLegend.setInnerGap(..): Null argument."); 223 } 224 this.innerGap = innerGap; 225 notifyListeners(new LegendChangeEvent(this)); 226 } 227 228 233 public Paint getBackgroundPaint() { 234 return this.backgroundPaint; 235 } 236 237 243 public void setBackgroundPaint(Paint paint) { 244 this.backgroundPaint = paint; 245 notifyListeners(new LegendChangeEvent(this)); 246 } 247 248 253 public Stroke getOutlineStroke() { 254 return this.outlineStroke; 255 } 256 257 263 public void setOutlineStroke(Stroke stroke) { 264 if (stroke == null) { 265 throw new NullPointerException ("StandardLegend.setOutlineStroke(..): null argument"); 266 } 267 this.outlineStroke = stroke; 268 notifyListeners(new LegendChangeEvent(this)); 269 } 270 271 276 public Paint getOutlinePaint() { 277 return this.outlinePaint; 278 } 279 280 285 public void setOutlinePaint(Paint paint) { 286 this.outlinePaint = paint; 287 notifyListeners(new LegendChangeEvent(this)); 288 } 289 290 295 public String getTitle() { 296 return title; 297 } 298 299 304 public void setTitle(String title) { 305 this.title = title; 306 } 307 308 313 public Font getTitleFont() { 314 return this.titleFont; 315 } 316 317 322 public void setTitleFont(Font font) { 323 this.titleFont = font; 324 notifyListeners(new LegendChangeEvent(this)); 325 } 326 327 332 public Font getItemFont() { 333 return this.itemFont; 334 } 335 336 342 public void setItemFont(Font font) { 343 this.itemFont = font; 344 notifyListeners(new LegendChangeEvent(this)); 345 } 346 347 352 public Paint getItemPaint() { 353 return this.itemPaint; 354 } 355 356 362 public void setItemPaint(Paint paint) { 363 this.itemPaint = paint; 364 notifyListeners(new LegendChangeEvent(this)); 365 } 366 367 372 public boolean getOutlineShapes() { 373 return this.outlineShapes; 374 } 375 376 381 public void setOutlineShapes(boolean flag) { 382 this.outlineShapes = flag; 383 notifyListeners(new LegendChangeEvent(this)); 384 } 385 386 391 public Stroke getShapeOutlineStroke() { 392 return this.shapeOutlineStroke; 393 } 394 395 401 public void setShapeOutlineStroke(Stroke stroke) { 402 if (stroke == null) { 403 throw new NullPointerException ( 404 "StandardLegend.setShapeOutlineStroke(..): null argument"); 405 } 406 this.shapeOutlineStroke = stroke; 407 notifyListeners(new LegendChangeEvent(this)); 408 } 409 410 415 public Paint getShapeOutlinePaint() { 416 return this.shapeOutlinePaint; 417 } 418 419 425 public void setShapeOutlinePaint(Paint paint) { 426 this.shapeOutlinePaint = paint; 427 notifyListeners(new LegendChangeEvent(this)); 428 } 429 430 435 public void setDisplaySeriesShapes(boolean flag) { 436 this.displaySeriesShapes = flag; 437 notifyListeners(new LegendChangeEvent(this)); 438 } 439 440 446 public boolean getDisplaySeriesShapes() { 447 return this.displaySeriesShapes; 448 } 449 450 455 public void setDisplaySeriesLines(boolean flag) { 456 this.displaySeriesLines = flag; 457 notifyListeners(new LegendChangeEvent(this)); 458 } 459 460 466 public boolean getDisplaySeriesLines() { 467 return this.displaySeriesLines; 468 } 469 470 480 public Rectangle2D draw(Graphics2D g2, Rectangle2D available, ChartRenderingInfo info) { 481 482 return draw(g2, available, 483 (getAnchor() & HORIZONTAL) != 0, (getAnchor() & INVERTED) != 0, 484 info); 485 486 } 487 488 499 protected Rectangle2D draw(Graphics2D g2, Rectangle2D available, 500 boolean horizontal, boolean inverted, 501 ChartRenderingInfo info) { 502 503 LegendItemCollection legendItems = getChart().getPlot().getLegendItems(); 504 505 if ((legendItems != null) && (legendItems.getItemCount() > 0)) { 506 507 DrawableLegendItem legendTitle = null; 508 509 Rectangle2D legendArea = new Rectangle2D.Double (); 510 double availableWidth = available.getWidth(); 511 double availableHeight = available.getHeight(); 512 513 Point2D translation = new Point2D.Double (); 515 516 DrawableLegendItem[] items = new DrawableLegendItem[legendItems.getItemCount()]; 518 519 if (horizontal) { 522 double xstart = available.getX() + getOuterGap().getLeftSpace(availableWidth); 523 double xlimit = available.getMaxX() 524 + getOuterGap().getRightSpace(availableWidth) - 1; 525 double maxRowWidth = 0; 526 double xoffset = 0; 527 double rowHeight = 0; 528 double totalHeight = 0; 529 boolean startingNewRow = true; 530 531 532 if (title != null && !title.equals("")) { 533 534 g2.setFont(getTitleFont()); 535 536 LegendItem titleItem = new LegendItem(title, 537 title, 538 null, 539 Color.black, 540 DEFAULT_OUTLINE_PAINT, 541 DEFAULT_OUTLINE_STROKE); 542 543 legendTitle = createDrawableLegendItem(g2, titleItem, 544 xoffset, 545 totalHeight); 546 547 rowHeight = Math.max(rowHeight, legendTitle.getHeight()); 548 xoffset += legendTitle.getWidth(); 549 } 550 551 g2.setFont(itemFont); 552 for (int i = 0; i < legendItems.getItemCount(); i++) { 553 items[i] = createDrawableLegendItem(g2, legendItems.get(i), 554 xoffset, totalHeight); 555 if ((!startingNewRow) 556 && (items[i].getX() + items[i].getWidth() + xstart > xlimit)) { 557 558 maxRowWidth = Math.max(maxRowWidth, xoffset); 559 xoffset = 0; 560 totalHeight += rowHeight; 561 i--; 562 startingNewRow = true; 563 564 } 565 else { 566 rowHeight = Math.max(rowHeight, items[i].getHeight()); 567 xoffset += items[i].getWidth(); 568 startingNewRow = false; 569 } 570 } 571 572 maxRowWidth = Math.max(maxRowWidth, xoffset); 573 totalHeight += rowHeight; 574 575 legendArea = new Rectangle2D.Double (0, 0, maxRowWidth, totalHeight); 577 578 double yloc = (inverted) 581 ? available.getMaxY() - totalHeight 582 - getOuterGap().getBottomSpace(availableHeight) 583 : available.getY() + getOuterGap().getTopSpace(availableHeight); 584 double xloc = available.getX() + available.getWidth() / 2 - maxRowWidth / 2; 585 586 translation = new Point2D.Double (xloc, yloc); 588 } 589 else { double totalHeight = 0; 591 double maxWidth = 0; 592 593 if (title != null && !title.equals("")) { 594 595 g2.setFont(getTitleFont()); 596 597 LegendItem titleItem = new LegendItem(title, 598 title, 599 null, 600 Color.black, 601 DEFAULT_OUTLINE_PAINT, 602 DEFAULT_OUTLINE_STROKE); 603 604 legendTitle = createDrawableLegendItem(g2, titleItem, 0, totalHeight); 605 606 totalHeight += legendTitle.getHeight(); 607 maxWidth = Math.max(maxWidth, legendTitle.getWidth()); 608 } 609 610 g2.setFont(itemFont); 611 for (int i = 0; i < items.length; i++) { 612 items[i] = createDrawableLegendItem(g2, legendItems.get(i), 0, totalHeight); 613 totalHeight += items[i].getHeight(); 614 maxWidth = Math.max(maxWidth, items[i].getWidth()); 615 } 616 617 legendArea = new Rectangle2D.Float (0, 0, (float) maxWidth, (float) totalHeight); 619 620 double xloc = (inverted) 623 ? available.getMaxX() - maxWidth - getOuterGap().getRightSpace(availableWidth) 624 : available.getX() + getOuterGap().getLeftSpace(availableWidth); 625 double yloc = available.getY() + (available.getHeight() / 2) - (totalHeight / 2); 626 627 translation = new Point2D.Double (xloc, yloc); 629 } 630 631 g2.translate(translation.getX(), translation.getY()); 633 634 g2.setPaint(backgroundPaint); 636 g2.fill(legendArea); 637 g2.setPaint(outlinePaint); 638 g2.setStroke(outlineStroke); 639 g2.draw(legendArea); 640 641 if (legendTitle != null) { 643 g2.setPaint(legendTitle.getItem().getPaint()); 645 g2.setPaint(this.itemPaint); 646 g2.setFont(getTitleFont()); 647 g2.drawString(legendTitle.getItem().getLabel(), 648 (float) legendTitle.getLabelPosition().getX(), 649 (float) legendTitle.getLabelPosition().getY()); 650 } 651 652 EntityCollection entities = null; 653 if (info != null) { 654 entities = info.getEntityCollection(); 655 } 656 for (int i = 0; i < items.length; i++) { 658 g2.setPaint(items[i].getItem().getPaint()); 659 Shape keyBox = items[i].getMarker(); 660 if (displaySeriesLines) { 661 g2.setStroke(items[i].getLineStroke()); 662 g2.draw(items[i].getLine()); 663 664 if (displaySeriesShapes) { 665 g2.fill(keyBox); 666 } 667 668 } 669 else { 670 g2.fill(keyBox); 671 } 672 if (getOutlineShapes()) { 673 g2.setPaint(this.shapeOutlinePaint); 674 g2.setStroke(this.shapeOutlineStroke); 675 g2.draw(keyBox); 676 } 677 g2.setPaint(this.itemPaint); 678 g2.setFont(this.itemFont); 679 RefineryUtilities.drawAlignedString(items[i].getItem().getLabel(), g2, 683 (float) items[i].getLabelPosition().getX(), 684 (float) items[i].getLabelPosition().getY(), 685 TextAnchor.CENTER_LEFT); 686 687 if (entities != null) { 688 Rectangle2D area = new Rectangle2D.Double (translation.getX() + items[i].getX(), 689 translation.getY() + items[i].getY(), 690 items[i].getWidth(), 691 items[i].getHeight()); 692 LegendItemEntity entity = new LegendItemEntity(area); 693 entity.setSeriesIndex(i); 694 entities.addEntity(entity); 695 } 696 } 697 698 g2.translate(-translation.getX(), -translation.getY()); 700 701 if (horizontal) { 702 double yy = available.getY(); 709 double yloc = (inverted) ? yy 710 : yy + legendArea.getHeight() 711 + getOuterGap().getBottomSpace(availableHeight); 712 713 return new Rectangle2D.Double (available.getX(), yloc, availableWidth, 715 availableHeight - legendArea.getHeight() 716 - getOuterGap().getTopSpace(availableHeight) 717 - getOuterGap().getBottomSpace(availableHeight)); 718 } 719 else { 720 double xloc = (inverted) ? available.getX() 727 : available.getX() 728 + legendArea.getWidth() 729 + getOuterGap().getLeftSpace(availableWidth) 730 + getOuterGap().getRightSpace(availableWidth); 731 732 733 return new Rectangle2D.Double (xloc, available.getY(), 735 availableWidth - legendArea.getWidth() 736 - getOuterGap().getLeftSpace(availableWidth) 737 - getOuterGap().getRightSpace(availableWidth), 738 availableHeight); 739 } 740 } 741 else { 742 return available; 743 } 744 } 745 746 760 private DrawableLegendItem createDrawableLegendItem(Graphics2D graphics, 761 LegendItem legendItem, 762 double x, double y) { 763 764 int innerGap = 2; 765 FontMetrics fm = graphics.getFontMetrics(); 766 LineMetrics lm = fm.getLineMetrics(legendItem.getLabel(), graphics); 767 float textAscent = lm.getAscent(); 768 float lineHeight = textAscent + lm.getDescent() + lm.getLeading(); 769 770 DrawableLegendItem item = new DrawableLegendItem(legendItem); 771 772 float xLabelLoc = (float) (x + innerGap + 1.15f * lineHeight); 773 float yLabelLoc = (float) (y + innerGap + 0.5f * lineHeight); 775 776 item.setLabelPosition(new Point2D.Float (xLabelLoc, yLabelLoc)); 777 778 float width = (float) (item.getLabelPosition().getX() - x 779 + fm.getStringBounds(legendItem.getLabel(), graphics).getWidth() 780 + 0.5 * textAscent); 781 782 float height = (2 * innerGap + lineHeight); 783 item.setBounds(x, y, width, height); 784 float boxDim = lineHeight * 0.70f; 785 float xloc = (float) (x + innerGap + 0.15f * lineHeight); 786 float yloc = (float) (y + innerGap + 0.15f * lineHeight); 787 if (this.displaySeriesLines) { 788 Line2D line = new Line2D.Float (xloc, yloc + boxDim / 2, 789 xloc + boxDim * 3, yloc + boxDim / 2); 790 item.setLineStroke(legendItem.getStroke()); 791 item.setLine(line); 792 item.setBounds(item.getX(), item.getY(), 794 item.getWidth() + boxDim * 2, 795 item.getHeight()); 796 item.setLabelPosition(new Point2D.Float (xLabelLoc + boxDim * 2, yLabelLoc)); 797 if (this.displaySeriesShapes) { 798 Shape marker = legendItem.getShape(); 799 AffineTransform transformer = AffineTransform.getTranslateInstance( 800 xloc + (boxDim * 1.5), yloc + boxDim / 2); 801 marker = transformer.createTransformedShape(marker); 802 item.setMarker(marker); 803 } 804 805 } 806 else { 807 if (this.displaySeriesShapes) { 808 Shape marker = legendItem.getShape(); 809 810 AffineTransform transformer = AffineTransform.getTranslateInstance( 811 xloc + boxDim / 2, yloc + boxDim / 2); 812 marker = transformer.createTransformedShape(marker); 813 item.setMarker(marker); 814 } 815 else { 816 item.setMarker(new Rectangle2D.Float (xloc, yloc, boxDim, boxDim)); 817 } 818 } 819 return item; 820 821 } 822 823 830 public boolean equals(Object obj) { 831 832 if (obj == null) { 833 return false; 834 } 835 836 if (obj == this) { 837 return true; 838 } 839 840 if (obj instanceof StandardLegend) { 841 StandardLegend l = (StandardLegend) obj; 842 if (super.equals(obj)) { 843 844 if (ObjectUtils.equal(this.outerGap, l.outerGap) == false) { 845 return false; 846 } 847 if (ObjectUtils.equal(this.outlineStroke, l.outlineStroke) == false) { 848 return false; 849 } 850 if (ObjectUtils.equal(this.outlinePaint, l.outlinePaint) == false) { 851 return false; 852 } 853 if (ObjectUtils.equal(this.backgroundPaint, l.backgroundPaint) == false) { 854 return false; 855 } 856 if (ObjectUtils.equal(this.innerGap, l.innerGap) == false) { 857 return false; 858 } 859 if (ObjectUtils.equal(this.title, l.title) == false) { 860 return false; 861 } 862 if (ObjectUtils.equal(this.titleFont, l.titleFont) == false) { 863 return false; 864 } 865 if (ObjectUtils.equal(this.itemFont, l.itemFont) == false) { 866 return false; 867 } 868 if (ObjectUtils.equal(this.itemPaint, l.itemPaint) == false) { 869 return false; 870 } 871 if (this.outlineShapes != l.outlineShapes) { 872 return false; 873 } 874 if (ObjectUtils.equal(this.shapeOutlineStroke, l.shapeOutlineStroke) == false) { 875 return false; 876 } 877 if (ObjectUtils.equal(this.shapeOutlinePaint, l.shapeOutlinePaint) == false) { 878 return false; 879 } 880 if (this.displaySeriesShapes == l.displaySeriesShapes) { 881 return true; 882 } 883 } 884 } 885 886 return false; 887 } 888 889 896 private void writeObject(ObjectOutputStream stream) throws IOException { 897 stream.defaultWriteObject(); 898 SerialUtilities.writeStroke(this.outlineStroke, stream); 899 SerialUtilities.writePaint(this.outlinePaint, stream); 900 SerialUtilities.writePaint(this.backgroundPaint, stream); 901 SerialUtilities.writePaint(this.itemPaint, stream); 902 SerialUtilities.writeStroke(this.shapeOutlineStroke, stream); 903 SerialUtilities.writePaint(this.shapeOutlinePaint, stream); 904 } 905 906 914 private void readObject(ObjectInputStream stream) throws IOException , ClassNotFoundException { 915 stream.defaultReadObject(); 916 this.outlineStroke = SerialUtilities.readStroke(stream); 917 this.outlinePaint = SerialUtilities.readPaint(stream); 918 this.backgroundPaint = SerialUtilities.readPaint(stream); 919 this.itemPaint = SerialUtilities.readPaint(stream); 920 this.shapeOutlineStroke = SerialUtilities.readStroke(stream); 921 this.shapeOutlinePaint = SerialUtilities.readPaint(stream); 922 } 923 924 } 925 | Popular Tags |