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