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.13 2005/10/18 13:17:16 mungady 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.Log; 67: import org.jfree.util.LogContext; 68: import org.jfree.util.ShapeUtilities; 69: 70: /** 71: * A list of {@link TextLine} objects that form a block of text. 72: * 73: * @see TextUtilities#createTextBlock(String, Font, Paint) 74: * 75: * @author David Gilbert 76: */ 77: public class TextBlock implements Serializable { 78: 79: /** For serialization. */ 80: private static final long serialVersionUID = -4333175719424385526L; 81: 82: /** Storage for the lines of text. */ 83: private List lines; 84: 85: /** The alignment of the lines. */ 86: private HorizontalAlignment lineAlignment; 87: 88: /** Access to logging facilities. */ 89: protected static final LogContext logger 90: = Log.createContext(TextBlock.class); 91: 92: /** 93: * Creates a new empty text block. 94: */ 95: public TextBlock() { 96: this.lines = new java.util.ArrayList(); 97: this.lineAlignment = HorizontalAlignment.CENTER; 98: } 99: 100: /** 101: * Returns the alignment of the lines of text within the block. 102: * 103: * @return The alignment (never <code>null</code>). 104: */ 105: public HorizontalAlignment getLineAlignment() { 106: return this.lineAlignment; 107: } 108: 109: /** 110: * Sets the alignment of the lines of text within the block. 111: * 112: * @param alignment the alignment (<code>null</code> not permitted). 113: */ 114: public void setLineAlignment(HorizontalAlignment alignment) { 115: if (alignment == null) { 116: throw new IllegalArgumentException("Null 'alignment' argument."); 117: } 118: this.lineAlignment = alignment; 119: } 120: 121: /** 122: * Adds a line of text that will be displayed using the specified font. 123: * 124: * @param text the text. 125: * @param font the font. 126: * @param paint the paint. 127: */ 128: public void addLine(final String text, final Font font, final Paint paint) { 129: addLine(new TextLine(text, font, paint)); 130: } 131: 132: /** 133: * Adds a {@link TextLine} to the block. 134: * 135: * @param line the line. 136: */ 137: public void addLine(final TextLine line) { 138: this.lines.add(line); 139: } 140: 141: /** 142: * Returns the last line in the block. 143: * 144: * @return The last line in the block. 145: */ 146: public TextLine getLastLine() { 147: TextLine last = null; 148: final int index = this.lines.size() - 1; 149: if (index >= 0) { 150: last = (TextLine) this.lines.get(index); 151: } 152: return last; 153: } 154: 155: /** 156: * Returns an unmodifiable list containing the lines for the text block. 157: * 158: * @return A list of {@link TextLine} objects. 159: */ 160: public List getLines() { 161: return Collections.unmodifiableList(this.lines); 162: } 163: 164: /** 165: * Returns the width and height of the text block. 166: * 167: * @param g2 the graphics device. 168: * 169: * @return The width and height. 170: */ 171: public Size2D calculateDimensions(final Graphics2D g2) { 172: double width = 0.0; 173: double height = 0.0; 174: final Iterator iterator = this.lines.iterator(); 175: while (iterator.hasNext()) { 176: final TextLine line = (TextLine) iterator.next(); 177: final Size2D dimension = line.calculateDimensions(g2); 178: width = Math.max(width, dimension.getWidth()); 179: height = height + dimension.getHeight(); 180: } 181: if (logger.isDebugEnabled()) { 182: logger.debug("width = " + width + ", height = " + height); 183: } 184: return new Size2D(width, height); 185: } 186: 187: /** 188: * Returns the bounds of the text block. 189: * 190: * @param g2 the graphics device (<code>null</code> not permitted). 191: * @param anchorX the x-coordinate for the anchor point. 192: * @param anchorY the y-coordinate for the anchor point. 193: * @param anchor the text block anchor (<code>null</code> not permitted). 194: * @param rotateX the x-coordinate for the rotation point. 195: * @param rotateY the y-coordinate for the rotation point. 196: * @param angle the rotation angle. 197: * 198: * @return The bounds. 199: */ 200: public Shape calculateBounds(final Graphics2D g2, 201: final float anchorX, final float anchorY, 202: final TextBlockAnchor anchor, 203: final float rotateX, final float rotateY, 204: final double angle) { 205: 206: final Size2D d = calculateDimensions(g2); 207: final float[] offsets = calculateOffsets( 208: anchor, d.getWidth(), d.getHeight() 209: ); 210: final Rectangle2D bounds = new Rectangle2D.Double( 211: anchorX + offsets[0], anchorY + offsets[1], 212: d.getWidth(), d.getHeight() 213: ); 214: final Shape rotatedBounds = ShapeUtilities.rotateShape( 215: bounds, angle, rotateX, rotateY 216: ); 217: return rotatedBounds; 218: 219: } 220: 221: /** 222: * Draws the text block at a specific location. 223: * 224: * @param g2 the graphics device. 225: * @param x the x-coordinate for the anchor point. 226: * @param y the y-coordinate for the anchor point. 227: * @param anchor the anchor point. 228: */ 229: public void draw(final Graphics2D g2, final float x, final float y, 230: final TextBlockAnchor anchor) { 231: draw(g2, x, y, anchor, 0.0f, 0.0f, 0.0); 232: } 233: 234: /** 235: * Draws the text block, aligning it with the specified anchor point and 236: * rotating it about the specified rotation point. 237: * 238: * @param g2 the graphics device. 239: * @param anchorX the x-coordinate for the anchor point. 240: * @param anchorY the y-coordinate for the anchor point. 241: * @param anchor the point on the text block that is aligned to the 242: * anchor point. 243: * @param rotateX the x-coordinate for the rotation point. 244: * @param rotateY the x-coordinate for the rotation point. 245: * @param angle the rotation (in radians). 246: */ 247: public void draw(final Graphics2D g2, 248: final float anchorX, final float anchorY, 249: final TextBlockAnchor anchor, 250: final float rotateX, final float rotateY, 251: final double angle) { 252: 253: final Size2D d = calculateDimensions(g2); 254: final float[] offsets = calculateOffsets(anchor, d.getWidth(), 255: d.getHeight()); 256: final Iterator iterator = this.lines.iterator(); 257: float yCursor = 0.0f; 258: while (iterator.hasNext()) { 259: TextLine line = (TextLine) iterator.next(); 260: Size2D dimension = line.calculateDimensions(g2); 261: float lineOffset = 0.0f; 262: if (this.lineAlignment == HorizontalAlignment.CENTER) { 263: lineOffset = (float) (d.getWidth() - dimension.getWidth()) 264: / 2.0f; 265: } 266: else if (this.lineAlignment == HorizontalAlignment.RIGHT) { 267: lineOffset = (float) (d.getWidth() - dimension.getWidth()); 268: } 269: line.draw( 270: g2, anchorX + offsets[0] + lineOffset, anchorY + offsets[1] + yCursor, 271: TextAnchor.TOP_LEFT, rotateX, rotateY, angle 272: ); 273: yCursor = yCursor + (float) dimension.getHeight(); 274: } 275: 276: } 277: 278: /** 279: * Calculates the x and y offsets required to align the text block with the 280: * specified anchor point. This assumes that the top left of the text 281: * block is at (0.0, 0.0). 282: * 283: * @param anchor the anchor position. 284: * @param width the width of the text block. 285: * @param height the height of the text block. 286: * 287: * @return The offsets (float[0] = x offset, float[1] = y offset). 288: */ 289: private float[] calculateOffsets(final TextBlockAnchor anchor, 290: final double width, final double height) { 291: final float[] result = new float[2]; 292: float xAdj = 0.0f; 293: float yAdj = 0.0f; 294: 295: if (anchor == TextBlockAnchor.TOP_CENTER 296: || anchor == TextBlockAnchor.CENTER 297: || anchor == TextBlockAnchor.BOTTOM_CENTER) { 298: 299: xAdj = (float) -width / 2.0f; 300: 301: } 302: else if (anchor == TextBlockAnchor.TOP_RIGHT 303: || anchor == TextBlockAnchor.CENTER_RIGHT 304: || anchor == TextBlockAnchor.BOTTOM_RIGHT) { 305: 306: xAdj = (float) -width; 307: 308: } 309: 310: if (anchor == TextBlockAnchor.TOP_LEFT 311: || anchor == TextBlockAnchor.TOP_CENTER 312: || anchor == TextBlockAnchor.TOP_RIGHT) { 313: 314: yAdj = 0.0f; 315: 316: } 317: else if (anchor == TextBlockAnchor.CENTER_LEFT 318: || anchor == TextBlockAnchor.CENTER 319: || anchor == TextBlockAnchor.CENTER_RIGHT) { 320: 321: yAdj = (float) -height / 2.0f; 322: 323: } 324: else if (anchor == TextBlockAnchor.BOTTOM_LEFT 325: || anchor == TextBlockAnchor.BOTTOM_CENTER 326: || anchor == TextBlockAnchor.BOTTOM_RIGHT) { 327: 328: yAdj = (float) -height; 329: 330: } 331: result[0] = xAdj; 332: result[1] = yAdj; 333: return result; 334: } 335: 336: /** 337: * Tests this object for equality with an arbitrary object. 338: * 339: * @param obj the object to test against (<code>null</code> permitted). 340: * 341: * @return A boolean. 342: */ 343: public boolean equals(final Object obj) { 344: if (obj == this) { 345: return true; 346: } 347: if (obj instanceof TextBlock) { 348: final TextBlock block = (TextBlock) obj; 349: return this.lines.equals(block.lines); 350: } 351: return false; 352: } 353: 354: /** 355: * Returns a hash code for this object. 356: * 357: * @return A hash code. 358: */ 359: public int hashCode() { 360: return (this.lines != null ? this.lines.hashCode() : 0); 361: } 362: }