package com.limegroup.gnutella.gui;
import java.awt.FontMetrics;
import java.util.StringTokenizer;
import javax.swing.JTextArea;
import javax.swing.LookAndFeel;
/**
* This class uses a <tt>JTextArea</tt> to simulate a <tt>JLabel</tt> that
* allows multiple-line labels. It does this by using JLabel's values for
* border, font, etc.
*/
//2345678|012345678|012345678|012345678|012345678|012345678|012345678|012345678|
public class MultiLineLabel extends JTextArea {
/**
* The default pixel width for labels when the width is not
* specified in the constructor.
*/
private final static int DEFAULT_LABEL_WIDTH = 200;
/**
* The actual text, prior to \n being inserted.
*/
private String _theText;
/**
* Creates a label that can have multiple lines and that has the
* default width.
*
* @param s the <tt>String</tt> to display in the label
* @throws <tt>NullPointerException</tt> if the string argument is
* <tt>null</tt>
*/
public MultiLineLabel(String s) {
if(s == null) {
throw new NullPointerException("null string in multilinelabel");
}
this.setOpaque(false);
setText(s);
}
/**
* Creates a label with new lines inserted after the specified number
* of pixels have been filled on each line.
*
* @param s the <tt>String</tt> to display in the label
* @param pixels the pixel limit for each line
* @throws <tt>NullPointerException</tt> if the string argument is
* <tt>null</tt>
*/
public MultiLineLabel(String s, int pixels) {
if(s == null) {
throw new NullPointerException("null string in multilinelabel");
}
this.setOpaque(false);
setText(s, pixels);
}
/**
* New constructor that takes an array of strings. This creates a
* new <tt>MultiLineLabel</tt> with the string at each index in
* the array placed on its own line. The array cannot contain
* any null strings.
*
* @param strs the array of strings that should each be placed on
* its own line in the label
*/
public MultiLineLabel(String[] strs) {
this.setOpaque(false);
_theText = createSizedString(strs);
super.setText(_theText);
}
/**
* Creates a label that can have multiple lines and that sets the
* number of rows and columns for the JTextArea.
*
* @param s the <tt>String</tt> to display in the label
* @param pixels the pixel limit for each line.
* @param rows the number of rows to include in the label
* @param cols the number of columns to include in the label
* @throws <tt>NullPointerException</tt> if the string argument is
* <tt>null</tt>
*/
public MultiLineLabel(String s, int pixels, int rows, int cols) {
super(rows, cols);
if(s == null) {
throw new NullPointerException("null string in multilinelabel");
}
this.setOpaque(false);
setText(s, pixels);
}
/**
* Change the text before passing it up to the super setText.
*
* @param s the <tt>String</tt> to display in the label
* @param pixels the pixel limit for each line
*/
public void setText(String s, int pixels) {
_theText = s;
super.setText( createSizedString(s, pixels) );
}
/**
* Change the text before passing it up to the super setText.
*
* @param s the <tt>String</tt> to display in the label
*/
public void setText(String s) {
_theText = s;
super.setText( createSizedString(s, DEFAULT_LABEL_WIDTH) );
}
/**
* Gets the current text.
*/
public String getText() {
return _theText;
}
/**
* Tells the look and feel to reset some of the values for this
* component so that it doesn't use JTextArea's default values.
*
* DO NOT CALL THIS METHOD YOURSELF!
*/
public void updateUI() {
super.updateUI();
//setLineWrap(true);
setWrapStyleWord(true);
setHighlighter(null);
setEditable(false);
LookAndFeel.installBorder(this, "Label.border");
LookAndFeel.installColorsAndFont(this,
"Label.background",
"Label.foreground",
"Label.font");
}
/**
* Convert the input string to a string with newlines at the
* closest word to the number of pixels specified in the 'pixels'
* parameter.
*
* @param message the <tt>String</tt> to display in the label
* @param pixels the pixel width on each line before
* inserting a new line character
*/
private String createSizedString(final String message, final int pixels) {
FontMetrics fm = getFontMetrics(getFont());
String word;
// Find if a single line is longer than the pixel limit. If so, use
// that limit instead of pixels
StringTokenizer st = new StringTokenizer(message);
int newWidth = pixels;
while(st.hasMoreTokens()) {
word = st.nextToken();
newWidth = Math.max(newWidth, fm.stringWidth(word));
}
// layout multiple lines
StringBuffer sb = new StringBuffer();
StringBuffer cursb = new StringBuffer();
boolean isNewLine;
st = new StringTokenizer(message, " \n", true);
while(st.hasMoreTokens()) {
word = st.nextToken();
if(word.equals(" "))
continue;
isNewLine = word.equals("\n");
if (isNewLine || fm.stringWidth(cursb.toString() + word) > newWidth) {
sb.append(cursb.toString());
sb.append("\n");
cursb = new StringBuffer();
}
if(!isNewLine) {
cursb.append(word);
cursb.append(" ");
}
}
sb.append(cursb.toString());
return sb.toString();
}
/**
* Creates a multiline label with one line taken up by each string
* in the string array argument.
*
* @param strs the array of strings to put in the multiline label
* @return a new string with newlines inserted at the appropriate
* places
*/
private String createSizedString(final String[] strs) {
StringBuffer sb = new StringBuffer();
for(int i=0; i<strs.length; i++) {
sb.append(strs[i]);
sb.append("\n");
}
return sb.toString();
}
}