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: * TextLine.java
029: * -------------
030: * (C) Copyright 2003-2005, by Object Refinery Limited and Contributors.
031: *
032: * Original Author: David Gilbert (for Object Refinery Limited);
033: * Contributor(s): -;
034: *
035: * $Id: TextLine.java,v 1.11 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: * 29-Jan-2004 : Added new constructor (DG);
042: * 22-Mar-2004 : Added equals() method and implemented Serializable (DG);
043: * 01-Apr-2004 : Changed java.awt.geom.Dimension2D to org.jfree.ui.Size2D
044: * because of JDK bug 4976448 which persists on JDK 1.3.1 (DG);
045: * 03-Sep-2004 : Added a method to remove a fragment (DG);
046: * 08-Jul-2005 : Fixed bug in calculateBaselineOffset() (DG);
047: *
048: */
049:
050: package org.jfree.text;
051:
052: import java.awt.Font;
053: import java.awt.Graphics2D;
054: import java.awt.Paint;
055: import java.io.Serializable;
056: import java.util.Iterator;
057: import java.util.List;
058:
059: import org.jfree.ui.Size2D;
060: import org.jfree.ui.TextAnchor;
061: import org.jfree.util.Log;
062: import org.jfree.util.LogContext;
063:
064: /**
065: * A sequence of {@link TextFragment} objects that together form a line of
066: * text. A sequence of text lines is managed by the {@link TextBlock} class.
067: *
068: * @author David Gilbert
069: */
070: public class TextLine implements Serializable {
071:
072: /** For serialization. */
073: private static final long serialVersionUID = 7100085690160465444L;
074:
075: /** Storage for the text fragments that make up the line. */
076: private List fragments;
077:
078: /** Access to logging facilities. */
079: protected static final LogContext logger = Log
080: .createContext(TextLine.class);
081:
082: /**
083: * Creates a new empty line.
084: */
085: public TextLine() {
086: this .fragments = new java.util.ArrayList();
087: }
088:
089: /**
090: * Creates a new text line using the default font.
091: *
092: * @param text the text (<code>null</code> not permitted).
093: */
094: public TextLine(final String text) {
095: this (text, TextFragment.DEFAULT_FONT);
096: }
097:
098: /**
099: * Creates a new text line.
100: *
101: * @param text the text (<code>null</code> not permitted).
102: * @param font the text font (<code>null</code> not permitted).
103: */
104: public TextLine(final String text, final Font font) {
105: this .fragments = new java.util.ArrayList();
106: final TextFragment fragment = new TextFragment(text, font);
107: this .fragments.add(fragment);
108: }
109:
110: /**
111: * Creates a new text line.
112: *
113: * @param text the text (<code>null</code> not permitted).
114: * @param font the text font (<code>null</code> not permitted).
115: * @param paint the text color (<code>null</code> not permitted).
116: */
117: public TextLine(final String text, final Font font,
118: final Paint paint) {
119: if (text == null) {
120: throw new IllegalArgumentException("Null 'text' argument.");
121: }
122: if (font == null) {
123: throw new IllegalArgumentException("Null 'font' argument.");
124: }
125: if (paint == null) {
126: throw new IllegalArgumentException("Null 'paint' argument.");
127: }
128: this .fragments = new java.util.ArrayList();
129: final TextFragment fragment = new TextFragment(text, font,
130: paint);
131: this .fragments.add(fragment);
132: }
133:
134: /**
135: * Adds a text fragment to the text line.
136: *
137: * @param fragment the text fragment (<code>null</code> not permitted).
138: */
139: public void addFragment(final TextFragment fragment) {
140: this .fragments.add(fragment);
141: }
142:
143: /**
144: * Removes a fragment from the line.
145: *
146: * @param fragment the fragment to remove.
147: */
148: public void removeFragment(final TextFragment fragment) {
149: this .fragments.remove(fragment);
150: }
151:
152: /**
153: * Draws the text line.
154: *
155: * @param g2 the graphics device.
156: * @param anchorX the x-coordinate for the anchor point.
157: * @param anchorY the y-coordinate for the anchor point.
158: * @param anchor the point on the text line that is aligned to the anchor
159: * point.
160: * @param rotateX the x-coordinate for the rotation point.
161: * @param rotateY the y-coordinate for the rotation point.
162: * @param angle the rotation angle (in radians).
163: */
164: public void draw(final Graphics2D g2, final float anchorX,
165: final float anchorY, final TextAnchor anchor,
166: final float rotateX, final float rotateY, final double angle) {
167:
168: float x = anchorX;
169: final float yOffset = calculateBaselineOffset(g2, anchor);
170: final Iterator iterator = this .fragments.iterator();
171: while (iterator.hasNext()) {
172: final TextFragment fragment = (TextFragment) iterator
173: .next();
174: final Size2D d = fragment.calculateDimensions(g2);
175: fragment.draw(g2, x, anchorY + yOffset,
176: TextAnchor.BASELINE_LEFT, rotateX, rotateY, angle);
177: x = x + (float) d.getWidth();
178: }
179:
180: }
181:
182: /**
183: * Calculates the width and height of the text line.
184: *
185: * @param g2 the graphics device.
186: *
187: * @return The width and height.
188: */
189: public Size2D calculateDimensions(final Graphics2D g2) {
190: double width = 0.0;
191: double height = 0.0;
192: final Iterator iterator = this .fragments.iterator();
193: while (iterator.hasNext()) {
194: final TextFragment fragment = (TextFragment) iterator
195: .next();
196: final Size2D dimension = fragment.calculateDimensions(g2);
197: width = width + dimension.getWidth();
198: height = Math.max(height, dimension.getHeight());
199: if (logger.isDebugEnabled()) {
200: logger.debug("width = " + width + ", height = "
201: + height);
202: }
203: }
204: return new Size2D(width, height);
205: }
206:
207: /**
208: * Returns the first text fragment in the line.
209: *
210: * @return The first text fragment in the line.
211: */
212: public TextFragment getFirstTextFragment() {
213: TextFragment result = null;
214: if (this .fragments.size() > 0) {
215: result = (TextFragment) this .fragments.get(0);
216: }
217: return result;
218: }
219:
220: /**
221: * Returns the last text fragment in the line.
222: *
223: * @return The last text fragment in the line.
224: */
225: public TextFragment getLastTextFragment() {
226: TextFragment result = null;
227: if (this .fragments.size() > 0) {
228: result = (TextFragment) this .fragments.get(this .fragments
229: .size() - 1);
230: }
231: return result;
232: }
233:
234: /**
235: * Calculate the offsets required to translate from the specified anchor
236: * position to the left baseline position.
237: *
238: * @param g2 the graphics device.
239: * @param anchor the anchor position.
240: *
241: * @return The offsets.
242: */
243: private float calculateBaselineOffset(final Graphics2D g2,
244: final TextAnchor anchor) {
245: float result = 0.0f;
246: Iterator iterator = this .fragments.iterator();
247: while (iterator.hasNext()) {
248: TextFragment fragment = (TextFragment) iterator.next();
249: result = Math.max(result, fragment.calculateBaselineOffset(
250: g2, anchor));
251: }
252: return result;
253: }
254:
255: /**
256: * Tests this object for equality with an arbitrary object.
257: *
258: * @param obj the object to test against (<code>null</code> permitted).
259: *
260: * @return A boolean.
261: */
262: public boolean equals(final Object obj) {
263: if (obj == null) {
264: return false;
265: }
266: if (obj == this ) {
267: return true;
268: }
269: if (obj instanceof TextLine) {
270: final TextLine line = (TextLine) obj;
271: return this .fragments.equals(line.fragments);
272: }
273: return false;
274: }
275:
276: /**
277: * Returns a hash code for this object.
278: *
279: * @return A hash code.
280: */
281: public int hashCode() {
282: return (this .fragments != null ? this .fragments.hashCode() : 0);
283: }
284:
285: }
|