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