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