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