001/* ======================================================================== 002 * JCommon : a free general purpose class library for the Java(tm) platform 003 * ======================================================================== 004 * 005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jcommon/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 * TextBlock.java 029 * -------------- 030 * (C) Copyright 2003, 2004, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * $Id: TextBlock.java,v 1.15 2007/11/02 17:50:35 taqua Exp $ 036 * 037 * Changes 038 * ------- 039 * 07-Nov-2003 : Version 1 (DG); 040 * 22-Dec-2003 : Added workaround for Java bug 4245442 (DG); 041 * 09-Jan-2004 : Added an extra draw() method for no rotation case (DG); 042 * 25-Feb-2004 : Added getLines() method (DG); 043 * 22-Mar-2004 : Added equals() method and implemented Serializable (DG); 044 * 24-Mar-2004 : Added 'paint' argument to addLine() method (DG); 045 * 01-Apr-2004 : Changed java.awt.geom.Dimension2D to org.jfree.ui.Size2D 046 * because of JDK bug 4976448 which persists on JDK 1.3.1 (DG); 047 * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG); 048 * 049 */ 050 051package org.jfree.text; 052 053import java.awt.Font; 054import java.awt.Graphics2D; 055import java.awt.Paint; 056import java.awt.Shape; 057import java.awt.geom.Rectangle2D; 058import java.io.Serializable; 059import java.util.Collections; 060import java.util.Iterator; 061import java.util.List; 062 063import org.jfree.ui.HorizontalAlignment; 064import org.jfree.ui.Size2D; 065import org.jfree.ui.TextAnchor; 066import org.jfree.util.ShapeUtilities; 067 068/** 069 * A list of {@link TextLine} objects that form a block of text. 070 * 071 * @see TextUtilities#createTextBlock(String, Font, Paint) 072 * 073 * @author David Gilbert 074 */ 075public class TextBlock implements Serializable { 076 077 /** For serialization. */ 078 private static final long serialVersionUID = -4333175719424385526L; 079 080 /** Storage for the lines of text. */ 081 private List lines; 082 083 /** The alignment of the lines. */ 084 private HorizontalAlignment lineAlignment; 085 086 /** 087 * Creates a new empty text block. 088 */ 089 public TextBlock() { 090 this.lines = new java.util.ArrayList(); 091 this.lineAlignment = HorizontalAlignment.CENTER; 092 } 093 094 /** 095 * Returns the alignment of the lines of text within the block. 096 * 097 * @return The alignment (never <code>null</code>). 098 */ 099 public HorizontalAlignment getLineAlignment() { 100 return this.lineAlignment; 101 } 102 103 /** 104 * Sets the alignment of the lines of text within the block. 105 * 106 * @param alignment the alignment (<code>null</code> not permitted). 107 */ 108 public void setLineAlignment(HorizontalAlignment alignment) { 109 if (alignment == null) { 110 throw new IllegalArgumentException("Null 'alignment' argument."); 111 } 112 this.lineAlignment = alignment; 113 } 114 115 /** 116 * Adds a line of text that will be displayed using the specified font. 117 * 118 * @param text the text. 119 * @param font the font. 120 * @param paint the paint. 121 */ 122 public void addLine(final String text, final Font font, final Paint paint) { 123 addLine(new TextLine(text, font, paint)); 124 } 125 126 /** 127 * Adds a {@link TextLine} to the block. 128 * 129 * @param line the line. 130 */ 131 public void addLine(final TextLine line) { 132 this.lines.add(line); 133 } 134 135 /** 136 * Returns the last line in the block. 137 * 138 * @return The last line in the block. 139 */ 140 public TextLine getLastLine() { 141 TextLine last = null; 142 final int index = this.lines.size() - 1; 143 if (index >= 0) { 144 last = (TextLine) this.lines.get(index); 145 } 146 return last; 147 } 148 149 /** 150 * Returns an unmodifiable list containing the lines for the text block. 151 * 152 * @return A list of {@link TextLine} objects. 153 */ 154 public List getLines() { 155 return Collections.unmodifiableList(this.lines); 156 } 157 158 /** 159 * Returns the width and height of the text block. 160 * 161 * @param g2 the graphics device. 162 * 163 * @return The width and height. 164 */ 165 public Size2D calculateDimensions(final Graphics2D g2) { 166 double width = 0.0; 167 double height = 0.0; 168 final Iterator iterator = this.lines.iterator(); 169 while (iterator.hasNext()) { 170 final TextLine line = (TextLine) iterator.next(); 171 final Size2D dimension = line.calculateDimensions(g2); 172 width = Math.max(width, dimension.getWidth()); 173 height = height + dimension.getHeight(); 174 } 175 return new Size2D(width, height); 176 } 177 178 /** 179 * Returns the bounds of the text block. 180 * 181 * @param g2 the graphics device (<code>null</code> not permitted). 182 * @param anchorX the x-coordinate for the anchor point. 183 * @param anchorY the y-coordinate for the anchor point. 184 * @param anchor the text block anchor (<code>null</code> not permitted). 185 * @param rotateX the x-coordinate for the rotation point. 186 * @param rotateY the y-coordinate for the rotation point. 187 * @param angle the rotation angle. 188 * 189 * @return The bounds. 190 */ 191 public Shape calculateBounds(final Graphics2D g2, 192 final float anchorX, final float anchorY, 193 final TextBlockAnchor anchor, 194 final float rotateX, final float rotateY, 195 final double angle) { 196 197 final Size2D d = calculateDimensions(g2); 198 final float[] offsets = calculateOffsets( 199 anchor, d.getWidth(), d.getHeight() 200 ); 201 final Rectangle2D bounds = new Rectangle2D.Double( 202 anchorX + offsets[0], anchorY + offsets[1], 203 d.getWidth(), d.getHeight() 204 ); 205 final Shape rotatedBounds = ShapeUtilities.rotateShape( 206 bounds, angle, rotateX, rotateY 207 ); 208 return rotatedBounds; 209 210 } 211 212 /** 213 * Draws the text block at a specific location. 214 * 215 * @param g2 the graphics device. 216 * @param x the x-coordinate for the anchor point. 217 * @param y the y-coordinate for the anchor point. 218 * @param anchor the anchor point. 219 */ 220 public void draw(final Graphics2D g2, final float x, final float y, 221 final TextBlockAnchor anchor) { 222 draw(g2, x, y, anchor, 0.0f, 0.0f, 0.0); 223 } 224 225 /** 226 * Draws the text block, aligning it with the specified anchor point and 227 * rotating it about the specified rotation point. 228 * 229 * @param g2 the graphics device. 230 * @param anchorX the x-coordinate for the anchor point. 231 * @param anchorY the y-coordinate for the anchor point. 232 * @param anchor the point on the text block that is aligned to the 233 * anchor point. 234 * @param rotateX the x-coordinate for the rotation point. 235 * @param rotateY the x-coordinate for the rotation point. 236 * @param angle the rotation (in radians). 237 */ 238 public void draw(final Graphics2D g2, 239 final float anchorX, final float anchorY, 240 final TextBlockAnchor anchor, 241 final float rotateX, final float rotateY, 242 final double angle) { 243 244 final Size2D d = calculateDimensions(g2); 245 final float[] offsets = calculateOffsets(anchor, d.getWidth(), 246 d.getHeight()); 247 final Iterator iterator = this.lines.iterator(); 248 float yCursor = 0.0f; 249 while (iterator.hasNext()) { 250 TextLine line = (TextLine) iterator.next(); 251 Size2D dimension = line.calculateDimensions(g2); 252 float lineOffset = 0.0f; 253 if (this.lineAlignment == HorizontalAlignment.CENTER) { 254 lineOffset = (float) (d.getWidth() - dimension.getWidth()) 255 / 2.0f; 256 } 257 else if (this.lineAlignment == HorizontalAlignment.RIGHT) { 258 lineOffset = (float) (d.getWidth() - dimension.getWidth()); 259 } 260 line.draw( 261 g2, anchorX + offsets[0] + lineOffset, anchorY + offsets[1] + yCursor, 262 TextAnchor.TOP_LEFT, rotateX, rotateY, angle 263 ); 264 yCursor = yCursor + (float) dimension.getHeight(); 265 } 266 267 } 268 269 /** 270 * Calculates the x and y offsets required to align the text block with the 271 * specified anchor point. This assumes that the top left of the text 272 * block is at (0.0, 0.0). 273 * 274 * @param anchor the anchor position. 275 * @param width the width of the text block. 276 * @param height the height of the text block. 277 * 278 * @return The offsets (float[0] = x offset, float[1] = y offset). 279 */ 280 private float[] calculateOffsets(final TextBlockAnchor anchor, 281 final double width, final double height) { 282 final float[] result = new float[2]; 283 float xAdj = 0.0f; 284 float yAdj = 0.0f; 285 286 if (anchor == TextBlockAnchor.TOP_CENTER 287 || anchor == TextBlockAnchor.CENTER 288 || anchor == TextBlockAnchor.BOTTOM_CENTER) { 289 290 xAdj = (float) -width / 2.0f; 291 292 } 293 else if (anchor == TextBlockAnchor.TOP_RIGHT 294 || anchor == TextBlockAnchor.CENTER_RIGHT 295 || anchor == TextBlockAnchor.BOTTOM_RIGHT) { 296 297 xAdj = (float) -width; 298 299 } 300 301 if (anchor == TextBlockAnchor.TOP_LEFT 302 || anchor == TextBlockAnchor.TOP_CENTER 303 || anchor == TextBlockAnchor.TOP_RIGHT) { 304 305 yAdj = 0.0f; 306 307 } 308 else if (anchor == TextBlockAnchor.CENTER_LEFT 309 || anchor == TextBlockAnchor.CENTER 310 || anchor == TextBlockAnchor.CENTER_RIGHT) { 311 312 yAdj = (float) -height / 2.0f; 313 314 } 315 else if (anchor == TextBlockAnchor.BOTTOM_LEFT 316 || anchor == TextBlockAnchor.BOTTOM_CENTER 317 || anchor == TextBlockAnchor.BOTTOM_RIGHT) { 318 319 yAdj = (float) -height; 320 321 } 322 result[0] = xAdj; 323 result[1] = yAdj; 324 return result; 325 } 326 327 /** 328 * Tests this object for equality with an arbitrary object. 329 * 330 * @param obj the object to test against (<code>null</code> permitted). 331 * 332 * @return A boolean. 333 */ 334 public boolean equals(final Object obj) { 335 if (obj == this) { 336 return true; 337 } 338 if (obj instanceof TextBlock) { 339 final TextBlock block = (TextBlock) obj; 340 return this.lines.equals(block.lines); 341 } 342 return false; 343 } 344 345 /** 346 * Returns a hash code for this object. 347 * 348 * @return A hash code. 349 */ 350 public int hashCode() { 351 return (this.lines != null ? this.lines.hashCode() : 0); 352 } 353}