KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > izforge > izpack > util > MultiLineLabel


1 /*
2  * IzPack - Copyright 2001-2007 Julien Ponge, All Rights Reserved.
3  *
4  * http://www.izforge.com/izpack/
5  * http://developer.berlios.de/projects/izpack/
6  *
7  * Copyright 1997,2002 Elmar Grom
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  * http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */

21
22 package com.izforge.izpack.util;
23
24 import java.awt.Color JavaDoc;
25 import java.awt.Dimension JavaDoc;
26 import java.awt.Font JavaDoc;
27 import java.awt.FontMetrics JavaDoc;
28 import java.awt.Graphics JavaDoc;
29 import java.util.Vector JavaDoc;
30
31 import javax.swing.JComponent JavaDoc;
32
33 /*---------------------------------------------------------------------------*/
34 /**
35  * <BR>
36  * <code>MultiLineLabel</code> may be used in place of javax.swing.JLabel. <BR>
37  * <BR>
38  * This class implements a component that is capable of displaying multiple lines of text. Line
39  * breaks are inserted automatically whenever a line of text extends beyond the predefined maximum
40  * line length. Line breaks will only be inserted between words, except where a single word is
41  * longer than the maximum line length. Line breaks may be forced at any location in the text by
42  * inserting a newline (\n). White space that is not valuable (i.e. is placed at the beginning of a
43  * new line or at the very beginning or end of the text) is removed. <br>
44  * <br>
45  * <b>Note:</b> you can set the maximum width of the label either through one of the constructors
46  * or you can call <code>setMaxWidth()</code> explicitly. If this is not set,
47  * <code>MultiLineLabel</code> will derive its width from the parent component.
48  *
49  * @version 0.0.1 / 05-15-97
50  * @version 1.0 / 04-13-02
51  * @author Elmar Grom
52  */

53 /*---------------------------------------------------------------------------*
54  * Reviving some old code here that was written before there was swing.
55  * The original was written to work with awt. I had to do some masaging to
56  * make it a JComponent and I hope it behaves like a reasonably good mannered
57  * swing component.
58  *---------------------------------------------------------------------------*/

59 public class MultiLineLabel extends JComponent JavaDoc
60 {
61
62     /**
63      *
64      */

65     private static final long serialVersionUID = 4051045255031894837L;
66
67     public static final int LEFT = 0; // alignment constants
68

69     public static final int CENTER = 1;
70
71     public static final int RIGHT = 2;
72
73     public static final int DEFAULT_MARGIN = 10;
74
75     public static final int DEFAULT_ALIGN = LEFT;
76
77     public static final int LEAST_ALLOWED = 200; // default setting for
78

79     // maxAllowed
80

81     private static final int FOUND = 0; // constants for string search.
82

83     private static final int NOT_FOUND = 1;
84
85     private static final int NOT_DONE = 0;
86
87     private static final int DONE = 1;
88
89     private static final char[] WHITE_SPACE = { ' ', '\n', '\t'};
90
91     private static final char[] SPACES = { ' ', '\t'};
92
93     private static final char NEW_LINE = '\n';
94
95     protected Vector JavaDoc line = new Vector JavaDoc();// text lines to display
96

97     protected String JavaDoc labelText; // text lines to display
98

99     protected int numLines; // the number of lines
100

101     protected int marginHeight; // top and bottom margins
102

103     protected int marginWidth; // left and right margins
104

105     protected int lineHeight; // total height of the font
106

107     protected int lineAscent; // font height above the baseline
108

109     protected int lineDescent; // font hight below the baseline
110

111     protected int[] lineWidth; // width of each line
112

113     protected int maxWidth; // width of the widest line
114

115     private int maxAllowed = LEAST_ALLOWED; // max width allowed to use
116

117     private boolean maxAllowedSet = false; // signals if the max allowed width
118

119     // has been explicitly set
120

121     protected int alignment = LEFT; // default text alignment
122

123     /*-------------------------------------------------------------------*/
124     /**
125      * Constructor
126      *
127      * @param text the text to be displayed
128      * @param horMargin the horizontal margin for the label
129      * @param vertMargin the vertical margin for the label
130      * @param maxWidth the maximum allowed width of the text
131      * @param justify the text alignment for the label
132      */

133     /*-------------------------------------------------------------------*
134      * <detailed description / implementation details if applicable>
135      *-------------------------------------------------------------------*/

136     public MultiLineLabel(String JavaDoc text, int horMargin, int vertMargin, int maxWidth, int justify)
137     {
138         this.labelText = text;
139         this.marginWidth = horMargin;
140         this.marginHeight = vertMargin;
141         this.maxAllowed = maxWidth;
142         this.maxAllowedSet = true;
143         this.alignment = justify;
144     }
145
146     /*-------------------------------------------------------------------*/
147     /**
148      * Constructor using default max-width and alignment.
149      *
150      * @param label the text to be displayed
151      * @param marginWidth the horizontal margin for the label
152      * @param marginHeight the vertical margin for the label
153      */

154     /*-------------------------------------------------------------------*
155      * <detailed description / implementation details if applicable>
156      *-------------------------------------------------------------------*/

157     public MultiLineLabel(String JavaDoc label, int marginWidth, int marginHeight)
158     {
159         this.labelText = label;
160         this.marginWidth = marginWidth;
161         this.marginHeight = marginHeight;
162     }
163
164     /*-------------------------------------------------------------------*/
165     /**
166      * Constructor using default max-width, and margin.
167      *
168      * @param label the text to be displayed
169      * @param alignment the text alignment for the label
170      */

171     /*-------------------------------------------------------------------*
172      * <detailed description / implementation details if applicable>
173      *-------------------------------------------------------------------*/

174     public MultiLineLabel(String JavaDoc label, int alignment)
175     {
176         this.labelText = label;
177         this.alignment = alignment;
178     }
179
180     /*-------------------------------------------------------------------*/
181     /**
182      * Constructor using default max-width, alignment, and margin.
183      *
184      * @param label the text to be displayed
185      */

186     /*-------------------------------------------------------------------*
187      * <detailed description / implementation details if applicable>
188      *-------------------------------------------------------------------*/

189     public MultiLineLabel(String JavaDoc label)
190     {
191         this.labelText = label;
192     }
193
194     /*-------------------------------------------------------------------*/
195     /**
196      * This method searches the target string for occurences of any of the characters in the source
197      * string. The return value is the position of the first hit. Based on the mode parameter the
198      * hit position is either the position where any of the source characters first was found or the
199      * first position where none of the source characters where found.
200      *
201      *
202      * @return position of the first occurence
203      * @param target the text to be searched
204      * @param start the start position for the search
205      * @param source the list of characters to be searched for
206      * @param mode the search mode FOUND = reports first found NOT_FOUND = reports first not found
207      */

208     /*-------------------------------------------------------------------*
209      * <detailed description / implementation details if applicable>
210      *-------------------------------------------------------------------*/

211     int getPosition(String JavaDoc target, int start, char[] source, int mode)
212     {
213         int status;
214         int position;
215         int scan;
216         int targetEnd;
217         int sourceLength;
218         char temp;
219
220         targetEnd = (target.length() - 1);
221         sourceLength = source.length;
222         position = start;
223
224         if (mode == FOUND)
225         {
226             status = NOT_DONE;
227             while (status != DONE)
228             {
229                 position++;
230                 if (!(position < targetEnd)) // end of string reached, the
231
// next
232
{ // statement would cause a runtime error
233
return (targetEnd);
234                 }
235                 temp = target.charAt(position);
236                 for (scan = 0; scan < sourceLength; scan++) // walk through the
237
// source
238
{ // string and compare each char
239
if (source[scan] == temp)
240                     {
241                         status = DONE;
242                     }
243                 }
244             }
245             return (position);
246         }
247         else if (mode == NOT_FOUND)
248         {
249             status = NOT_DONE;
250             while (status != DONE)
251             {
252                 position++;
253                 if (!(position < targetEnd)) // end of string reached, the
254
// next
255
{ // statement would cause a runtime error
256
return (targetEnd);
257                 }
258                 temp = target.charAt(position);
259                 status = DONE;
260                 for (scan = 0; scan < sourceLength; scan++) // walk through the
261
// source
262
{ // string and compare each char
263
if (source[scan] == temp)
264                     {
265                         status = NOT_DONE;
266                     }
267                 }
268             }
269             return (position);
270         }
271         return (0);
272     }
273
274     /*-------------------------------------------------------------------*/
275     /**
276      * This method scans the input string until the max allowed width is reached. The return value
277      * indicates the position just before this happens.
278      *
279      *
280      * @return position character position just before the string is too long
281      * @param word word to break
282      */

283     /*-------------------------------------------------------------------*
284      * <detailed description / implementation details if applicable>
285      *-------------------------------------------------------------------*/

286     int breakWord(String JavaDoc word, FontMetrics JavaDoc fm)
287     {
288         int width;
289         int currentPos;
290         int endPos;
291
292         width = 0;
293         currentPos = 0;
294         endPos = word.length() - 1;
295
296         // make sure we don't end up with a negative position
297
if (endPos <= 0) { return (currentPos); }
298         // seek the position where the word first is longer than allowed
299
while ((width < maxAllowed) && (currentPos < endPos))
300         {
301             currentPos++;
302             width = fm.stringWidth(labelText.substring(0, currentPos));
303         }
304         // adjust to get the chatacter just before (this should make it a bit
305
// shorter than allowed!)
306
if (currentPos != endPos)
307         {
308             currentPos--;
309         }
310         return (currentPos);
311     }
312
313     /*-------------------------------------------------------------------*/
314     /**
315      * This method breaks the label text up into multiple lines of text. Line breaks are established
316      * based on the maximum available space. A new line is started whenever a line break is
317      * encountered, even if the permissible length is not yet reached. Words are broken only if a
318      * single word happens to be longer than one line.
319      */

320     /*-------------------------------------------------------------------*/
321     private void divideLabel()
322     {
323         int width;
324         int startPos;
325         int currentPos;
326         int lastPos;
327         int endPos;
328
329         line.clear();
330         FontMetrics JavaDoc fm = this.getFontMetrics(this.getFont());
331
332         startPos = 0;
333         currentPos = startPos;
334         lastPos = currentPos;
335         endPos = (labelText.length() - 1);
336
337         while (currentPos < endPos)
338         {
339             width = 0;
340             // ----------------------------------------------------------------
341
// find the first substring that occupies more than the granted
342
// space.
343
// Break at the end of the string or a line break
344
// ----------------------------------------------------------------
345
while ((width < maxAllowed) && (currentPos < endPos)
346                     && (labelText.charAt(currentPos) != NEW_LINE))
347             {
348                 lastPos = currentPos;
349                 currentPos = getPosition(labelText, currentPos, WHITE_SPACE, FOUND);
350                 width = fm.stringWidth(labelText.substring(startPos, currentPos));
351             }
352             // ----------------------------------------------------------------
353
// if we have a line break we want to copy everything up to
354
// currentPos
355
// ----------------------------------------------------------------
356
if (labelText.charAt(currentPos) == NEW_LINE)
357             {
358                 lastPos = currentPos;
359             }
360             // ----------------------------------------------------------------
361
// if we are at the end of the string we want to copy everything up
362
// to
363
// the last character. Since there seems to be a problem to get the
364
// last
365
// character if the substring definition ends at the very last
366
// character
367
// we have to call a different substring function than normal.
368
// ----------------------------------------------------------------
369
if (currentPos == endPos && width <= maxAllowed)
370             {
371                 lastPos = currentPos;
372                 String JavaDoc s = labelText.substring(startPos);
373                 line.addElement(s);
374             }
375             // ----------------------------------------------------------------
376
// in all other cases copy the substring that we have found to fit
377
// and
378
// add it as a new line of text to the line vector.
379
// ----------------------------------------------------------------
380
else
381             {
382                 // ------------------------------------------------------------
383
// make sure it's not a single word. If so we must break it at
384
// the
385
// proper location.
386
// ------------------------------------------------------------
387
if (lastPos == startPos)
388                 {
389                     lastPos = startPos + breakWord(labelText.substring(startPos, currentPos), fm);
390                 }
391                 String JavaDoc s = labelText.substring(startPos, lastPos);
392                 line.addElement(s);
393             }
394
395             // ----------------------------------------------------------------
396
// seek for the end of the white space to cut out any unnecessary
397
// spaces
398
// and tabs and set the new start condition.
399
// ----------------------------------------------------------------
400
startPos = getPosition(labelText, lastPos, SPACES, NOT_FOUND);
401             currentPos = startPos;
402         }
403
404         numLines = line.size();
405         lineWidth = new int[numLines];
406     }
407
408     /*-------------------------------------------------------------------*/
409     /**
410      * This method finds the font size, each line width and the widest line.
411      *
412      */

413     /*-------------------------------------------------------------------*/
414     protected void measure()
415     {
416         if (!maxAllowedSet)
417         {
418             maxAllowed = getParent().getSize().width;
419         }
420
421         // return if width is too small
422
if (maxAllowed < (20)) { return; }
423
424         FontMetrics JavaDoc fm = this.getFontMetrics(this.getFont());
425
426         // return if no font metrics available
427
if (fm == null) { return; }
428
429         divideLabel();
430
431         this.lineHeight = fm.getHeight();
432         this.lineDescent = fm.getDescent();
433         this.maxWidth = 0;
434
435         for (int i = 0; i < numLines; i++)
436         {
437             this.lineWidth[i] = fm.stringWidth((String JavaDoc) this.line.elementAt(i));
438             if (this.lineWidth[i] > this.maxWidth)
439             {
440                 this.maxWidth = this.lineWidth[i];
441             }
442         }
443     }
444
445     /*-------------------------------------------------------------------*/
446     /**
447      * This method draws the label.
448      *
449      * @param graphics the device context
450      */

451     /*-------------------------------------------------------------------*/
452     public void paint(Graphics JavaDoc graphics)
453     {
454         int x;
455         int y;
456
457         measure();
458         Dimension JavaDoc d = this.getSize();
459
460         y = lineAscent + (d.height - (numLines * lineHeight)) / 2;
461
462         for (int i = 0; i < numLines; i++)
463         {
464             y += lineHeight;
465             switch (alignment)
466             {
467             case LEFT:
468                 x = marginWidth;
469                 break;
470             case CENTER:
471                 x = (d.width - lineWidth[i]) / 2;
472                 break;
473             case RIGHT:
474                 x = d.width - marginWidth - lineWidth[i];
475                 break;
476             default:
477                 x = (d.width - lineWidth[i]) / 2;
478             }
479             graphics.drawString((String JavaDoc) line.elementAt(i), x, y);
480         }
481     }
482
483     /*-------------------------------------------------------------------*/
484     /**
485      * This method may be used to set the label text
486      *
487      * @param labelText the text to be displayed
488      */

489     /*-------------------------------------------------------------------*/
490     public void setText(String JavaDoc labelText)
491     {
492         this.labelText = labelText;
493         repaint();
494     }
495
496     /*-------------------------------------------------------------------*/
497     /**
498      * This method may be used to set the font that should be used to draw the label
499      *
500      * @param font font to be used within the label
501      */

502     /*-------------------------------------------------------------------*/
503     public void setFont(Font JavaDoc font)
504     {
505         super.setFont(font);
506         repaint();
507     }
508
509     /*-------------------------------------------------------------------*/
510     /**
511      * This method may be used to set the color in which the text should be drawn
512      *
513      * @param color the text color
514      */

515     /*-------------------------------------------------------------------*/
516     public void setColor(Color JavaDoc color)
517     {
518         super.setForeground(color);
519         repaint();
520     }
521
522     /*-------------------------------------------------------------------*/
523     /**
524      * This method may be used to set the text alignment for the label
525      *
526      * @param alignment the alignment, possible values are LEFT, CENTER, RIGHT
527      */

528     /*-------------------------------------------------------------------*/
529     public void setJustify(int alignment)
530     {
531         this.alignment = alignment;
532         repaint();
533     }
534
535     /*-------------------------------------------------------------------*/
536     /**
537      * This method may be used to set the max allowed line width
538      *
539      * @param width the max allowed line width in pixels
540      */

541     /*-------------------------------------------------------------------*/
542     public void setMaxWidth(int width)
543     {
544         this.maxAllowed = width;
545         this.maxAllowedSet = true;
546         repaint();
547     }
548
549     /*-------------------------------------------------------------------*/
550     /**
551      * This method may be used to set the horizontal margin
552      *
553      * @param margin the margin to the left and to the right of the label
554      */

555     /*-------------------------------------------------------------------*/
556     public void setMarginWidth(int margin)
557     {
558         this.marginWidth = margin;
559         repaint();
560     }
561
562     /*-------------------------------------------------------------------*/
563     /**
564      * This method may be used to set the vertical margin for the label
565      *
566      * @param margin the margin on the top and bottom of the label
567      */

568     /*-------------------------------------------------------------------*/
569     public void setMarginHeight(int margin)
570     {
571         this.marginHeight = margin;
572         repaint();
573     }
574
575     /*-------------------------------------------------------------------*/
576     /**
577      * Moves and resizes this component. The new location of the top-left corner is specified by
578      * <code>x</code> and <code>y</code>, and the new size is specified by <code>width</code>
579      * and <code>height</code>.
580      *
581      * @param x The new x-coordinate of this component.
582      * @param y The new y-coordinate of this component.
583      * @param width The new width of this component.
584      * @param height The new height of this component.
585      */

586     /*-------------------------------------------------------------------*/
587     public void setBounds(int x, int y, int width, int height)
588     {
589         super.setBounds(x, y, width, height);
590         this.maxAllowed = width;
591         this.maxAllowedSet = true;
592     }
593
594     /*-------------------------------------------------------------------*/
595     /**
596      * This method may be used to retrieve the text alignment for the label
597      *
598      * @return alignment the text alignment currently in use for the label
599      */

600     /*-------------------------------------------------------------------*/
601     public int getAlignment()
602     {
603         return (this.alignment);
604     }
605
606     /*-------------------------------------------------------------------*/
607     /**
608      * This method may be used to retrieve the horizontal margin for the label
609      *
610      * @return marginWidth the margin currently in use to the left and right of the label
611      */

612     /*-------------------------------------------------------------------*/
613     public int getMarginWidth()
614     {
615         return (this.marginWidth);
616     }
617
618     /*-------------------------------------------------------------------*/
619     /**
620      * This method may be used to retrieve the vertical margin for the label
621      *
622      * @return marginHeight the margin currently in use on the top and bottom of the label
623      */

624     /*-------------------------------------------------------------------*/
625     public int getMarginHeight()
626     {
627         return (this.marginHeight);
628     }
629
630     /*-------------------------------------------------------------------*/
631     /**
632      * This method is typically used by the layout manager, it reports the necessary space to
633      * display the label comfortably.
634      */

635     /*-------------------------------------------------------------------*/
636     public Dimension JavaDoc getPreferredSize()
637     {
638         measure();
639         return (new Dimension JavaDoc(maxAllowed, (numLines * (lineHeight + lineAscent + lineDescent))
640                 + (2 * marginHeight)));
641     }
642
643     /*-------------------------------------------------------------------*/
644     /**
645      * This method is typically used by the layout manager, it reports the absolute minimum space
646      * required to display the entire label.
647      *
648      */

649     /*-------------------------------------------------------------------*/
650     public Dimension JavaDoc getMinimumSize()
651     {
652         measure();
653         return (new Dimension JavaDoc(maxAllowed, (numLines * (lineHeight + lineAscent + lineDescent))
654                 + (2 * marginHeight)));
655     }
656
657     /*-------------------------------------------------------------------*/
658     /**
659      * This method is called by the system after this object is first created.
660      *
661      */

662     /*-------------------------------------------------------------------*/
663     public void addNotify()
664     {
665         super.addNotify(); // invoke the superclass
666
}
667 }
668 /*---------------------------------------------------------------------------*/
669
Popular Tags