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