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
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.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 */
075 public 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 }