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