Frames | No Frames |
1: /* ======================================================================== 2: * JCommon : a free general purpose class library for the Java(tm) platform 3: * ======================================================================== 4: * 5: * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 6: * 7: * Project Info: http://www.jfree.org/jcommon/index.html 8: * 9: * This library is free software; you can redistribute it and/or modify it 10: * under the terms of the GNU Lesser General Public License as published by 11: * the Free Software Foundation; either version 2.1 of the License, or 12: * (at your option) any later version. 13: * 14: * This library is distributed in the hope that it will be useful, but 15: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 17: * License for more details. 18: * 19: * You should have received a copy of the GNU Lesser General Public 20: * License along with this library; if not, write to the Free Software 21: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 22: * USA. 23: * 24: * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 25: * in the United States and other countries.] 26: * 27: * -------------- 28: * TextBlock.java 29: * -------------- 30: * (C) Copyright 2003, 2004, by Object Refinery Limited and Contributors. 31: * 32: * Original Author: David Gilbert (for Object Refinery Limited); 33: * Contributor(s): -; 34: * 35: * $Id: TextBlock.java,v 1.15 2007/11/02 17:50:35 taqua Exp $ 36: * 37: * Changes 38: * ------- 39: * 07-Nov-2003 : Version 1 (DG); 40: * 22-Dec-2003 : Added workaround for Java bug 4245442 (DG); 41: * 09-Jan-2004 : Added an extra draw() method for no rotation case (DG); 42: * 25-Feb-2004 : Added getLines() method (DG); 43: * 22-Mar-2004 : Added equals() method and implemented Serializable (DG); 44: * 24-Mar-2004 : Added 'paint' argument to addLine() method (DG); 45: * 01-Apr-2004 : Changed java.awt.geom.Dimension2D to org.jfree.ui.Size2D 46: * because of JDK bug 4976448 which persists on JDK 1.3.1 (DG); 47: * 04-Oct-2004 : Renamed ShapeUtils --> ShapeUtilities (DG); 48: * 49: */ 50: 51: package org.jfree.text; 52: 53: import java.awt.Font; 54: import java.awt.Graphics2D; 55: import java.awt.Paint; 56: import java.awt.Shape; 57: import java.awt.geom.Rectangle2D; 58: import java.io.Serializable; 59: import java.util.Collections; 60: import java.util.Iterator; 61: import java.util.List; 62: 63: import org.jfree.ui.HorizontalAlignment; 64: import org.jfree.ui.Size2D; 65: import org.jfree.ui.TextAnchor; 66: import org.jfree.util.ShapeUtilities; 67: 68: /** 69: * A list of {@link TextLine} objects that form a block of text. 70: * 71: * @see TextUtilities#createTextBlock(String, Font, Paint) 72: * 73: * @author David Gilbert 74: */ 75: public class TextBlock implements Serializable { 76: 77: /** For serialization. */ 78: private static final long serialVersionUID = -4333175719424385526L; 79: 80: /** Storage for the lines of text. */ 81: private List lines; 82: 83: /** The alignment of the lines. */ 84: private HorizontalAlignment lineAlignment; 85: 86: /** 87: * Creates a new empty text block. 88: */ 89: public TextBlock() { 90: this.lines = new java.util.ArrayList(); 91: this.lineAlignment = HorizontalAlignment.CENTER; 92: } 93: 94: /** 95: * Returns the alignment of the lines of text within the block. 96: * 97: * @return The alignment (never <code>null</code>). 98: */ 99: 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: }