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