Mega Code Archive

 
Categories / Java / Swing Components
 

Multi Line Label extends JComponent

/*  * 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     } } /*---------------------------------------------------------------------------*/