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