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