/*
* IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
*
* http://izpack.org/
* http://izpack.codehaus.org/
*
* Copyright 1997,2002 Elmar Grom
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import javax.swing.*;
import java.awt.*;
import java.util.Vector;
/*---------------------------------------------------------------------------*/
/**
* <BR>
* <code>MultiLineLabel</code> may be used in place of javax.swing.JLabel. <BR>
* <BR>
* This class implements a component that is capable of displaying multiple lines of text. Line
* breaks are inserted automatically whenever a line of text extends beyond the predefined maximum
* line length. Line breaks will only be inserted between words, except where a single word is
* longer than the maximum line length. Line breaks may be forced at any location in the text by
* inserting a newline (\n). White space that is not valuable (i.e. is placed at the beginning of a
* new line or at the very beginning or end of the text) is removed. <br>
* <br>
* <b>Note:</b> you can set the maximum width of the label either through one of the constructors
* or you can call <code>setMaxWidth()</code> explicitly. If this is not set,
* <code>MultiLineLabel</code> will derive its width from the parent component.
*
* @author Elmar Grom
* @version 1.0 / 04-13-02
*/
/*---------------------------------------------------------------------------*
* Reviving some old code here that was written before there was swing.
* The original was written to work with awt. I had to do some masaging to
* make it a JComponent and I hope it behaves like a reasonably good mannered
* swing component.
*---------------------------------------------------------------------------*/
public class MultiLineLabel extends JComponent
{
/**
*
*/
private static final long serialVersionUID = 4051045255031894837L;
public static final int LEFT = 0; // alignment constants
public static final int CENTER = 1;
public static final int RIGHT = 2;
public static final int DEFAULT_MARGIN = 10;
public static final int DEFAULT_ALIGN = LEFT;
public static final int LEAST_ALLOWED = 200; // default setting for
// maxAllowed
private static final int FOUND = 0; // constants for string search.
private static final int NOT_FOUND = 1;
private static final int NOT_DONE = 0;
private static final int DONE = 1;
private static final char[] WHITE_SPACE = {' ', '\n', '\t'};
private static final char[] SPACES = {' ', '\t'};
private static final char NEW_LINE = '\n';
protected Vector<String> line = new Vector<String>();// text lines to display
protected String labelText; // text lines to display
protected int numLines; // the number of lines
protected int marginHeight; // top and bottom margins
protected int marginWidth; // left and right margins
protected int lineHeight; // total height of the font
protected int lineAscent; // font height above the baseline
protected int lineDescent; // font hight below the baseline
protected int[] lineWidth; // width of each line
protected int maxWidth; // width of the widest line
private int maxAllowed = LEAST_ALLOWED; // max width allowed to use
private boolean maxAllowedSet = false; // signals if the max allowed width
// has been explicitly set
protected int alignment = LEFT; // default text alignment
/*-------------------------------------------------------------------*/
/**
* Constructor
*
* @param text the text to be displayed
* @param horMargin the horizontal margin for the label
* @param vertMargin the vertical margin for the label
* @param maxWidth the maximum allowed width of the text
* @param justify the text alignment for the label
*/
/*-------------------------------------------------------------------*
* <detailed description / implementation details if applicable>
*-------------------------------------------------------------------*/
public MultiLineLabel(String text, int horMargin, int vertMargin, int maxWidth, int justify)
{
this.labelText = text;
this.marginWidth = horMargin;
this.marginHeight = vertMargin;
this.maxAllowed = maxWidth;
this.maxAllowedSet = true;
this.alignment = justify;
}
/*-------------------------------------------------------------------*/
/**
* Constructor using default max-width and alignment.
*
* @param label the text to be displayed
* @param marginWidth the horizontal margin for the label
* @param marginHeight the vertical margin for the label
*/
/*-------------------------------------------------------------------*
* <detailed description / implementation details if applicable>
*-------------------------------------------------------------------*/
public MultiLineLabel(String label, int marginWidth, int marginHeight)
{
this.labelText = label;
this.marginWidth = marginWidth;
this.marginHeight = marginHeight;
}
/*-------------------------------------------------------------------*/
/**
* Constructor using default max-width, and margin.
*
* @param label the text to be displayed
* @param alignment the text alignment for the label
*/
/*-------------------------------------------------------------------*
* <detailed description / implementation details if applicable>
*-------------------------------------------------------------------*/
public MultiLineLabel(String label, int alignment)
{
this.labelText = label;
this.alignment = alignment;
}
/*-------------------------------------------------------------------*/
/**
* Constructor using default max-width, alignment, and margin.
*
* @param label the text to be displayed
*/
/*-------------------------------------------------------------------*
* <detailed description / implementation details if applicable>
*-------------------------------------------------------------------*/
public MultiLineLabel(String label)
{
this.labelText = label;
}
/*-------------------------------------------------------------------*/
/**
* This method searches the target string for occurences of any of the characters in the source
* string. The return value is the position of the first hit. Based on the mode parameter the
* hit position is either the position where any of the source characters first was found or the
* first position where none of the source characters where found.
*
* @param target the text to be searched
* @param start the start position for the search
* @param source the list of characters to be searched for
* @param mode the search mode FOUND = reports first found NOT_FOUND = reports first not found
* @return position of the first occurence
*/
/*-------------------------------------------------------------------*
* <detailed description / implementation details if applicable>
*-------------------------------------------------------------------*/
int getPosition(String target, int start, char[] source, int mode)
{
int status;
int position;
int scan;
int targetEnd;
int sourceLength;
char temp;
targetEnd = (target.length() - 1);
sourceLength = source.length;
position = start;
if (mode == FOUND)
{
status = NOT_DONE;
while (status != DONE)
{
position++;
if (!(position < targetEnd)) // end of string reached, the
// next
{ // statement would cause a runtime error
return (targetEnd);
}
temp = target.charAt(position);
for (scan = 0; scan < sourceLength; scan++) // walk through the
// source
{ // string and compare each char
if (source[scan] == temp)
{
status = DONE;
}
}
}
return (position);
}
else if (mode == NOT_FOUND)
{
status = NOT_DONE;
while (status != DONE)
{
position++;
if (!(position < targetEnd)) // end of string reached, the
// next
{ // statement would cause a runtime error
return (targetEnd);
}
temp = target.charAt(position);
status = DONE;
for (scan = 0; scan < sourceLength; scan++) // walk through the
// source
{ // string and compare each char
if (source[scan] == temp)
{
status = NOT_DONE;
}
}
}
return (position);
}
return (0);
}
/*-------------------------------------------------------------------*/
/**
* This method scans the input string until the max allowed width is reached. The return value
* indicates the position just before this happens.
*
* @param word word to break
* @return position character position just before the string is too long
*/
/*-------------------------------------------------------------------*
* <detailed description / implementation details if applicable>
*-------------------------------------------------------------------*/
int breakWord(String word, FontMetrics fm)
{
int width;
int currentPos;
int endPos;
width = 0;
currentPos = 0;
endPos = word.length() - 1;
// make sure we don't end up with a negative position
if (endPos <= 0)
{
return (currentPos);
}
// seek the position where the word first is longer than allowed
while ((width < maxAllowed) && (currentPos < endPos))
{
currentPos++;
width = fm.stringWidth(labelText.substring(0, currentPos));
}
// adjust to get the chatacter just before (this should make it a bit
// shorter than allowed!)
if (currentPos != endPos)
{
currentPos--;
}
return (currentPos);
}
/*-------------------------------------------------------------------*/
/**
* This method breaks the label text up into multiple lines of text. Line breaks are established
* based on the maximum available space. A new line is started whenever a line break is
* encountered, even if the permissible length is not yet reached. Words are broken only if a
* single word happens to be longer than one line.
*/
/*-------------------------------------------------------------------*/
private void divideLabel()
{
int width;
int startPos;
int currentPos;
int lastPos;
int endPos;
line.clear();
FontMetrics fm = this.getFontMetrics(this.getFont());
startPos = 0;
currentPos = startPos;
lastPos = currentPos;
endPos = (labelText.length() - 1);
while (currentPos < endPos)
{
width = 0;
// ----------------------------------------------------------------
// find the first substring that occupies more than the granted
// space.
// Break at the end of the string or a line break
// ----------------------------------------------------------------
while ((width < maxAllowed) && (currentPos < endPos)
&& (labelText.charAt(currentPos) != NEW_LINE))
{
lastPos = currentPos;
currentPos = getPosition(labelText, currentPos, WHITE_SPACE, FOUND);
width = fm.stringWidth(labelText.substring(startPos, currentPos));
}
// ----------------------------------------------------------------
// if we have a line break we want to copy everything up to
// currentPos
// ----------------------------------------------------------------
if (labelText.charAt(currentPos) == NEW_LINE)
{
lastPos = currentPos;
}
// ----------------------------------------------------------------
// if we are at the end of the string we want to copy everything up
// to
// the last character. Since there seems to be a problem to get the
// last
// character if the substring definition ends at the very last
// character
// we have to call a different substring function than normal.
// ----------------------------------------------------------------
if (currentPos == endPos && width <= maxAllowed)
{
lastPos = currentPos;
String s = labelText.substring(startPos);
line.addElement(s);
}
// ----------------------------------------------------------------
// in all other cases copy the substring that we have found to fit
// and
// add it as a new line of text to the line vector.
// ----------------------------------------------------------------
else
{
// ------------------------------------------------------------
// make sure it's not a single word. If so we must break it at
// the
// proper location.
// ------------------------------------------------------------
if (lastPos == startPos)
{
lastPos = startPos + breakWord(labelText.substring(startPos, currentPos), fm);
}
String s = labelText.substring(startPos, lastPos);
line.addElement(s);
}
// ----------------------------------------------------------------
// seek for the end of the white space to cut out any unnecessary
// spaces
// and tabs and set the new start condition.
// ----------------------------------------------------------------
startPos = getPosition(labelText, lastPos, SPACES, NOT_FOUND);
currentPos = startPos;
}
numLines = line.size();
lineWidth = new int[numLines];
}
/*-------------------------------------------------------------------*/
/**
* This method finds the font size, each line width and the widest line.
*/
/*-------------------------------------------------------------------*/
protected void measure()
{
if (!maxAllowedSet)
{
maxAllowed = getParent().getSize().width;
}
// return if width is too small
if (maxAllowed < (20))
{
return;
}
FontMetrics fm = this.getFontMetrics(this.getFont());
// return if no font metrics available
if (fm == null)
{
return;
}
divideLabel();
this.lineHeight = fm.getHeight();
this.lineDescent = fm.getDescent();
this.maxWidth = 0;
for (int i = 0; i < numLines; i++)
{
this.lineWidth[i] = fm.stringWidth(this.line.elementAt(i));
if (this.lineWidth[i] > this.maxWidth)
{
this.maxWidth = this.lineWidth[i];
}
}
}
/*-------------------------------------------------------------------*/
/**
* This method draws the label.
*
* @param graphics the device context
*/
/*-------------------------------------------------------------------*/
public void paint(Graphics graphics)
{
int x;
int y;
measure();
Dimension d = this.getSize();
y = lineAscent + (d.height - (numLines * lineHeight)) / 2;
for (int i = 0; i < numLines; i++)
{
y += lineHeight;
switch (alignment)
{
case LEFT:
x = marginWidth;
break;
case CENTER:
x = (d.width - lineWidth[i]) / 2;
break;
case RIGHT:
x = d.width - marginWidth - lineWidth[i];
break;
default:
x = (d.width - lineWidth[i]) / 2;
}
graphics.drawString(line.elementAt(i), x, y);
}
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to set the label text
*
* @param labelText the text to be displayed
*/
/*-------------------------------------------------------------------*/
public void setText(String labelText)
{
this.labelText = labelText;
repaint();
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to set the font that should be used to draw the label
*
* @param font font to be used within the label
*/
/*-------------------------------------------------------------------*/
public void setFont(Font font)
{
super.setFont(font);
repaint();
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to set the color in which the text should be drawn
*
* @param color the text color
*/
/*-------------------------------------------------------------------*/
public void setColor(Color color)
{
super.setForeground(color);
repaint();
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to set the text alignment for the label
*
* @param alignment the alignment, possible values are LEFT, CENTER, RIGHT
*/
/*-------------------------------------------------------------------*/
public void setJustify(int alignment)
{
this.alignment = alignment;
repaint();
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to set the max allowed line width
*
* @param width the max allowed line width in pixels
*/
/*-------------------------------------------------------------------*/
public void setMaxWidth(int width)
{
this.maxAllowed = width;
this.maxAllowedSet = true;
repaint();
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to set the horizontal margin
*
* @param margin the margin to the left and to the right of the label
*/
/*-------------------------------------------------------------------*/
public void setMarginWidth(int margin)
{
this.marginWidth = margin;
repaint();
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to set the vertical margin for the label
*
* @param margin the margin on the top and bottom of the label
*/
/*-------------------------------------------------------------------*/
public void setMarginHeight(int margin)
{
this.marginHeight = margin;
repaint();
}
/*-------------------------------------------------------------------*/
/**
* Moves and resizes this component. The new location of the top-left corner is specified by
* <code>x</code> and <code>y</code>, and the new size is specified by <code>width</code>
* and <code>height</code>.
*
* @param x The new x-coordinate of this component.
* @param y The new y-coordinate of this component.
* @param width The new width of this component.
* @param height The new height of this component.
*/
/*-------------------------------------------------------------------*/
public void setBounds(int x, int y, int width, int height)
{
super.setBounds(x, y, width, height);
this.maxAllowed = width;
this.maxAllowedSet = true;
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to retrieve the text alignment for the label
*
* @return alignment the text alignment currently in use for the label
*/
/*-------------------------------------------------------------------*/
public int getAlignment()
{
return (this.alignment);
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to retrieve the horizontal margin for the label
*
* @return marginWidth the margin currently in use to the left and right of the label
*/
/*-------------------------------------------------------------------*/
public int getMarginWidth()
{
return (this.marginWidth);
}
/*-------------------------------------------------------------------*/
/**
* This method may be used to retrieve the vertical margin for the label
*
* @return marginHeight the margin currently in use on the top and bottom of the label
*/
/*-------------------------------------------------------------------*/
public int getMarginHeight()
{
return (this.marginHeight);
}
/*-------------------------------------------------------------------*/
/**
* This method is typically used by the layout manager, it reports the necessary space to
* display the label comfortably.
*/
/*-------------------------------------------------------------------*/
public Dimension getPreferredSize()
{
measure();
return (new Dimension(maxAllowed, (numLines * (lineHeight + lineAscent + lineDescent))
+ (2 * marginHeight)));
}
/*-------------------------------------------------------------------*/
/**
* This method is typically used by the layout manager, it reports the absolute minimum space
* required to display the entire label.
*/
/*-------------------------------------------------------------------*/
public Dimension getMinimumSize()
{
measure();
return (new Dimension(maxAllowed, (numLines * (lineHeight + lineAscent + lineDescent))
+ (2 * marginHeight)));
}
/*-------------------------------------------------------------------*/
/**
* This method is called by the system after this object is first created.
*/
/*-------------------------------------------------------------------*/
public void addNotify()
{
super.addNotify(); // invoke the superclass
}
}
/*---------------------------------------------------------------------------*/
|