001: /*
002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
003: * Distributed under the terms of either:
004: * - the common development and distribution license (CDDL), v1.0; or
005: * - the GNU Lesser General Public License, v2.1 or later
006: * $Id: JMultiLabel.java 3634 2007-01-08 21:42:24Z gbevin $
007: */
008: package com.uwyn.rife.swing;
009:
010: import java.awt.*;
011:
012: import com.uwyn.rife.tools.StringUtils;
013: import java.awt.event.ComponentEvent;
014: import java.awt.event.ComponentListener;
015: import java.awt.font.FontRenderContext;
016: import java.awt.font.LineBreakMeasurer;
017: import java.awt.font.TextAttribute;
018: import java.awt.font.TextLayout;
019: import java.text.AttributedCharacterIterator;
020: import java.text.AttributedString;
021: import java.util.ArrayList;
022: import javax.swing.JPanel;
023: import javax.swing.UIManager;
024: import javax.swing.border.Border;
025:
026: public class JMultiLabel extends JPanel implements ComponentListener {
027: private static final long serialVersionUID = 4732617870135188398L;
028: private String mMessageText = null;
029: private Font mMessageFont = null;
030: private Color mMessageColor = null;
031: private int mWrapWidth = 0;
032: private boolean mAutoWrap = false;
033:
034: private ArrayList<TextLayout> mTextLayouts = null;
035: private double mTextWidth = 0;
036: private double mTextHeight = 0;
037:
038: private int mCachedWidth = -1;
039:
040: public JMultiLabel() {
041: this ("");
042: }
043:
044: public JMultiLabel(String messageText) {
045: this (messageText, 0);
046: }
047:
048: public JMultiLabel(String messageText, int wrapWidth) {
049: mMessageText = messageText;
050: setOpaque(false);
051: addComponentListener(this );
052: setFont(UIManager.getFont("Label.font"));
053: setWrapWidth(wrapWidth);
054: }
055:
056: public void setMessageText(String messageText) {
057: mMessageText = messageText;
058: mCachedWidth = -1;
059: }
060:
061: public void setWrapWidth(int wrapWidth) {
062: mWrapWidth = wrapWidth;
063: mCachedWidth = -1;
064: }
065:
066: public void setAutoWrap(boolean autoWrap) {
067: mAutoWrap = autoWrap;
068: }
069:
070: public void setFont(Font font) {
071: mCachedWidth = -1;
072: mMessageFont = font;
073: }
074:
075: public Font getFont() {
076: return mMessageFont;
077: }
078:
079: public void setColor(Color color) {
080: mMessageColor = color;
081: }
082:
083: public Dimension getPreferredSize() {
084: Graphics2D g2 = (Graphics2D) getGraphics();
085: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
086: RenderingHints.VALUE_ANTIALIAS_ON);
087: g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
088: RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
089: ensureValidLayouts(g2);
090:
091: Dimension dimension = null;
092: Border border = getBorder();
093: if (null == border) {
094: dimension = new Dimension((int) Math.ceil(mTextWidth),
095: (int) Math.ceil(mTextHeight));
096: } else {
097: Insets border_insets = border.getBorderInsets(this );
098: dimension = new Dimension(((int) Math.ceil(mTextWidth))
099: + border_insets.left + border_insets.right,
100: ((int) Math.ceil(mTextHeight)) + border_insets.top
101: + border_insets.bottom);
102: }
103:
104: return dimension;
105: }
106:
107: private int getWrappingWidth() {
108: if (mWrapWidth <= 0) {
109: if (!mAutoWrap) {
110: return Integer.MAX_VALUE;
111: }
112:
113: Border border = getBorder();
114: int border_horiz = 0;
115: if (border != null) {
116: Insets border_insets = border.getBorderInsets(this );
117: border_horiz = border_insets.left + border_insets.right;
118: }
119:
120: int width = super .getWidth() - border_horiz;
121: if (width <= 0) {
122: width = Integer.MAX_VALUE;
123: }
124:
125: return width;
126: }
127:
128: return mWrapWidth;
129: }
130:
131: public Dimension getMinimumSize() {
132: Dimension minimum_size = super .getMinimumSize();
133: Dimension preferred_size = getPreferredSize();
134: return new Dimension((int) Math.ceil(minimum_size.getWidth()),
135: (int) Math.ceil(preferred_size.getHeight()));
136: }
137:
138: public Dimension getMaximumSize() {
139: return super .getMaximumSize();
140: }
141:
142: public void paint(Graphics g) {
143: super .paint(g);
144: paintMessage((Graphics2D) g);
145: }
146:
147: private void addTextLayout(TextLayout layout) {
148: mTextLayouts.add(layout);
149:
150: // update the global text dimensions
151: if (mTextHeight != 0) {
152: mTextHeight += layout.getLeading();
153: }
154: double layout_width = layout.getBounds().getWidth() + 1;
155: if (layout_width > mTextWidth) {
156: mTextWidth = layout_width;
157: }
158: mTextHeight += layout.getAscent() + layout.getDescent();
159: }
160:
161: private void ensureValidLayouts(Graphics2D g2) {
162: int wrapping_width = getWrappingWidth();
163:
164: if (mCachedWidth == wrapping_width) {
165: return;
166: }
167:
168: mTextLayouts = new ArrayList<TextLayout>();
169:
170: FontRenderContext render_context = g2.getFontRenderContext();
171: if (null == mMessageText || 0 == mMessageText.length()) {
172: TextLayout layout = new TextLayout(" ", mMessageFont,
173: render_context);
174: mTextLayouts.add(layout);
175:
176: // update the global text dimensions
177: mTextWidth = 0;
178: mTextHeight = 0;
179: } else {
180: // Get the newlines and create a new text that has them stripped away
181: ArrayList<String> lines = StringUtils.split(mMessageText,
182: "\n");
183: int newline_count = lines.size();
184: int character_count = 0;
185: for (String line : lines) {
186: character_count += line.length();
187: }
188:
189: TextLayout layout = null;
190: // It might be possible that the string only contains linebreaks
191: // the stripped string will then be empty and no linebreakmeasurer
192: // can be used
193: if (0 == character_count) {
194: for (int i = 0; i < newline_count; i++) {
195: layout = new TextLayout(" ", getFont(),
196: render_context);
197: addTextLayout(layout);
198: }
199: } else {
200: mTextWidth = 0;
201: mTextHeight = 0;
202:
203: for (String line : lines) {
204: // handle empty newlines
205: if (0 == line.length()) {
206: layout = new TextLayout(" ", getFont(),
207: render_context);
208: addTextLayout(layout);
209: } else {
210: // Create an Attributed String
211: AttributedString attributed_string = new AttributedString(
212: line);
213: attributed_string.addAttribute(
214: TextAttribute.FONT, getFont());
215:
216: // Get the iterator.
217: AttributedCharacterIterator iterator = attributed_string
218: .getIterator();
219:
220: // Create the layouts
221: LineBreakMeasurer measurer = new LineBreakMeasurer(
222: iterator, render_context);
223: while (measurer.getPosition() < iterator
224: .getEndIndex()) {
225: try {
226: layout = measurer
227: .nextLayout(wrapping_width);
228: }
229: // fix around jdk bug
230: catch (ArrayIndexOutOfBoundsException e) {
231: break;
232: }
233:
234: addTextLayout(layout);
235: }
236: }
237: }
238: }
239: }
240:
241: // Cache the width so that all these calculations are not needlessly
242: // done
243: if (Integer.MAX_VALUE != wrapping_width) {
244: mCachedWidth = -1;
245: } else {
246: mCachedWidth = wrapping_width;
247: }
248: }
249:
250: public boolean paintMessage(Graphics2D g2) {
251: Object previous_aliasing = g2
252: .getRenderingHint(RenderingHints.KEY_ANTIALIASING);
253: Object previous_textaliasing = g2
254: .getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING);
255: Color previous_color = g2.getColor();
256:
257: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
258: RenderingHints.VALUE_ANTIALIAS_ON);
259: g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
260: RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
261: g2.setColor(mMessageColor);
262:
263: ensureValidLayouts(g2);
264:
265: Border border = getBorder();
266: int border_x = 0;
267: int border_y = 0;
268: if (border != null) {
269: Insets border_insets = border.getBorderInsets(this );
270: border_x = border_insets.left;
271: border_y = border_insets.top;
272: }
273: boolean all_lines_shown = true;
274: if (mTextLayouts != null && mTextLayouts.size() > 0) {
275: float x = border_x;
276: float y1 = border_y;
277: float y2 = -1;
278: for (TextLayout layout : mTextLayouts) {
279: y1 += layout.getAscent();
280: y2 = y1 + layout.getDescent();
281:
282: // don't draw lines that can't be rendered completely
283: int height = getHeight();
284: if (y2 > height) {
285: all_lines_shown = false;
286: break;
287: }
288:
289: layout.draw(g2, x, y1);
290:
291: y1 = y2 + layout.getLeading();
292: }
293: }
294:
295: g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
296: previous_textaliasing);
297: g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
298: previous_aliasing);
299: g2.setColor(previous_color);
300:
301: return all_lines_shown;
302: }
303:
304: public void componentMoved(ComponentEvent e) {
305: }
306:
307: public void componentHidden(ComponentEvent e) {
308: }
309:
310: public void componentShown(ComponentEvent e) {
311: }
312:
313: public void componentResized(ComponentEvent e) {
314: if (mAutoWrap) {
315: mWrapWidth = 0;
316: mCachedWidth = -1;
317: }
318: }
319: }
|