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 * $Id: TextTitle.java,v 1.16.2.7 2007/04/04 10:44:11 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.x 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 = new Font("SansSerif", Font.BOLD,
128 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 * @see #setText(String)
243 */
244 public String getText() {
245 return this.text;
246 }
247
248 /**
249 * Sets the title to the specified text and sends a
250 * {@link TitleChangeEvent} to all registered listeners.
251 *
252 * @param text the text (<code>null</code> not permitted).
253 */
254 public void setText(String text) {
255 if (text == null) {
256 throw new IllegalArgumentException("Null 'text' argument.");
257 }
258 if (!this.text.equals(text)) {
259 this.text = text;
260 notifyListeners(new TitleChangeEvent(this));
261 }
262 }
263
264 /**
265 * Returns the text alignment. This controls how the text is aligned
266 * within the title's bounds, whereas the title's horizontal alignment
267 * controls how the title's bounding rectangle is aligned within the
268 * drawing space.
269 *
270 * @return The text alignment.
271 */
272 public HorizontalAlignment getTextAlignment() {
273 return this.textAlignment;
274 }
275
276 /**
277 * Sets the text alignment.
278 *
279 * @param alignment the alignment (<code>null</code> not permitted).
280 */
281 public void setTextAlignment(HorizontalAlignment alignment) {
282 if (alignment == null) {
283 throw new IllegalArgumentException("Null 'alignment' argument.");
284 }
285 this.textAlignment = alignment;
286 notifyListeners(new TitleChangeEvent(this));
287 }
288
289 /**
290 * Returns the font used to display the title string.
291 *
292 * @return The font (never <code>null</code>).
293 *
294 * @see #setFont(Font)
295 */
296 public Font getFont() {
297 return this.font;
298 }
299
300 /**
301 * Sets the font used to display the title string. Registered listeners
302 * are notified that the title has been modified.
303 *
304 * @param font the new font (<code>null</code> not permitted).
305 *
306 * @see #getFont()
307 */
308 public void setFont(Font font) {
309 if (font == null) {
310 throw new IllegalArgumentException("Null 'font' argument.");
311 }
312 if (!this.font.equals(font)) {
313 this.font = font;
314 notifyListeners(new TitleChangeEvent(this));
315 }
316 }
317
318 /**
319 * Returns the paint used to display the title string.
320 *
321 * @return The paint (never <code>null</code>).
322 *
323 * @see #setPaint(Paint)
324 */
325 public Paint getPaint() {
326 return this.paint;
327 }
328
329 /**
330 * Sets the paint used to display the title string. Registered listeners
331 * are notified that the title has been modified.
332 *
333 * @param paint the new paint (<code>null</code> not permitted).
334 *
335 * @see #getPaint()
336 */
337 public void setPaint(Paint paint) {
338 if (paint == null) {
339 throw new IllegalArgumentException("Null 'paint' argument.");
340 }
341 if (!this.paint.equals(paint)) {
342 this.paint = paint;
343 notifyListeners(new TitleChangeEvent(this));
344 }
345 }
346
347 /**
348 * Returns the background paint.
349 *
350 * @return The paint (possibly <code>null</code>).
351 */
352 public Paint getBackgroundPaint() {
353 return this.backgroundPaint;
354 }
355
356 /**
357 * Sets the background paint and sends a {@link TitleChangeEvent} to all
358 * registered listeners. If you set this attribute to <code>null</code>,
359 * no background is painted (which makes the title background transparent).
360 *
361 * @param paint the background paint (<code>null</code> permitted).
362 */
363 public void setBackgroundPaint(Paint paint) {
364 this.backgroundPaint = paint;
365 notifyListeners(new TitleChangeEvent(this));
366 }
367
368 /**
369 * Returns the tool tip text.
370 *
371 * @return The tool tip text (possibly <code>null</code>).
372 */
373 public String getToolTipText() {
374 return this.toolTipText;
375 }
376
377 /**
378 * Sets the tool tip text to the specified text and sends a
379 * {@link TitleChangeEvent} to all registered listeners.
380 *
381 * @param text the text (<code>null</code> permitted).
382 */
383 public void setToolTipText(String text) {
384 this.toolTipText = text;
385 notifyListeners(new TitleChangeEvent(this));
386 }
387
388 /**
389 * Returns the URL text.
390 *
391 * @return The URL text (possibly <code>null</code>).
392 */
393 public String getURLText() {
394 return this.urlText;
395 }
396
397 /**
398 * Sets the URL text to the specified text and sends a
399 * {@link TitleChangeEvent} to all registered listeners.
400 *
401 * @param text the text (<code>null</code> permitted).
402 */
403 public void setURLText(String text) {
404 this.urlText = text;
405 notifyListeners(new TitleChangeEvent(this));
406 }
407
408 /**
409 * Returns the flag that controls whether or not the title expands to fit
410 * the available space.
411 *
412 * @return The flag.
413 */
414 public boolean getExpandToFitSpace() {
415 return this.expandToFitSpace;
416 }
417
418 /**
419 * Sets the flag that controls whether the title expands to fit the
420 * available space, and sends a {@link TitleChangeEvent} to all registered
421 * listeners.
422 *
423 * @param expand the flag.
424 */
425 public void setExpandToFitSpace(boolean expand) {
426 this.expandToFitSpace = expand;
427 notifyListeners(new TitleChangeEvent(this));
428 }
429
430 /**
431 * Arranges the contents of the block, within the given constraints, and
432 * returns the block size.
433 *
434 * @param g2 the graphics device.
435 * @param constraint the constraint (<code>null</code> not permitted).
436 *
437 * @return The block size (in Java2D units, never <code>null</code>).
438 */
439 public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) {
440 RectangleConstraint cc = toContentConstraint(constraint);
441 LengthConstraintType w = cc.getWidthConstraintType();
442 LengthConstraintType h = cc.getHeightConstraintType();
443 Size2D contentSize = null;
444 if (w == LengthConstraintType.NONE) {
445 if (h == LengthConstraintType.NONE) {
446 throw new RuntimeException("Not yet implemented.");
447 }
448 else if (h == LengthConstraintType.RANGE) {
449 throw new RuntimeException("Not yet implemented.");
450 }
451 else if (h == LengthConstraintType.FIXED) {
452 throw new RuntimeException("Not yet implemented.");
453 }
454 }
455 else if (w == LengthConstraintType.RANGE) {
456 if (h == LengthConstraintType.NONE) {
457 throw new RuntimeException("Not yet implemented.");
458 }
459 else if (h == LengthConstraintType.RANGE) {
460 contentSize = arrangeRR(g2, cc.getWidthRange(),
461 cc.getHeightRange());
462 }
463 else if (h == LengthConstraintType.FIXED) {
464 throw new RuntimeException("Not yet implemented.");
465 }
466 }
467 else if (w == LengthConstraintType.FIXED) {
468 if (h == LengthConstraintType.NONE) {
469 throw new RuntimeException("Not yet implemented.");
470 }
471 else if (h == LengthConstraintType.RANGE) {
472 throw new RuntimeException("Not yet implemented.");
473 }
474 else if (h == LengthConstraintType.FIXED) {
475 throw new RuntimeException("Not yet implemented.");
476 }
477 }
478 return new Size2D(calculateTotalWidth(contentSize.getWidth()),
479 calculateTotalHeight(contentSize.getHeight()));
480 }
481
482 /**
483 * Returns the content size for the title. This will reflect the fact that
484 * a text title positioned on the left or right of a chart will be rotated
485 * 90 degrees.
486 *
487 * @param g2 the graphics device.
488 * @param widthRange the width range.
489 * @param heightRange the height range.
490 *
491 * @return The content size.
492 */
493 protected Size2D arrangeRR(Graphics2D g2, Range widthRange,
494 Range heightRange) {
495 RectangleEdge position = getPosition();
496 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) {
497 float maxWidth = (float) widthRange.getUpperBound();
498 g2.setFont(this.font);
499 this.content = TextUtilities.createTextBlock(this.text, this.font,
500 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 else if (position == RectangleEdge.LEFT || position
511 == RectangleEdge.RIGHT) {
512 float maxWidth = (float) heightRange.getUpperBound();
513 g2.setFont(this.font);
514 this.content = TextUtilities.createTextBlock(this.text, this.font,
515 this.paint, maxWidth, new G2TextMeasurer(g2));
516 this.content.setLineAlignment(this.textAlignment);
517 Size2D contentSize = this.content.calculateDimensions(g2);
518
519 // transpose the dimensions, because the title is rotated
520 if (this.expandToFitSpace) {
521 return new Size2D(contentSize.getHeight(), maxWidth);
522 }
523 else {
524 return new Size2D(contentSize.height, contentSize.width);
525 }
526 }
527 else {
528 throw new RuntimeException("Unrecognised exception.");
529 }
530 }
531
532 /**
533 * Draws the title on a Java 2D graphics device (such as the screen or a
534 * printer).
535 *
536 * @param g2 the graphics device.
537 * @param area the area allocated for the title.
538 */
539 public void draw(Graphics2D g2, Rectangle2D area) {
540 draw(g2, area, null);
541 }
542
543 /**
544 * Draws the block within the specified area.
545 *
546 * @param g2 the graphics device.
547 * @param area the area.
548 * @param params if this is an instance of {@link EntityBlockParams} it
549 * is used to determine whether or not an
550 * {@link EntityCollection} is returned by this method.
551 *
552 * @return An {@link EntityCollection} containing a chart entity for the
553 * title, or <code>null</code>.
554 */
555 public Object draw(Graphics2D g2, Rectangle2D area, Object params) {
556 if (this.content == null) {
557 return null;
558 }
559 area = trimMargin(area);
560 drawBorder(g2, area);
561 if (this.text.equals("")) {
562 return null;
563 }
564 ChartEntity entity = null;
565 if (params instanceof EntityBlockParams) {
566 EntityBlockParams p = (EntityBlockParams) params;
567 if (p.getGenerateEntities()) {
568 entity = new ChartEntity(area, this.toolTipText, this.urlText);
569 }
570 }
571 area = trimBorder(area);
572 if (this.backgroundPaint != null) {
573 g2.setPaint(this.backgroundPaint);
574 g2.fill(area);
575 }
576 area = trimPadding(area);
577 RectangleEdge position = getPosition();
578 if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) {
579 drawHorizontal(g2, area);
580 }
581 else if (position == RectangleEdge.LEFT
582 || position == RectangleEdge.RIGHT) {
583 drawVertical(g2, area);
584 }
585 BlockResult result = new BlockResult();
586 if (entity != null) {
587 StandardEntityCollection sec = new StandardEntityCollection();
588 sec.add(entity);
589 result.setEntityCollection(sec);
590 }
591 return result;
592 }
593
594 /**
595 * Draws a the title horizontally within the specified area. This method
596 * will be called from the {@link #draw(Graphics2D, Rectangle2D) draw}
597 * method.
598 *
599 * @param g2 the graphics device.
600 * @param area the area for the title.
601 */
602 protected void drawHorizontal(Graphics2D g2, Rectangle2D area) {
603 Rectangle2D titleArea = (Rectangle2D) area.clone();
604 g2.setFont(this.font);
605 g2.setPaint(this.paint);
606 TextBlockAnchor anchor = null;
607 float x = 0.0f;
608 HorizontalAlignment horizontalAlignment = getHorizontalAlignment();
609 if (horizontalAlignment == HorizontalAlignment.LEFT) {
610 x = (float) titleArea.getX();
611 anchor = TextBlockAnchor.TOP_LEFT;
612 }
613 else if (horizontalAlignment == HorizontalAlignment.RIGHT) {
614 x = (float) titleArea.getMaxX();
615 anchor = TextBlockAnchor.TOP_RIGHT;
616 }
617 else if (horizontalAlignment == HorizontalAlignment.CENTER) {
618 x = (float) titleArea.getCenterX();
619 anchor = TextBlockAnchor.TOP_CENTER;
620 }
621 float y = 0.0f;
622 RectangleEdge position = getPosition();
623 if (position == RectangleEdge.TOP) {
624 y = (float) titleArea.getY();
625 }
626 else if (position == RectangleEdge.BOTTOM) {
627 y = (float) titleArea.getMaxY();
628 if (horizontalAlignment == HorizontalAlignment.LEFT) {
629 anchor = TextBlockAnchor.BOTTOM_LEFT;
630 }
631 else if (horizontalAlignment == HorizontalAlignment.CENTER) {
632 anchor = TextBlockAnchor.BOTTOM_CENTER;
633 }
634 else if (horizontalAlignment == HorizontalAlignment.RIGHT) {
635 anchor = TextBlockAnchor.BOTTOM_RIGHT;
636 }
637 }
638 this.content.draw(g2, x, y, anchor);
639 }
640
641 /**
642 * Draws a the title vertically within the specified area. This method
643 * will be called from the {@link #draw(Graphics2D, Rectangle2D) draw}
644 * method.
645 *
646 * @param g2 the graphics device.
647 * @param area the area for the title.
648 */
649 protected void drawVertical(Graphics2D g2, Rectangle2D area) {
650 Rectangle2D titleArea = (Rectangle2D) area.clone();
651 g2.setFont(this.font);
652 g2.setPaint(this.paint);
653 TextBlockAnchor anchor = null;
654 float y = 0.0f;
655 VerticalAlignment verticalAlignment = getVerticalAlignment();
656 if (verticalAlignment == VerticalAlignment.TOP) {
657 y = (float) titleArea.getY();
658 anchor = TextBlockAnchor.TOP_RIGHT;
659 }
660 else if (verticalAlignment == VerticalAlignment.BOTTOM) {
661 y = (float) titleArea.getMaxY();
662 anchor = TextBlockAnchor.TOP_LEFT;
663 }
664 else if (verticalAlignment == VerticalAlignment.CENTER) {
665 y = (float) titleArea.getCenterY();
666 anchor = TextBlockAnchor.TOP_CENTER;
667 }
668 float x = 0.0f;
669 RectangleEdge position = getPosition();
670 if (position == RectangleEdge.LEFT) {
671 x = (float) titleArea.getX();
672 }
673 else if (position == RectangleEdge.RIGHT) {
674 x = (float) titleArea.getMaxX();
675 if (verticalAlignment == VerticalAlignment.TOP) {
676 anchor = TextBlockAnchor.BOTTOM_RIGHT;
677 }
678 else if (verticalAlignment == VerticalAlignment.CENTER) {
679 anchor = TextBlockAnchor.BOTTOM_CENTER;
680 }
681 else if (verticalAlignment == VerticalAlignment.BOTTOM) {
682 anchor = TextBlockAnchor.BOTTOM_LEFT;
683 }
684 }
685 this.content.draw(g2, x, y, anchor, x, y, -Math.PI / 2.0);
686 }
687
688 /**
689 * Tests this title for equality with another object.
690 *
691 * @param obj the object (<code>null</code> permitted).
692 *
693 * @return <code>true</code> or <code>false</code>.
694 */
695 public boolean equals(Object obj) {
696 if (obj == this) {
697 return true;
698 }
699 if (!(obj instanceof TextTitle)) {
700 return false;
701 }
702 if (!super.equals(obj)) {
703 return false;
704 }
705 TextTitle that = (TextTitle) obj;
706 if (!ObjectUtilities.equal(this.text, that.text)) {
707 return false;
708 }
709 if (!ObjectUtilities.equal(this.font, that.font)) {
710 return false;
711 }
712 if (!PaintUtilities.equal(this.paint, that.paint)) {
713 return false;
714 }
715 if (this.textAlignment != that.textAlignment) {
716 return false;
717 }
718 if (!PaintUtilities.equal(this.backgroundPaint, that.backgroundPaint)) {
719 return false;
720 }
721 return true;
722 }
723
724 /**
725 * Returns a hash code.
726 *
727 * @return A hash code.
728 */
729 public int hashCode() {
730 int result = super.hashCode();
731 result = 29 * result + (this.text != null ? this.text.hashCode() : 0);
732 result = 29 * result + (this.font != null ? this.font.hashCode() : 0);
733 result = 29 * result + (this.paint != null ? this.paint.hashCode() : 0);
734 result = 29 * result + (this.backgroundPaint != null
735 ? this.backgroundPaint.hashCode() : 0);
736 return result;
737 }
738
739 /**
740 * Returns a clone of this object.
741 *
742 * @return A clone.
743 *
744 * @throws CloneNotSupportedException never.
745 */
746 public Object clone() throws CloneNotSupportedException {
747 return super.clone();
748 }
749
750 /**
751 * Provides serialization support.
752 *
753 * @param stream the output stream.
754 *
755 * @throws IOException if there is an I/O error.
756 */
757 private void writeObject(ObjectOutputStream stream) throws IOException {
758 stream.defaultWriteObject();
759 SerialUtilities.writePaint(this.paint, stream);
760 SerialUtilities.writePaint(this.backgroundPaint, stream);
761 }
762
763 /**
764 * Provides serialization support.
765 *
766 * @param stream the input stream.
767 *
768 * @throws IOException if there is an I/O error.
769 * @throws ClassNotFoundException if there is a classpath problem.
770 */
771 private void readObject(ObjectInputStream stream)
772 throws IOException, ClassNotFoundException
773 {
774 stream.defaultReadObject();
775 this.paint = SerialUtilities.readPaint(stream);
776 this.backgroundPaint = SerialUtilities.readPaint(stream);
777 }
778
779 }
780