001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2009, 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 * TextTitle.java 029 * -------------- 030 * (C) Copyright 2000-2009, by David Berry and Contributors. 031 * 032 * Original Author: David Berry; 033 * Contributor(s): David Gilbert (for Object Refinery Limited); 034 * Nicolas Brodu; 035 * Peter Kolb - patch 2603321; 036 * 037 * Changes (from 18-Sep-2001) 038 * -------------------------- 039 * 18-Sep-2001 : Added standard header (DG); 040 * 07-Nov-2001 : Separated the JCommon Class Library classes, JFreeChart now 041 * requires jcommon.jar (DG); 042 * 09-Jan-2002 : Updated Javadoc comments (DG); 043 * 07-Feb-2002 : Changed Insets --> Spacer in AbstractTitle.java (DG); 044 * 06-Mar-2002 : Updated import statements (DG); 045 * 25-Jun-2002 : Removed redundant imports (DG); 046 * 18-Sep-2002 : Fixed errors reported by Checkstyle (DG); 047 * 28-Oct-2002 : Small modifications while changing JFreeChart class (DG); 048 * 13-Mar-2003 : Changed width used for relative spacing to fix bug 703050 (DG); 049 * 26-Mar-2003 : Implemented Serializable (DG); 050 * 15-Jul-2003 : Fixed null pointer exception (DG); 051 * 11-Sep-2003 : Implemented Cloneable (NB) 052 * 22-Sep-2003 : Added checks for null values and throw nullpointer 053 * exceptions (TM); 054 * Background paint was not serialized. 055 * 07-Oct-2003 : Added fix for exception caused by empty string in title (DG); 056 * 29-Oct-2003 : Added workaround for text alignment in PDF output (DG); 057 * 03-Feb-2004 : Fixed bug in getPreferredWidth() method (DG); 058 * 17-Feb-2004 : Added clone() method and fixed bug in equals() method (DG); 059 * 01-Apr-2004 : Changed java.awt.geom.Dimension2D to org.jfree.ui.Size2D 060 * because of JDK bug 4976448 which persists on JDK 1.3.1. Also 061 * fixed bug in getPreferredHeight() method (DG); 062 * 29-Apr-2004 : Fixed bug in getPreferredWidth() method - see bug id 063 * 944173 (DG); 064 * 11-Jan-2005 : Removed deprecated code in preparation for the 1.0.0 065 * release (DG); 066 * 08-Feb-2005 : Updated for changes in RectangleConstraint class (DG); 067 * 11-Feb-2005 : Implemented PublicCloneable (DG); 068 * 20-Apr-2005 : Added support for tooltips (DG); 069 * 26-Apr-2005 : Removed LOGGER (DG); 070 * 06-Jun-2005 : Modified equals() to handle GradientPaint (DG); 071 * 06-Jul-2005 : Added flag to control whether or not the title expands to 072 * fit the available space (DG); 073 * 07-Oct-2005 : Added textAlignment attribute (DG); 074 * ------------- JFREECHART 1.0.x RELEASED ------------------------------------ 075 * 13-Dec-2005 : Fixed bug 1379331 - incorrect drawing with LEFT or RIGHT 076 * title placement (DG); 077 * 19-Dec-2007 : Implemented some of the missing arrangement options (DG); 078 * 28-Apr-2008 : Added option for maximum lines, and fixed minor bugs in 079 * equals() method (DG); 080 * 19-Mar-2009 : Changed ChartEntity to TitleEntity - see patch 2603321 by 081 * Peter Kolb (DG); 082 * 083 */ 084 085 package org.jfree.chart.title; 086 087 import java.awt.Color; 088 import java.awt.Font; 089 import java.awt.Graphics2D; 090 import java.awt.Paint; 091 import java.awt.geom.Rectangle2D; 092 import java.io.IOException; 093 import java.io.ObjectInputStream; 094 import java.io.ObjectOutputStream; 095 import java.io.Serializable; 096 097 import org.jfree.chart.block.BlockResult; 098 import org.jfree.chart.block.EntityBlockParams; 099 import org.jfree.chart.block.LengthConstraintType; 100 import org.jfree.chart.block.RectangleConstraint; 101 import org.jfree.chart.entity.ChartEntity; 102 import org.jfree.chart.entity.EntityCollection; 103 import org.jfree.chart.entity.StandardEntityCollection; 104 import org.jfree.chart.entity.TitleEntity; 105 import org.jfree.chart.event.TitleChangeEvent; 106 import org.jfree.data.Range; 107 import org.jfree.io.SerialUtilities; 108 import org.jfree.text.G2TextMeasurer; 109 import org.jfree.text.TextBlock; 110 import org.jfree.text.TextBlockAnchor; 111 import org.jfree.text.TextUtilities; 112 import org.jfree.ui.HorizontalAlignment; 113 import org.jfree.ui.RectangleEdge; 114 import org.jfree.ui.RectangleInsets; 115 import org.jfree.ui.Size2D; 116 import org.jfree.ui.VerticalAlignment; 117 import org.jfree.util.ObjectUtilities; 118 import org.jfree.util.PaintUtilities; 119 import org.jfree.util.PublicCloneable; 120 121 /** 122 * A chart title that displays a text string with automatic wrapping as 123 * required. 124 */ 125 public class TextTitle extends Title 126 implements Serializable, Cloneable, PublicCloneable { 127 128 /** For serialization. */ 129 private static final long serialVersionUID = 8372008692127477443L; 130 131 /** The default font. */ 132 public static final Font DEFAULT_FONT = new Font("SansSerif", Font.BOLD, 133 12); 134 135 /** The default text color. */ 136 public static final Paint DEFAULT_TEXT_PAINT = Color.black; 137 138 /** The title text. */ 139 private String text; 140 141 /** The font used to display the title. */ 142 private Font font; 143 144 /** The text alignment. */ 145 private HorizontalAlignment textAlignment; 146 147 /** The paint used to display the title text. */ 148 private transient Paint paint; 149 150 /** The background paint. */ 151 private transient Paint backgroundPaint; 152 153 /** The tool tip text (can be <code>null</code>). */ 154 private String toolTipText; 155 156 /** The URL text (can be <code>null</code>). */ 157 private String urlText; 158 159 /** The content. */ 160 private TextBlock content; 161 162 /** 163 * A flag that controls whether the title expands to fit the available 164 * space.. 165 */ 166 private boolean expandToFitSpace = false; 167 168 /** 169 * The maximum number of lines to display. 170 * 171 * @since 1.0.10 172 */ 173 private int maximumLinesToDisplay = Integer.MAX_VALUE; 174 175 /** 176 * Creates a new title, using default attributes where necessary. 177 */ 178 public TextTitle() { 179 this(""); 180 } 181 182 /** 183 * Creates a new title, using default attributes where necessary. 184 * 185 * @param text the title text (<code>null</code> not permitted). 186 */ 187 public TextTitle(String text) { 188 this(text, TextTitle.DEFAULT_FONT, TextTitle.DEFAULT_TEXT_PAINT, 189 Title.DEFAULT_POSITION, Title.DEFAULT_HORIZONTAL_ALIGNMENT, 190 Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); 191 } 192 193 /** 194 * Creates a new title, using default attributes where necessary. 195 * 196 * @param text the title text (<code>null</code> not permitted). 197 * @param font the title font (<code>null</code> not permitted). 198 */ 199 public TextTitle(String text, Font font) { 200 this(text, font, TextTitle.DEFAULT_TEXT_PAINT, Title.DEFAULT_POSITION, 201 Title.DEFAULT_HORIZONTAL_ALIGNMENT, 202 Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); 203 } 204 205 /** 206 * Creates a new title. 207 * 208 * @param text the text for the title (<code>null</code> not permitted). 209 * @param font the title font (<code>null</code> not permitted). 210 * @param paint the title paint (<code>null</code> not permitted). 211 * @param position the title position (<code>null</code> not permitted). 212 * @param horizontalAlignment the horizontal alignment (<code>null</code> 213 * not permitted). 214 * @param verticalAlignment the vertical alignment (<code>null</code> not 215 * permitted). 216 * @param padding the space to leave around the outside of the title. 217 */ 218 public TextTitle(String text, Font font, Paint paint, 219 RectangleEdge position, 220 HorizontalAlignment horizontalAlignment, 221 VerticalAlignment verticalAlignment, 222 RectangleInsets padding) { 223 224 super(position, horizontalAlignment, verticalAlignment, padding); 225 226 if (text == null) { 227 throw new NullPointerException("Null 'text' argument."); 228 } 229 if (font == null) { 230 throw new NullPointerException("Null 'font' argument."); 231 } 232 if (paint == null) { 233 throw new NullPointerException("Null 'paint' argument."); 234 } 235 this.text = text; 236 this.font = font; 237 this.paint = paint; 238 // the textAlignment and the horizontalAlignment are separate things, 239 // but it makes sense for the default textAlignment to match the 240 // title's horizontal alignment... 241 this.textAlignment = horizontalAlignment; 242 this.backgroundPaint = null; 243 this.content = null; 244 this.toolTipText = null; 245 this.urlText = null; 246 247 } 248 249 /** 250 * Returns the title text. 251 * 252 * @return The text (never <code>null</code>). 253 * 254 * @see #setText(String) 255 */ 256 public String getText() { 257 return this.text; 258 } 259 260 /** 261 * Sets the title to the specified text and sends a 262 * {@link TitleChangeEvent} to all registered listeners. 263 * 264 * @param text the text (<code>null</code> not permitted). 265 */ 266 public void setText(String text) { 267 if (text == null) { 268 throw new IllegalArgumentException("Null 'text' argument."); 269 } 270 if (!this.text.equals(text)) { 271 this.text = text; 272 notifyListeners(new TitleChangeEvent(this)); 273 } 274 } 275 276 /** 277 * Returns the text alignment. This controls how the text is aligned 278 * within the title's bounds, whereas the title's horizontal alignment 279 * controls how the title's bounding rectangle is aligned within the 280 * drawing space. 281 * 282 * @return The text alignment. 283 */ 284 public HorizontalAlignment getTextAlignment() { 285 return this.textAlignment; 286 } 287 288 /** 289 * Sets the text alignment and sends a {@link TitleChangeEvent} to 290 * all registered listeners. 291 * 292 * @param alignment the alignment (<code>null</code> not permitted). 293 */ 294 public void setTextAlignment(HorizontalAlignment alignment) { 295 if (alignment == null) { 296 throw new IllegalArgumentException("Null 'alignment' argument."); 297 } 298 this.textAlignment = alignment; 299 notifyListeners(new TitleChangeEvent(this)); 300 } 301 302 /** 303 * Returns the font used to display the title string. 304 * 305 * @return The font (never <code>null</code>). 306 * 307 * @see #setFont(Font) 308 */ 309 public Font getFont() { 310 return this.font; 311 } 312 313 /** 314 * Sets the font used to display the title string. Registered listeners 315 * are notified that the title has been modified. 316 * 317 * @param font the new font (<code>null</code> not permitted). 318 * 319 * @see #getFont() 320 */ 321 public void setFont(Font font) { 322 if (font == null) { 323 throw new IllegalArgumentException("Null 'font' argument."); 324 } 325 if (!this.font.equals(font)) { 326 this.font = font; 327 notifyListeners(new TitleChangeEvent(this)); 328 } 329 } 330 331 /** 332 * Returns the paint used to display the title string. 333 * 334 * @return The paint (never <code>null</code>). 335 * 336 * @see #setPaint(Paint) 337 */ 338 public Paint getPaint() { 339 return this.paint; 340 } 341 342 /** 343 * Sets the paint used to display the title string. Registered listeners 344 * are notified that the title has been modified. 345 * 346 * @param paint the new paint (<code>null</code> not permitted). 347 * 348 * @see #getPaint() 349 */ 350 public void setPaint(Paint paint) { 351 if (paint == null) { 352 throw new IllegalArgumentException("Null 'paint' argument."); 353 } 354 if (!this.paint.equals(paint)) { 355 this.paint = paint; 356 notifyListeners(new TitleChangeEvent(this)); 357 } 358 } 359 360 /** 361 * Returns the background paint. 362 * 363 * @return The paint (possibly <code>null</code>). 364 */ 365 public Paint getBackgroundPaint() { 366 return this.backgroundPaint; 367 } 368 369 /** 370 * Sets the background paint and sends a {@link TitleChangeEvent} to all 371 * registered listeners. If you set this attribute to <code>null</code>, 372 * no background is painted (which makes the title background transparent). 373 * 374 * @param paint the background paint (<code>null</code> permitted). 375 */ 376 public void setBackgroundPaint(Paint paint) { 377 this.backgroundPaint = paint; 378 notifyListeners(new TitleChangeEvent(this)); 379 } 380 381 /** 382 * Returns the tool tip text. 383 * 384 * @return The tool tip text (possibly <code>null</code>). 385 */ 386 public String getToolTipText() { 387 return this.toolTipText; 388 } 389 390 /** 391 * Sets the tool tip text to the specified text and sends a 392 * {@link TitleChangeEvent} to all registered listeners. 393 * 394 * @param text the text (<code>null</code> permitted). 395 */ 396 public void setToolTipText(String text) { 397 this.toolTipText = text; 398 notifyListeners(new TitleChangeEvent(this)); 399 } 400 401 /** 402 * Returns the URL text. 403 * 404 * @return The URL text (possibly <code>null</code>). 405 */ 406 public String getURLText() { 407 return this.urlText; 408 } 409 410 /** 411 * Sets the URL text to the specified text and sends a 412 * {@link TitleChangeEvent} to all registered listeners. 413 * 414 * @param text the text (<code>null</code> permitted). 415 */ 416 public void setURLText(String text) { 417 this.urlText = text; 418 notifyListeners(new TitleChangeEvent(this)); 419 } 420 421 /** 422 * Returns the flag that controls whether or not the title expands to fit 423 * the available space. 424 * 425 * @return The flag. 426 */ 427 public boolean getExpandToFitSpace() { 428 return this.expandToFitSpace; 429 } 430 431 /** 432 * Sets the flag that controls whether the title expands to fit the 433 * available space, and sends a {@link TitleChangeEvent} to all registered 434 * listeners. 435 * 436 * @param expand the flag. 437 */ 438 public void setExpandToFitSpace(boolean expand) { 439 this.expandToFitSpace = expand; 440 notifyListeners(new TitleChangeEvent(this)); 441 } 442 443 /** 444 * Returns the maximum number of lines to display. 445 * 446 * @return The maximum. 447 * 448 * @since 1.0.10 449 * 450 * @see #setMaximumLinesToDisplay(int) 451 */ 452 public int getMaximumLinesToDisplay() { 453 return this.maximumLinesToDisplay; 454 } 455 456 /** 457 * Sets the maximum number of lines to display and sends a 458 * {@link TitleChangeEvent} to all registered listeners. 459 * 460 * @param max the maximum. 461 * 462 * @since 1.0.10. 463 * 464 * @see #getMaximumLinesToDisplay() 465 */ 466 public void setMaximumLinesToDisplay(int max) { 467 this.maximumLinesToDisplay = max; 468 notifyListeners(new TitleChangeEvent(this)); 469 } 470 471 /** 472 * Arranges the contents of the block, within the given constraints, and 473 * returns the block size. 474 * 475 * @param g2 the graphics device. 476 * @param constraint the constraint (<code>null</code> not permitted). 477 * 478 * @return The block size (in Java2D units, never <code>null</code>). 479 */ 480 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { 481 RectangleConstraint cc = toContentConstraint(constraint); 482 LengthConstraintType w = cc.getWidthConstraintType(); 483 LengthConstraintType h = cc.getHeightConstraintType(); 484 Size2D contentSize = null; 485 if (w == LengthConstraintType.NONE) { 486 if (h == LengthConstraintType.NONE) { 487 contentSize = arrangeNN(g2); 488 } 489 else if (h == LengthConstraintType.RANGE) { 490 throw new RuntimeException("Not yet implemented."); 491 } 492 else if (h == LengthConstraintType.FIXED) { 493 throw new RuntimeException("Not yet implemented."); 494 } 495 } 496 else if (w == LengthConstraintType.RANGE) { 497 if (h == LengthConstraintType.NONE) { 498 contentSize = arrangeRN(g2, cc.getWidthRange()); 499 } 500 else if (h == LengthConstraintType.RANGE) { 501 contentSize = arrangeRR(g2, cc.getWidthRange(), 502 cc.getHeightRange()); 503 } 504 else if (h == LengthConstraintType.FIXED) { 505 throw new RuntimeException("Not yet implemented."); 506 } 507 } 508 else if (w == LengthConstraintType.FIXED) { 509 if (h == LengthConstraintType.NONE) { 510 contentSize = arrangeFN(g2, cc.getWidth()); 511 } 512 else if (h == LengthConstraintType.RANGE) { 513 throw new RuntimeException("Not yet implemented."); 514 } 515 else if (h == LengthConstraintType.FIXED) { 516 throw new RuntimeException("Not yet implemented."); 517 } 518 } 519 return new Size2D(calculateTotalWidth(contentSize.getWidth()), 520 calculateTotalHeight(contentSize.getHeight())); 521 } 522 523 /** 524 * Arranges the content for this title assuming no bounds on the width 525 * or the height, and returns the required size. This will reflect the 526 * fact that a text title positioned on the left or right of a chart will 527 * be rotated by 90 degrees. 528 * 529 * @param g2 the graphics target. 530 * 531 * @return The content size. 532 * 533 * @since 1.0.9 534 */ 535 protected Size2D arrangeNN(Graphics2D g2) { 536 Range max = new Range(0.0, Float.MAX_VALUE); 537 return arrangeRR(g2, max, max); 538 } 539 540 /** 541 * Arranges the content for this title assuming a fixed width and no bounds 542 * on the height, and returns the required size. This will reflect the 543 * fact that a text title positioned on the left or right of a chart will 544 * be rotated by 90 degrees. 545 * 546 * @param g2 the graphics target. 547 * @param w the width. 548 * 549 * @return The content size. 550 * 551 * @since 1.0.9 552 */ 553 protected Size2D arrangeFN(Graphics2D g2, double w) { 554 RectangleEdge position = getPosition(); 555 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { 556 float maxWidth = (float) w; 557 g2.setFont(this.font); 558 this.content = TextUtilities.createTextBlock(this.text, this.font, 559 this.paint, maxWidth, this.maximumLinesToDisplay, 560 new G2TextMeasurer(g2)); 561 this.content.setLineAlignment(this.textAlignment); 562 Size2D contentSize = this.content.calculateDimensions(g2); 563 if (this.expandToFitSpace) { 564 return new Size2D(maxWidth, contentSize.getHeight()); 565 } 566 else { 567 return contentSize; 568 } 569 } 570 else if (position == RectangleEdge.LEFT || position 571 == RectangleEdge.RIGHT) { 572 float maxWidth = Float.MAX_VALUE; 573 g2.setFont(this.font); 574 this.content = TextUtilities.createTextBlock(this.text, this.font, 575 this.paint, maxWidth, this.maximumLinesToDisplay, 576 new G2TextMeasurer(g2)); 577 this.content.setLineAlignment(this.textAlignment); 578 Size2D contentSize = this.content.calculateDimensions(g2); 579 580 // transpose the dimensions, because the title is rotated 581 if (this.expandToFitSpace) { 582 return new Size2D(contentSize.getHeight(), maxWidth); 583 } 584 else { 585 return new Size2D(contentSize.height, contentSize.width); 586 } 587 } 588 else { 589 throw new RuntimeException("Unrecognised exception."); 590 } 591 } 592 593 /** 594 * Arranges the content for this title assuming a range constraint for the 595 * width and no bounds on the height, and returns the required size. This 596 * will reflect the fact that a text title positioned on the left or right 597 * of a chart will be rotated by 90 degrees. 598 * 599 * @param g2 the graphics target. 600 * @param widthRange the range for the width. 601 * 602 * @return The content size. 603 * 604 * @since 1.0.9 605 */ 606 protected Size2D arrangeRN(Graphics2D g2, Range widthRange) { 607 Size2D s = arrangeNN(g2); 608 if (widthRange.contains(s.getWidth())) { 609 return s; 610 } 611 double ww = widthRange.constrain(s.getWidth()); 612 return arrangeFN(g2, ww); 613 } 614 615 /** 616 * Returns the content size for the title. This will reflect the fact that 617 * a text title positioned on the left or right of a chart will be rotated 618 * 90 degrees. 619 * 620 * @param g2 the graphics device. 621 * @param widthRange the width range. 622 * @param heightRange the height range. 623 * 624 * @return The content size. 625 */ 626 protected Size2D arrangeRR(Graphics2D g2, Range widthRange, 627 Range heightRange) { 628 RectangleEdge position = getPosition(); 629 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { 630 float maxWidth = (float) widthRange.getUpperBound(); 631 g2.setFont(this.font); 632 this.content = TextUtilities.createTextBlock(this.text, this.font, 633 this.paint, maxWidth, this.maximumLinesToDisplay, 634 new G2TextMeasurer(g2)); 635 this.content.setLineAlignment(this.textAlignment); 636 Size2D contentSize = this.content.calculateDimensions(g2); 637 if (this.expandToFitSpace) { 638 return new Size2D(maxWidth, contentSize.getHeight()); 639 } 640 else { 641 return contentSize; 642 } 643 } 644 else if (position == RectangleEdge.LEFT || position 645 == RectangleEdge.RIGHT) { 646 float maxWidth = (float) heightRange.getUpperBound(); 647 g2.setFont(this.font); 648 this.content = TextUtilities.createTextBlock(this.text, this.font, 649 this.paint, maxWidth, this.maximumLinesToDisplay, 650 new G2TextMeasurer(g2)); 651 this.content.setLineAlignment(this.textAlignment); 652 Size2D contentSize = this.content.calculateDimensions(g2); 653 654 // transpose the dimensions, because the title is rotated 655 if (this.expandToFitSpace) { 656 return new Size2D(contentSize.getHeight(), maxWidth); 657 } 658 else { 659 return new Size2D(contentSize.height, contentSize.width); 660 } 661 } 662 else { 663 throw new RuntimeException("Unrecognised exception."); 664 } 665 } 666 667 /** 668 * Draws the title on a Java 2D graphics device (such as the screen or a 669 * printer). 670 * 671 * @param g2 the graphics device. 672 * @param area the area allocated for the title. 673 */ 674 public void draw(Graphics2D g2, Rectangle2D area) { 675 draw(g2, area, null); 676 } 677 678 /** 679 * Draws the block within the specified area. 680 * 681 * @param g2 the graphics device. 682 * @param area the area. 683 * @param params if this is an instance of {@link EntityBlockParams} it 684 * is used to determine whether or not an 685 * {@link EntityCollection} is returned by this method. 686 * 687 * @return An {@link EntityCollection} containing a chart entity for the 688 * title, or <code>null</code>. 689 */ 690 public Object draw(Graphics2D g2, Rectangle2D area, Object params) { 691 if (this.content == null) { 692 return null; 693 } 694 area = trimMargin(area); 695 drawBorder(g2, area); 696 if (this.text.equals("")) { 697 return null; 698 } 699 ChartEntity entity = null; 700 if (params instanceof EntityBlockParams) { 701 EntityBlockParams p = (EntityBlockParams) params; 702 if (p.getGenerateEntities()) { 703 entity = new TitleEntity(area, this, this.toolTipText, 704 this.urlText); 705 } 706 } 707 area = trimBorder(area); 708 if (this.backgroundPaint != null) { 709 g2.setPaint(this.backgroundPaint); 710 g2.fill(area); 711 } 712 area = trimPadding(area); 713 RectangleEdge position = getPosition(); 714 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { 715 drawHorizontal(g2, area); 716 } 717 else if (position == RectangleEdge.LEFT 718 || position == RectangleEdge.RIGHT) { 719 drawVertical(g2, area); 720 } 721 BlockResult result = new BlockResult(); 722 if (entity != null) { 723 StandardEntityCollection sec = new StandardEntityCollection(); 724 sec.add(entity); 725 result.setEntityCollection(sec); 726 } 727 return result; 728 } 729 730 /** 731 * Draws a the title horizontally within the specified area. This method 732 * will be called from the {@link #draw(Graphics2D, Rectangle2D) draw} 733 * method. 734 * 735 * @param g2 the graphics device. 736 * @param area the area for the title. 737 */ 738 protected void drawHorizontal(Graphics2D g2, Rectangle2D area) { 739 Rectangle2D titleArea = (Rectangle2D) area.clone(); 740 g2.setFont(this.font); 741 g2.setPaint(this.paint); 742 TextBlockAnchor anchor = null; 743 float x = 0.0f; 744 HorizontalAlignment horizontalAlignment = getHorizontalAlignment(); 745 if (horizontalAlignment == HorizontalAlignment.LEFT) { 746 x = (float) titleArea.getX(); 747 anchor = TextBlockAnchor.TOP_LEFT; 748 } 749 else if (horizontalAlignment == HorizontalAlignment.RIGHT) { 750 x = (float) titleArea.getMaxX(); 751 anchor = TextBlockAnchor.TOP_RIGHT; 752 } 753 else if (horizontalAlignment == HorizontalAlignment.CENTER) { 754 x = (float) titleArea.getCenterX(); 755 anchor = TextBlockAnchor.TOP_CENTER; 756 } 757 float y = 0.0f; 758 RectangleEdge position = getPosition(); 759 if (position == RectangleEdge.TOP) { 760 y = (float) titleArea.getY(); 761 } 762 else if (position == RectangleEdge.BOTTOM) { 763 y = (float) titleArea.getMaxY(); 764 if (horizontalAlignment == HorizontalAlignment.LEFT) { 765 anchor = TextBlockAnchor.BOTTOM_LEFT; 766 } 767 else if (horizontalAlignment == HorizontalAlignment.CENTER) { 768 anchor = TextBlockAnchor.BOTTOM_CENTER; 769 } 770 else if (horizontalAlignment == HorizontalAlignment.RIGHT) { 771 anchor = TextBlockAnchor.BOTTOM_RIGHT; 772 } 773 } 774 this.content.draw(g2, x, y, anchor); 775 } 776 777 /** 778 * Draws a the title vertically within the specified area. This method 779 * will be called from the {@link #draw(Graphics2D, Rectangle2D) draw} 780 * method. 781 * 782 * @param g2 the graphics device. 783 * @param area the area for the title. 784 */ 785 protected void drawVertical(Graphics2D g2, Rectangle2D area) { 786 Rectangle2D titleArea = (Rectangle2D) area.clone(); 787 g2.setFont(this.font); 788 g2.setPaint(this.paint); 789 TextBlockAnchor anchor = null; 790 float y = 0.0f; 791 VerticalAlignment verticalAlignment = getVerticalAlignment(); 792 if (verticalAlignment == VerticalAlignment.TOP) { 793 y = (float) titleArea.getY(); 794 anchor = TextBlockAnchor.TOP_RIGHT; 795 } 796 else if (verticalAlignment == VerticalAlignment.BOTTOM) { 797 y = (float) titleArea.getMaxY(); 798 anchor = TextBlockAnchor.TOP_LEFT; 799 } 800 else if (verticalAlignment == VerticalAlignment.CENTER) { 801 y = (float) titleArea.getCenterY(); 802 anchor = TextBlockAnchor.TOP_CENTER; 803 } 804 float x = 0.0f; 805 RectangleEdge position = getPosition(); 806 if (position == RectangleEdge.LEFT) { 807 x = (float) titleArea.getX(); 808 } 809 else if (position == RectangleEdge.RIGHT) { 810 x = (float) titleArea.getMaxX(); 811 if (verticalAlignment == VerticalAlignment.TOP) { 812 anchor = TextBlockAnchor.BOTTOM_RIGHT; 813 } 814 else if (verticalAlignment == VerticalAlignment.CENTER) { 815 anchor = TextBlockAnchor.BOTTOM_CENTER; 816 } 817 else if (verticalAlignment == VerticalAlignment.BOTTOM) { 818 anchor = TextBlockAnchor.BOTTOM_LEFT; 819 } 820 } 821 this.content.draw(g2, x, y, anchor, x, y, -Math.PI / 2.0); 822 } 823 824 /** 825 * Tests this title for equality with another object. 826 * 827 * @param obj the object (<code>null</code> permitted). 828 * 829 * @return <code>true</code> or <code>false</code>. 830 */ 831 public boolean equals(Object obj) { 832 if (obj == this) { 833 return true; 834 } 835 if (!(obj instanceof TextTitle)) { 836 return false; 837 } 838 TextTitle that = (TextTitle) obj; 839 if (!ObjectUtilities.equal(this.text, that.text)) { 840 return false; 841 } 842 if (!ObjectUtilities.equal(this.font, that.font)) { 843 return false; 844 } 845 if (!PaintUtilities.equal(this.paint, that.paint)) { 846 return false; 847 } 848 if (this.textAlignment != that.textAlignment) { 849 return false; 850 } 851 if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) { 852 return false; 853 } 854 if (this.maximumLinesToDisplay != that.maximumLinesToDisplay) { 855 return false; 856 } 857 if (this.expandToFitSpace != that.expandToFitSpace) { 858 return false; 859 } 860 if (!ObjectUtilities.equal(this.toolTipText, that.toolTipText)) { 861 return false; 862 } 863 if (!ObjectUtilities.equal(this.urlText, that.urlText)) { 864 return false; 865 } 866 return super.equals(obj); 867 } 868 869 /** 870 * Returns a hash code. 871 * 872 * @return A hash code. 873 */ 874 public int hashCode() { 875 int result = super.hashCode(); 876 result = 29 * result + (this.text != null ? this.text.hashCode() : 0); 877 result = 29 * result + (this.font != null ? this.font.hashCode() : 0); 878 result = 29 * result + (this.paint != null ? this.paint.hashCode() : 0); 879 result = 29 * result + (this.backgroundPaint != null 880 ? this.backgroundPaint.hashCode() : 0); 881 return result; 882 } 883 884 /** 885 * Returns a clone of this object. 886 * 887 * @return A clone. 888 * 889 * @throws CloneNotSupportedException never. 890 */ 891 public Object clone() throws CloneNotSupportedException { 892 return super.clone(); 893 } 894 895 /** 896 * Provides serialization support. 897 * 898 * @param stream the output stream. 899 * 900 * @throws IOException if there is an I/O error. 901 */ 902 private void writeObject(ObjectOutputStream stream) throws IOException { 903 stream.defaultWriteObject(); 904 SerialUtilities.writePaint(this.paint, stream); 905 SerialUtilities.writePaint(this.backgroundPaint, stream); 906 } 907 908 /** 909 * Provides serialization support. 910 * 911 * @param stream the input stream. 912 * 913 * @throws IOException if there is an I/O error. 914 * @throws ClassNotFoundException if there is a classpath problem. 915 */ 916 private void readObject(ObjectInputStream stream) 917 throws IOException, ClassNotFoundException { 918 stream.defaultReadObject(); 919 this.paint = SerialUtilities.readPaint(stream); 920 this.backgroundPaint = SerialUtilities.readPaint(stream); 921 } 922 923 } 924