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