Cleanup (dead code)
[idea/community.git] / platform / platform-api / src / com / intellij / openapi / ui / MultiLineLabelUI.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.openapi.ui;
17
18 import com.intellij.util.ArrayUtil;
19 import com.intellij.util.ui.UIUtil;
20
21 import javax.swing.*;
22 import javax.swing.plaf.basic.BasicGraphicsUtils;
23 import javax.swing.plaf.basic.BasicLabelUI;
24 import java.awt.*;
25 import java.util.ArrayList;
26 import java.util.StringTokenizer;
27
28 /**
29  * Based on Zafir Anjum example.
30  */
31 public class MultiLineLabelUI extends BasicLabelUI {
32   private String myString;
33   private String[] myLines;
34
35   protected String layoutCL(
36     JLabel label,
37     FontMetrics fontMetrics,
38     String text,
39     Icon icon,
40     Rectangle viewR,
41     Rectangle iconR,
42     Rectangle textR) {
43     String s = layoutCompoundLabel(
44       label,
45       fontMetrics,
46       splitStringByLines(text),
47       icon,
48       label.getVerticalAlignment(),
49       label.getHorizontalAlignment(),
50       label.getVerticalTextPosition(),
51       label.getHorizontalTextPosition(),
52       viewR,
53       iconR,
54       textR,
55       label.getIconTextGap());
56
57     if ("".equals(s))
58       return text;
59     return s;
60   }
61
62   static final int LEADING = SwingConstants.LEADING;
63   static final int TRAILING = SwingConstants.TRAILING;
64   static final int LEFT = SwingConstants.LEFT;
65   static final int RIGHT = SwingConstants.RIGHT;
66   static final int TOP = SwingConstants.TOP;
67   static final int CENTER = SwingConstants.CENTER;
68
69   /**
70    * Compute and return the location of the icons origin, the
71    * location of origin of the text baseline, and a possibly clipped
72    * version of the compound labels string.  Locations are computed
73    * relative to the viewR rectangle.
74    * The JComponents orientation (LEADING/TRAILING) will also be taken
75    * into account and translated into LEFT/RIGHT values accordingly.
76    */
77   public static String layoutCompoundLabel(JComponent c,
78     FontMetrics fm,
79     String[] text,
80     Icon icon,
81     int verticalAlignment,
82     int horizontalAlignment,
83     int verticalTextPosition,
84     int horizontalTextPosition,
85     Rectangle viewR,
86     Rectangle iconR,
87     Rectangle textR,
88     int textIconGap) {
89     boolean orientationIsLeftToRight = true;
90     int hAlign = horizontalAlignment;
91     int hTextPos = horizontalTextPosition;
92
93
94     if (c != null) {
95       if (!(c.getComponentOrientation().isLeftToRight())) {
96         orientationIsLeftToRight = false;
97       }
98     }
99
100
101     // Translate LEADING/TRAILING values in horizontalAlignment
102     // to LEFT/RIGHT values depending on the components orientation
103     switch (horizontalAlignment) {
104     case LEADING:
105       hAlign = (orientationIsLeftToRight) ? LEFT : RIGHT;
106       break;
107     case TRAILING:
108       hAlign = (orientationIsLeftToRight) ? RIGHT : LEFT;
109       break;
110     }
111
112     // Translate LEADING/TRAILING values in horizontalTextPosition
113     // to LEFT/RIGHT values depending on the components orientation
114     switch (horizontalTextPosition) {
115     case LEADING:
116       hTextPos = (orientationIsLeftToRight) ? LEFT : RIGHT;
117       break;
118     case TRAILING:
119       hTextPos = (orientationIsLeftToRight) ? RIGHT : LEFT;
120       break;
121     }
122
123     return layoutCompoundLabel(fm,
124       text,
125       icon,
126       verticalAlignment,
127       hAlign,
128       verticalTextPosition,
129       hTextPos,
130       viewR,
131       iconR,
132       textR,
133       textIconGap);
134   }
135
136   /**
137    * Compute and return the location of the icons origin, the
138    * location of origin of the text baseline, and a possibly clipped
139    * version of the compound labels string.  Locations are computed
140    * relative to the viewR rectangle.
141    * This layoutCompoundLabel() does not know how to handle LEADING/TRAILING
142    * values in horizontalTextPosition (they will default to RIGHT) and in
143    * horizontalAlignment (they will default to CENTER).
144    * Use the other version of layoutCompoundLabel() instead.
145    */
146   public static String layoutCompoundLabel(
147     FontMetrics fm,
148     String[] text,
149     Icon icon,
150     int verticalAlignment,
151     int horizontalAlignment,
152     int verticalTextPosition,
153     int horizontalTextPosition,
154     Rectangle viewR,
155     Rectangle iconR,
156     Rectangle textR,
157     int textIconGap) {
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 =
176       (text == null) || (text.length == 0) || (text.length == 1 && ((text[0] == null) || "".equals(text[0])));
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 {
244       // (verticalTextPosition == BOTTOM)
245       if (horizontalTextPosition != CENTER) {
246         textR.y = iconR.height - textR.height;
247       }
248       else {
249         textR.y = (iconR.height + gap);
250       }
251     }
252
253     if (horizontalTextPosition == LEFT) {
254       textR.x = -(textR.width + gap);
255     }
256     else if (horizontalTextPosition == CENTER) {
257       textR.x = (iconR.width / 2) - (textR.width / 2);
258     }
259     else {
260       // (horizontalTextPosition == RIGHT)
261       textR.x = (iconR.width + gap);
262     }
263
264     /* labelR is the rectangle that contains iconR and textR.
265      * Move it to its proper position given the labelAlignment
266      * properties.
267      *
268      * To avoid actually allocating a Rectangle, Rectangle.union
269      * has been inlined below.
270      */
271     int labelR_x = Math.min(iconR.x, textR.x);
272     int labelR_width = Math.max(iconR.x + iconR.width, textR.x + textR.width) - labelR_x;
273     int labelR_y = Math.min(iconR.y, textR.y);
274     int labelR_height = Math.max(iconR.y + iconR.height, 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 {
285       // (verticalAlignment == BOTTOM)
286       dy = (viewR.y + viewR.height) - (labelR_y + labelR_height);
287     }
288
289     if (horizontalAlignment == LEFT) {
290       dx = viewR.x - labelR_x;
291     }
292     else if (horizontalAlignment == RIGHT) {
293       dx = (viewR.x + viewR.width) - (labelR_x + labelR_width);
294     }
295     else {
296       // (horizontalAlignment == CENTER)
297       dx = (viewR.x + (viewR.width / 2)) -
298         (labelR_x + (labelR_width / 2));
299     }
300
301     /* Translate textR and glypyR by dx,dy.
302      */
303
304     textR.x += dx;
305     textR.y += dy;
306
307     iconR.x += dx;
308     iconR.y += dy;
309
310     return rettext;
311   }
312
313   protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY) {
314     int accChar = l.getDisplayedMnemonic();
315     g.setColor(l.getForeground());
316     drawString(g, s, accChar, textX, textY);
317   }
318
319   protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY) {
320     int accChar = l.getDisplayedMnemonic();
321     g.setColor(l.getBackground());
322     drawString(g, s, accChar, textX, textY);
323   }
324
325   protected void drawString(Graphics g, String s, int accChar, int textX, int textY) {
326     UIUtil.applyRenderingHints(g);
327     if (s.indexOf('\n') == -1)
328       BasicGraphicsUtils.drawString(g, s, accChar, textX, textY);
329     else {
330       String[] strs = splitStringByLines(s);
331       int height = g.getFontMetrics().getHeight();
332       // Only the first line can have the accel char
333       BasicGraphicsUtils.drawString(g, strs[0], accChar, textX, textY);
334       for (int i = 1; i < strs.length; i++) {
335         g.drawString(strs[i], textX, textY + (height * i));
336       }
337     }
338   }
339
340   public static Dimension computeMultiLineDimension(FontMetrics fm, String[] strs) {
341     int width = 0;
342     for (int i = 0; i < strs.length; i++) {
343       width = Math.max(width, SwingUtilities.computeStringWidth(fm, strs[i]));
344     }
345     return new Dimension(width, fm.getHeight() * strs.length);
346   }
347
348   public String[] splitStringByLines(String str) {
349     if (str == null) return ArrayUtil.EMPTY_STRING_ARRAY;
350     if (str.equals(myString)) {
351       return myLines;
352     }
353     myString = convertTabs(str, 2);
354
355     ArrayList list = new ArrayList();
356     StringTokenizer st = new StringTokenizer(str, "\n\r");
357     while (st.hasMoreTokens()) {
358       list.add(st.nextToken());
359     }
360
361     myLines = (String[])ArrayUtil.toStringArray(list);
362     return myLines;
363   }
364
365   public static String convertTabs(String text, final int tabLength) {
366     StringBuffer buf = new StringBuffer(text.length());
367     for (int idx = 0; idx < text.length(); idx++) {
368       char ch = text.charAt(idx);
369       if (ch == '\t') {
370         for (int i = 0; i < tabLength; i++) buf.append(' ');
371       }
372       else {
373         buf.append(ch);
374       }
375     }
376     return buf.toString();
377   }
378 }