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