001: package org.gui4j.core.swing;
002:
003: import java.awt.Dimension;
004: import java.awt.FontMetrics;
005: import java.awt.Graphics;
006: import java.awt.Rectangle;
007: import java.util.StringTokenizer;
008:
009: import javax.swing.Icon;
010: import javax.swing.JComponent;
011: import javax.swing.JLabel;
012: import javax.swing.SwingConstants;
013: import javax.swing.SwingUtilities;
014: import javax.swing.plaf.LabelUI;
015: import javax.swing.plaf.basic.BasicGraphicsUtils;
016: import javax.swing.plaf.basic.BasicLabelUI;
017:
018: public class MultiLineLabelUI extends BasicLabelUI {
019: static {
020: labelUI = new MultiLineLabelUI();
021: }
022:
023: public static LabelUI getInstance() {
024: return labelUI;
025: }
026:
027: protected String layoutCL(JLabel label, FontMetrics fontMetrics,
028: String text, Icon icon, Rectangle viewR, Rectangle iconR,
029: Rectangle textR) {
030: String s = layoutCompoundLabel(label, fontMetrics,
031: splitStringByLines(text), icon, label
032: .getVerticalAlignment(), label
033: .getHorizontalAlignment(), label
034: .getVerticalTextPosition(), label
035: .getHorizontalTextPosition(), viewR, iconR,
036: textR, label.getIconTextGap());
037:
038: if (s.equals(""))
039: return text;
040: return s;
041: }
042:
043: static final int LEADING = SwingConstants.LEADING;
044: static final int TRAILING = SwingConstants.TRAILING;
045: static final int LEFT = SwingConstants.LEFT;
046: static final int RIGHT = SwingConstants.RIGHT;
047: static final int TOP = SwingConstants.TOP;
048: static final int CENTER = SwingConstants.CENTER;
049:
050: /**
051: * Compute and return the location of the icons origin, the
052: * location of origin of the text baseline, and a possibly clipped
053: * version of the compound labels string. Locations are computed
054: * relative to the viewR rectangle.
055: * The JComponents orientation (LEADING/TRAILING) will also be taken
056: * into account and translated into LEFT/RIGHT values accordingly.
057: * @param c
058: * @param fm
059: * @param text
060: * @param icon
061: * @param verticalAlignment
062: * @param horizontalAlignment
063: * @param verticalTextPosition
064: * @param horizontalTextPosition
065: * @param viewR
066: * @param iconR
067: * @param textR
068: * @param textIconGap
069: * @return String
070: */
071: public static String layoutCompoundLabel(JComponent c,
072: FontMetrics fm, String[] text, Icon icon,
073: int verticalAlignment, int horizontalAlignment,
074: int verticalTextPosition, int horizontalTextPosition,
075: Rectangle viewR, Rectangle iconR, Rectangle textR,
076: int textIconGap) {
077: boolean orientationIsLeftToRight = true;
078: int hAlign = horizontalAlignment;
079: int hTextPos = horizontalTextPosition;
080:
081: if (c != null) {
082: if (!(c.getComponentOrientation().isLeftToRight())) {
083: orientationIsLeftToRight = false;
084: }
085: }
086:
087: // Translate LEADING/TRAILING values in horizontalAlignment
088: // to LEFT/RIGHT values depending on the components orientation
089: switch (horizontalAlignment) {
090: case LEADING:
091: hAlign = (orientationIsLeftToRight) ? LEFT : RIGHT;
092: break;
093: case TRAILING:
094: hAlign = (orientationIsLeftToRight) ? RIGHT : LEFT;
095: break;
096: }
097:
098: // Translate LEADING/TRAILING values in horizontalTextPosition
099: // to LEFT/RIGHT values depending on the components orientation
100: switch (horizontalTextPosition) {
101: case LEADING:
102: hTextPos = (orientationIsLeftToRight) ? LEFT : RIGHT;
103: break;
104: case TRAILING:
105: hTextPos = (orientationIsLeftToRight) ? RIGHT : LEFT;
106: break;
107: }
108:
109: return layoutCompoundLabel(fm, text, icon, verticalAlignment,
110: hAlign, verticalTextPosition, hTextPos, viewR, iconR,
111: textR, textIconGap);
112: }
113:
114: /**
115: * Compute and return the location of the icons origin, the
116: * location of origin of the text baseline, and a possibly clipped
117: * version of the compound labels string. Locations are computed
118: * relative to the viewR rectangle.
119: * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING
120: * values in horizontalTextPosition (they will default to RIGHT) and in
121: * horizontalAlignment (they will default to CENTER).
122: * Use the other version of layoutCompoundLabel() instead.
123: * @param fm
124: * @param text
125: * @param icon
126: * @param verticalAlignment
127: * @param horizontalAlignment
128: * @param verticalTextPosition
129: * @param horizontalTextPosition
130: * @param viewR
131: * @param iconR
132: * @param textR
133: * @param textIconGap
134: * @return String
135: */
136: public static String layoutCompoundLabel(FontMetrics fm,
137: String[] text, Icon icon, int verticalAlignment,
138: int horizontalAlignment, int verticalTextPosition,
139: int horizontalTextPosition, Rectangle viewR,
140: Rectangle iconR, Rectangle textR, int textIconGap) {
141: /* Initialize the icon bounds rectangle iconR.
142: */
143:
144: if (icon != null) {
145: iconR.width = icon.getIconWidth();
146: iconR.height = icon.getIconHeight();
147: } else {
148: iconR.width = iconR.height = 0;
149: }
150:
151: /* Initialize the text bounds rectangle textR. If a null
152: * or and empty String was specified we substitute "" here
153: * and use 0,0,0,0 for textR.
154: */
155:
156: // Fix for textIsEmpty sent by Paulo Santos
157: boolean textIsEmpty = (text == null)
158: || (text.length == 0)
159: || (text.length == 1 && ((text[0] == null) || text[0]
160: .equals("")));
161:
162: String rettext = "";
163: if (textIsEmpty) {
164: textR.width = textR.height = 0;
165: } else {
166: Dimension dim = computeMultiLineDimension(fm, text);
167: textR.width = dim.width;
168: textR.height = dim.height;
169: }
170:
171: /* Unless both text and icon are non-null, we effectively ignore
172: * the value of textIconGap. The code that follows uses the
173: * value of gap instead of textIconGap.
174: */
175:
176: int gap = (textIsEmpty || (icon == null)) ? 0 : textIconGap;
177:
178: if (!textIsEmpty) {
179:
180: /* If the label text string is too wide to fit within the available
181: * space "..." and as many characters as will fit will be
182: * displayed instead.
183: */
184:
185: int availTextWidth;
186:
187: if (horizontalTextPosition == CENTER) {
188: availTextWidth = viewR.width;
189: } else {
190: availTextWidth = viewR.width - (iconR.width + gap);
191: }
192:
193: if (textR.width > availTextWidth && text.length == 1) {
194: String clipString = "...";
195: int totalWidth = SwingUtilities.computeStringWidth(fm,
196: clipString);
197: int nChars;
198: for (nChars = 0; nChars < text[0].length(); nChars++) {
199: totalWidth += fm.charWidth(text[0].charAt(nChars));
200: if (totalWidth > availTextWidth) {
201: break;
202: }
203: }
204: rettext = text[0].substring(0, nChars) + clipString;
205: textR.width = SwingUtilities.computeStringWidth(fm,
206: rettext);
207: }
208: }
209:
210: /* Compute textR.x,y given the verticalTextPosition and
211: * horizontalTextPosition properties
212: */
213:
214: if (verticalTextPosition == TOP) {
215: if (horizontalTextPosition != CENTER) {
216: textR.y = 0;
217: } else {
218: textR.y = -(textR.height + gap);
219: }
220: } else if (verticalTextPosition == CENTER) {
221: textR.y = (iconR.height / 2) - (textR.height / 2);
222: } else { // (verticalTextPosition == BOTTOM)
223: if (horizontalTextPosition != CENTER) {
224: textR.y = iconR.height - textR.height;
225: } else {
226: textR.y = (iconR.height + gap);
227: }
228: }
229:
230: if (horizontalTextPosition == LEFT) {
231: textR.x = -(textR.width + gap);
232: } else if (horizontalTextPosition == CENTER) {
233: textR.x = (iconR.width / 2) - (textR.width / 2);
234: } else { // (horizontalTextPosition == RIGHT)
235: textR.x = (iconR.width + gap);
236: }
237:
238: /* labelR is the rectangle that contains iconR and textR.
239: * Move it to its proper position given the labelAlignment
240: * properties.
241: *
242: * To avoid actually allocating a Rectangle, Rectangle.union
243: * has been inlined below.
244: */
245: int labelR_x = Math.min(iconR.x, textR.x);
246: int labelR_width = Math.max(iconR.x + iconR.width, textR.x
247: + textR.width)
248: - labelR_x;
249: int labelR_y = Math.min(iconR.y, textR.y);
250: int labelR_height = Math.max(iconR.y + iconR.height, textR.y
251: + textR.height)
252: - labelR_y;
253:
254: int dx, dy;
255:
256: if (verticalAlignment == TOP) {
257: dy = viewR.y - labelR_y;
258: } else if (verticalAlignment == CENTER) {
259: dy = (viewR.y + (viewR.height / 2))
260: - (labelR_y + (labelR_height / 2));
261: } else { // (verticalAlignment == BOTTOM)
262: dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
263: }
264:
265: if (horizontalAlignment == LEFT) {
266: dx = viewR.x - labelR_x;
267: } else if (horizontalAlignment == RIGHT) {
268: dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
269: } else { // (horizontalAlignment == CENTER)
270: dx = (viewR.x + (viewR.width / 2))
271: - (labelR_x + (labelR_width / 2));
272: }
273:
274: /* Translate textR and glypyR by dx,dy.
275: */
276:
277: textR.x += dx;
278: textR.y += dy;
279:
280: iconR.x += dx;
281: iconR.y += dy;
282:
283: return rettext;
284: }
285:
286: protected void paintEnabledText(JLabel l, Graphics g, String s,
287: int textX, int textY) {
288: int accChar = l.getDisplayedMnemonic();
289: g.setColor(l.getForeground());
290: drawString(g, s, accChar, textX, textY);
291: }
292:
293: protected void paintDisabledText(JLabel l, Graphics g, String s,
294: int textX, int textY) {
295: int accChar = l.getDisplayedMnemonic();
296: g.setColor(l.getBackground());
297: drawString(g, s, accChar, textX, textY);
298: }
299:
300: protected void drawString(Graphics g, String s, int accChar,
301: int textX, int textY) {
302: if (s.indexOf('\n') == -1)
303: BasicGraphicsUtils.drawString(g, s, accChar, textX, textY);
304: else {
305: String[] lStrs = splitStringByLines(s);
306: int height = g.getFontMetrics().getHeight();
307: // Only the first line can have the accel char
308: BasicGraphicsUtils.drawString(g, lStrs[0], accChar, textX,
309: textY);
310: for (int i = 1; i < lStrs.length; i++)
311: g.drawString(lStrs[i], textX, textY + (height * i));
312: }
313: }
314:
315: public static Dimension computeMultiLineDimension(FontMetrics fm,
316: String[] strs) {
317: int i, c, width = 0;
318: for (i = 0, c = strs.length; i < c; i++)
319: width = Math.max(width, SwingUtilities.computeStringWidth(
320: fm, strs[i]));
321: return new Dimension(width, fm.getHeight() * strs.length);
322: }
323:
324: protected String str;
325: protected String[] strs;
326:
327: public String[] splitStringByLines(String pStr) {
328: if (pStr.equals(this .str))
329: return strs;
330:
331: this .str = pStr;
332:
333: int lines = 1;
334: int i, c;
335: for (i = 0, c = pStr.length(); i < c; i++) {
336: if (pStr.charAt(i) == '\n')
337: lines++;
338: }
339: strs = new String[lines];
340: StringTokenizer st = new StringTokenizer(pStr, "\n");
341:
342: int line = 0;
343: while (st.hasMoreTokens())
344: strs[line++] = st.nextToken();
345:
346: return strs;
347: }
348: }
|