001: //The contents of this file are subject to the Mozilla Public License Version 1.1
002: //(the "License"); you may not use this file except in compliance with the
003: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
004: //
005: //Software distributed under the License is distributed on an "AS IS" basis,
006: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
007: //for the specific language governing rights and
008: //limitations under the License.
009: //
010: //The Original Code is "The Columba Project"
011: //
012: //The Initial Developers of the Original Code are Frederik Dietz and Timo Stich.
013: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
014: //
015: //All Rights Reserved.
016:
017: package org.columba.core.gui.base;
018:
019: import java.awt.Container;
020: import java.awt.Dimension;
021: import java.awt.Graphics;
022: import java.awt.Graphics2D;
023: import java.awt.Insets;
024: import java.awt.Point;
025: import java.awt.Toolkit;
026: import java.awt.font.LineBreakMeasurer;
027: import java.awt.font.TextAttribute;
028: import java.awt.font.TextLayout;
029: import java.text.AttributedString;
030: import java.util.Iterator;
031: import java.util.LinkedList;
032: import java.util.List;
033:
034: import javax.swing.JComponent;
035: import javax.swing.UIManager;
036:
037: /**
038: * A Swing component capable of displaying text in multiple lines.
039: */
040: public class MultiLineLabel extends JComponent {
041: private String text;
042: private int[] lineBreaks;
043: protected LineBreakMeasurer measurer;
044: protected int lineSpacing = 4;
045:
046: /**
047: * Creates a new label with the given text.
048: */
049: public MultiLineLabel(String text) {
050: setForeground(UIManager.getColor("Label.foreground"));
051: setFont(UIManager.getFont("Label.font"));
052: setAlignmentX(LEFT_ALIGNMENT);
053: setPreferredSize(new Dimension(Toolkit.getDefaultToolkit()
054: .getScreenSize().width / 3, 50));
055: setText(text);
056: }
057:
058: /**
059: * Returns the label's text.
060: */
061: public String getText() {
062: return text;
063: }
064:
065: /**
066: * Sets the label's text.
067: */
068: public void setText(String text) {
069: String oldValue = this .text;
070: this .text = text;
071: measurer = null;
072: firePropertyChange("text", oldValue, text);
073: revalidate();
074: repaint();
075: }
076:
077: /**
078: * Returns the amount of space between the lines.
079: */
080: public int getLineSpacing() {
081: return lineSpacing;
082: }
083:
084: /**
085: * Sets the amount of space between the lines.
086: */
087: public void setLineSpacing(int lineSpacing) {
088: Integer oldValue = new Integer(this .lineSpacing);
089: this .lineSpacing = lineSpacing;
090: firePropertyChange("lineSpacing", oldValue, new Integer(
091: lineSpacing));
092: revalidate();
093: repaint();
094: }
095:
096: /**
097: * Overridden to return appropriate values. This method takes the parent
098: * component's size into account.
099: */
100: public Dimension getMinimumSize() {
101: int height = 5;
102: int width = 0;
103: Container parent = getParent();
104: if (parent != null) {
105: width = parent.getWidth();
106: }
107: if (width == 0) {
108: width = Toolkit.getDefaultToolkit().getScreenSize().width / 3;
109: }
110: LineBreakMeasurer measurer = getLineBreakMeasurer();
111: TextLayout layout;
112: int i = 0;
113: while (measurer != null
114: && measurer.getPosition() < text.length()) {
115: layout = measurer.nextLayout(width - 20, lineBreaks[i],
116: false);
117:
118: //if we stopped at line break, increase array index pointer
119: if (measurer.getPosition() == lineBreaks[i]) {
120: i++;
121: }
122:
123: //increase minimum height by line height and line spacing
124: height += layout.getAscent() + layout.getDescent()
125: + layout.getLeading() + lineSpacing;
126: }
127:
128: //add the component's border insets to our minimum dimension
129: Insets insets = getInsets();
130: return new Dimension(width + insets.left + insets.right, height
131: + insets.top + insets.bottom);
132: }
133:
134: protected LineBreakMeasurer getLineBreakMeasurer() {
135: if (measurer == null) {
136: if (text != null && text.length() > 0) {
137: AttributedString string = new AttributedString(text);
138: string.addAttribute(TextAttribute.FONT, getFont());
139: measurer = new LineBreakMeasurer(string.getIterator(),
140: ((Graphics2D) getGraphics())
141: .getFontRenderContext());
142:
143: //check for line breaks
144: List temp = new LinkedList();
145: int i;
146: char c;
147: for (i = 0; i < text.length(); i++) {
148: c = text.charAt(i);
149: if (c == '\r' || c == '\n') {
150: temp.add(new Integer(i + 1));
151: }
152: }
153: //put them into the array
154: i = 0;
155: lineBreaks = new int[temp.size() + 1];
156: Iterator iterator = temp.iterator();
157: while (iterator.hasNext()) {
158: lineBreaks[i++] = ((Integer) iterator.next())
159: .intValue();
160: }
161: lineBreaks[i] = text.length();
162: }
163: } else {
164: measurer.setPosition(0);
165: }
166: return measurer;
167: }
168:
169: protected void paintComponent(Graphics graphics) {
170: super .paintComponent(graphics);
171: graphics.setColor(getForeground());
172: Graphics2D g = (Graphics2D) graphics;
173: LineBreakMeasurer measurer = getLineBreakMeasurer();
174: float wrappingWidth = getWidth() - 15;
175: if (wrappingWidth <= 0 || measurer == null) {
176: return;
177: }
178: Insets insets = getInsets();
179: Point pen = new Point(5 + insets.left, 5 + insets.top);
180: TextLayout layout;
181: int i = 0;
182: while (measurer.getPosition() < text.length()) {
183: layout = measurer.nextLayout(wrappingWidth, lineBreaks[i],
184: false);
185:
186: //if we stopped at line break, increase array index pointer
187: if (measurer.getPosition() == lineBreaks[i]) {
188: i++;
189: }
190:
191: //draw line
192: pen.y += layout.getAscent();
193: float dx = layout.isLeftToRight() ? 0
194: : (wrappingWidth - layout.getAdvance());
195: layout.draw(g, pen.x + dx, pen.y);
196: pen.y += layout.getDescent() + layout.getLeading()
197: + lineSpacing;
198: }
199: }
200:
201: /**
202: * @see java.awt.Component#getPreferredSize()
203: */
204: public Dimension getPreferredSize() {
205:
206: return getMinimumSize();
207: }
208: }
|