TextBlock.java 12 KB

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