View Javadoc

1   /***
2    * 
3    */
4   package org.telscenter.pas.ui;
5   
6   import java.awt.Dimension;
7   import java.awt.FontMetrics;
8   import java.awt.Graphics;
9   import java.awt.Rectangle;
10  import java.util.StringTokenizer;
11  
12  import javax.swing.Icon;
13  import javax.swing.JComponent;
14  import javax.swing.JLabel;
15  import javax.swing.SwingConstants;
16  import javax.swing.SwingUtilities;
17  import javax.swing.plaf.basic.BasicGraphicsUtils;
18  import javax.swing.plaf.basic.BasicLabelUI;
19  
20  /***
21   * @author aperritano
22   *
23   */
24  public class MultiLineLabelUI extends BasicLabelUI
25  {
26  	static {
27  		labelUI = new MultiLineLabelUI();
28  	}
29  	
30      protected String layoutCL(
31          JLabel label,                  
32          FontMetrics fontMetrics, 
33          String text, 
34          Icon icon, 
35          Rectangle viewR, 
36          Rectangle iconR, 
37          Rectangle textR)
38      {
39          String s = layoutCompoundLabel(
40              (JComponent) label,
41              fontMetrics,
42              splitStringByLines(text),
43              icon,
44              label.getVerticalAlignment(),
45              label.getHorizontalAlignment(),
46              label.getVerticalTextPosition(),
47              label.getHorizontalTextPosition(),
48              viewR,
49              iconR,
50              textR,
51              label.getIconTextGap());
52      	
53      	if( s.equals("") )
54      		return text;
55      	return s;
56      }
57  	
58  	
59  	static final int LEADING = SwingConstants.LEADING;
60  	static final int TRAILING = SwingConstants.TRAILING;
61  	static final int LEFT = SwingConstants.LEFT;
62  	static final int RIGHT = SwingConstants.RIGHT;
63  	static final int TOP = SwingConstants.TOP;
64  	static final int CENTER = SwingConstants.CENTER;
65  
66  	/***
67       * Compute and return the location of the icons origin, the
68       * location of origin of the text baseline, and a possibly clipped
69       * version of the compound labels string.  Locations are computed
70       * relative to the viewR rectangle.
71       * The JComponents orientation (LEADING/TRAILING) will also be taken
72       * into account and translated into LEFT/RIGHT values accordingly.
73       */
74      public static String layoutCompoundLabel(JComponent c,
75                                               FontMetrics fm,
76                                               String[] text,
77                                               Icon icon,
78                                               int verticalAlignment,
79                                               int horizontalAlignment,
80                                               int verticalTextPosition,
81                                               int horizontalTextPosition,
82                                               Rectangle viewR,
83                                               Rectangle iconR,
84                                               Rectangle textR,
85                                               int textIconGap)
86      {
87          boolean orientationIsLeftToRight = true;
88          int     hAlign = horizontalAlignment;
89          int     hTextPos = horizontalTextPosition;
90  
91          
92          if (c != null) {
93              if (!(c.getComponentOrientation().isLeftToRight())) {
94                  orientationIsLeftToRight = false;
95              }
96          }
97          
98  
99          // Translate LEADING/TRAILING values in horizontalAlignment
100         // to LEFT/RIGHT values depending on the components orientation
101         switch (horizontalAlignment) {
102         case LEADING: 
103             hAlign = (orientationIsLeftToRight) ? LEFT : RIGHT;
104             break;
105         case TRAILING: 
106             hAlign = (orientationIsLeftToRight) ? RIGHT : LEFT;
107             break;
108         }
109 
110         // Translate LEADING/TRAILING values in horizontalTextPosition
111         // to LEFT/RIGHT values depending on the components orientation
112         switch (horizontalTextPosition) {
113         case LEADING: 
114             hTextPos = (orientationIsLeftToRight) ? LEFT : RIGHT;
115             break;
116         case TRAILING: 
117             hTextPos = (orientationIsLeftToRight) ? RIGHT : LEFT;
118             break;
119         }
120 
121         return layoutCompoundLabel(fm,
122                                    text,
123                                    icon,
124                                    verticalAlignment,
125                                    hAlign,
126                                    verticalTextPosition,
127                                    hTextPos,
128                                    viewR,
129                                    iconR,
130                                    textR,
131                                    textIconGap);
132     }
133 
134 
135     /***
136      * Compute and return the location of the icons origin, the
137      * location of origin of the text baseline, and a possibly clipped
138      * version of the compound labels string.  Locations are computed
139      * relative to the viewR rectangle.
140      * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING
141      * values in horizontalTextPosition (they will default to RIGHT) and in
142      * horizontalAlignment (they will default to CENTER).
143      * Use the other version of layoutCompoundLabel() instead.
144      */
145     public static String layoutCompoundLabel(
146         FontMetrics fm,
147         String[] text,
148         Icon icon,
149         int verticalAlignment,
150         int horizontalAlignment,
151         int verticalTextPosition,
152         int horizontalTextPosition,
153         Rectangle viewR,
154         Rectangle iconR,
155         Rectangle textR,
156         int textIconGap)
157     {
158         /* Initialize the icon bounds rectangle iconR.
159          */
160 
161         if (icon != null) {
162             iconR.width = icon.getIconWidth();
163             iconR.height = icon.getIconHeight();
164         }
165         else {
166             iconR.width = iconR.height = 0;
167         }
168 
169         /* Initialize the text bounds rectangle textR.  If a null
170          * or and empty String was specified we substitute "" here
171          * and use 0,0,0,0 for textR.
172          */
173 
174         // Fix for textIsEmpty sent by Paulo Santos
175         boolean textIsEmpty = (text == null) || (text.length == 0)
176 			|| (text.length == 1 && ( (text[0]==null) || text[0].equals("") ));
177 
178     	String rettext = "";
179         if (textIsEmpty) {
180             textR.width = textR.height = 0;
181         }
182         else {
183         	Dimension dim = computeMultiLineDimension( fm, text );
184             textR.width = dim.width;
185             textR.height = dim.height;
186         }
187 
188         /* Unless both text and icon are non-null, we effectively ignore
189          * the value of textIconGap.  The code that follows uses the
190          * value of gap instead of textIconGap.
191          */
192 
193         int gap = (textIsEmpty || (icon == null)) ? 0 : textIconGap;
194 
195         if (!textIsEmpty) {
196 
197             /* If the label text string is too wide to fit within the available
198              * space "..." and as many characters as will fit will be
199              * displayed instead.
200              */
201 
202             int availTextWidth;
203 
204             if (horizontalTextPosition == CENTER) {
205                 availTextWidth = viewR.width;
206             }
207             else {
208                 availTextWidth = viewR.width - (iconR.width + gap);
209             }
210 
211 
212             if (textR.width > availTextWidth && text.length == 1) {
213                 String clipString = "...";
214                 int totalWidth = SwingUtilities.computeStringWidth(fm,clipString);
215                 int nChars;
216                 for(nChars = 0; nChars < text[0].length(); nChars++) {
217                     totalWidth += fm.charWidth(text[0].charAt(nChars));
218                     if (totalWidth > availTextWidth) {
219                         break;
220                     }
221                 }
222                 rettext = text[0].substring(0, nChars) + clipString;
223                 textR.width = SwingUtilities.computeStringWidth(fm,rettext);
224             }
225         }
226 
227 
228         /* Compute textR.x,y given the verticalTextPosition and
229          * horizontalTextPosition properties
230          */
231 
232         if (verticalTextPosition == TOP) {
233             if (horizontalTextPosition != CENTER) {
234                 textR.y = 0;
235             }
236             else {
237                 textR.y = -(textR.height + gap);
238             }
239         }
240         else if (verticalTextPosition == CENTER) {
241             textR.y = (iconR.height / 2) - (textR.height / 2);
242         }
243         else { // (verticalTextPosition == BOTTOM)
244             if (horizontalTextPosition != CENTER) {
245                 textR.y = iconR.height - textR.height;
246             }
247             else {
248                 textR.y = (iconR.height + gap);
249             }
250         }
251 
252         if (horizontalTextPosition == LEFT) {
253             textR.x = -(textR.width + gap);
254         }
255         else if (horizontalTextPosition == CENTER) {
256             textR.x = (iconR.width / 2) - (textR.width / 2);
257         }
258         else { // (horizontalTextPosition == RIGHT)
259             textR.x = (iconR.width + gap);
260         }
261 
262         /* labelR is the rectangle that contains iconR and textR.
263          * Move it to its proper position given the labelAlignment
264          * properties.
265          *
266          * To avoid actually allocating a Rectangle, Rectangle.union
267          * has been inlined below.
268          */
269         int labelR_x = Math.min(iconR.x, textR.x);
270         int labelR_width = Math.max(iconR.x + iconR.width,
271                                     textR.x + textR.width) - labelR_x;
272         int labelR_y = Math.min(iconR.y, textR.y);
273         int labelR_height = Math.max(iconR.y + iconR.height,
274                                      textR.y + textR.height) - labelR_y;
275 
276         int dx, dy;
277 
278         if (verticalAlignment == TOP) {
279             dy = viewR.y - labelR_y;
280         }
281         else if (verticalAlignment == CENTER) {
282             dy = (viewR.y + (viewR.height / 2)) - (labelR_y + (labelR_height / 2));
283         }
284         else { // (verticalAlignment == BOTTOM)
285             dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
286         }
287 
288         if (horizontalAlignment == LEFT) {
289             dx = viewR.x - labelR_x;
290         }
291         else if (horizontalAlignment == RIGHT) {
292             dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
293         }
294         else { // (horizontalAlignment == CENTER)
295             dx = (viewR.x + (viewR.width / 2)) -
296                  (labelR_x + (labelR_width / 2));
297         }
298 
299         /* Translate textR and glypyR by dx,dy.
300          */
301 
302         textR.x += dx;
303         textR.y += dy;
304 
305         iconR.x += dx;
306         iconR.y += dy;
307 
308         return rettext;
309     }
310 	
311     protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)
312     {
313         int accChar = l.getDisplayedMnemonic();
314         g.setColor(l.getForeground());
315         drawString(g, s, accChar, textX, textY);
316     }
317 
318 
319     protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)
320     {
321 		int accChar = l.getDisplayedMnemonic();
322 		g.setColor(l.getBackground());
323     	drawString(g, s, accChar, textX, textY);
324     }
325 	
326 	protected void drawString( Graphics g, String s, int accChar, int textX, int textY )
327 	{
328     	if( s.indexOf('\n') == -1 )
329 			BasicGraphicsUtils.drawString(g, s, accChar, textX, textY);
330     	else
331     	{
332     		String[] strs = splitStringByLines( s );
333     		int height = g.getFontMetrics().getHeight();
334     		// Only the first line can have the accel char
335     		BasicGraphicsUtils.drawString(g, strs[0], accChar, textX, textY);
336     		for( int i = 1; i < strs.length; i++ )
337     			g.drawString( strs[i], textX, textY + (height*i) );
338     	}
339 	}
340 	
341 	public static Dimension computeMultiLineDimension( FontMetrics fm, String[] strs )
342 	{
343 		int i, c, width = 0;
344         for(i=0, c=strs.length ; i < c ; i++)
345         	width = Math.max( width, SwingUtilities.computeStringWidth(fm,strs[i]) );
346 		return new Dimension( width, fm.getHeight() * strs.length );
347 	}
348 	
349 	
350 	protected String str;
351 	protected String[] strs;
352 	
353 	public String[] splitStringByLines( String str )
354 	{
355 		if( str.equals(this.str) )
356 			return strs;
357 		
358 		this.str = str;
359 		
360 		int lines = 1;
361 		int i, c;
362         for(i=0, c=str.length() ; i < c ; i++) {
363             if( str.charAt(i) == '\n' )
364             	lines++;
365         }
366 		strs = new String[lines];
367 		StringTokenizer st = new StringTokenizer( str, "\n" );
368 		
369 		int line = 0;
370 		while( st.hasMoreTokens() )
371 			strs[line++] = st.nextToken();
372 			
373 		return strs;
374 	}
375 }