1 /*
2  * @(#)QuaquaLabelUI.java  1.6  2007-11-18
3  *
4  * Copyright (c) 2005-2007 Werner Randelshofer
5  * Staldenmattweg 2, Immensee, CH-6405, Switzerland.
6  * All rights reserved.
7  *
8  * The copyright of this software is owned by Werner Randelshofer.
9  * You may not use, copy or modify this software, except in
10  * accordance with the license agreement you entered into with
11  * Werner Randelshofer. For details see accompanying license terms.
12  */
13 package ch.randelshofer.quaqua;
14 
15 import ch.randelshofer.quaqua.util.*;
16 import ch.randelshofer.quaqua.border.BackgroundBorder;
17 import ch.randelshofer.quaqua.color.InactivatableColorUIResource;
18 import ch.randelshofer.quaqua.util.Debug;
19 import ch.randelshofer.quaqua.color.PaintableColor;
20 import java.awt.*;
21 import java.awt.event.*;
22 import java.beans.*;
23 
24 import javax.swing.*;
25 import javax.swing.border.*;
26 import javax.swing.plaf.*;
27 import javax.swing.plaf.basic.*;
28 import javax.swing.text.*;
29 
30 /**
31  * QuaquaLabelUI.
32  *
33  * @author  Werner Randelshofer
34  * @version 1.6.1 2009-02-01 Added support for property "Quaqua.Label.style"=
35  * "row", "rowSelected", "category" and "categorySelected" to support side bar JTrees.
36  * <br>1.6 2007-11-18 Added support for property "Quaqua.Label.style"=
37  * "emboss" and "shadow".
38  * <br>1.5.1 2007-01-15 Perceiced bounds.height must reflect font size
39  * even if the label is empty.
40  * <br>1.5 2006-02-18 Tweaked perceived text bounds. Draw disabled label
41  * with disabled text color. Draw background again if we are opaque.
42  * <br>1.4 2005-12-08 Support for background border added.
43  * <br>1.3 2005-07-17 Adapted to changes in interface VisuallyLayoutable.
44  * <br>1.2 2006-06-20 Paint text antialiased.
45  * <br>1.0  02 April 2005  Created.
46  */
47 public class QuaquaLabelUI extends BasicLabelUI implements VisuallyLayoutable {
48 
49     protected static QuaquaLabelUI labelUI = new QuaquaLabelUI();
50     /* These rectangles/insets are allocated once for this shared LabelUI
51      * implementation.  Re-using rectangles rather than allocating
52      * them in each getPreferredSize call sped up the method substantially.
53      */
54     private static Rectangle iconR = new Rectangle();
55     private static Rectangle textR = new Rectangle();
56     private static Rectangle viewR = new Rectangle();
57     private static Insets viewInsets = new Insets(0, 0, 0, 0);
58 
59     /**
60      * Preferred spacing between labels and other components.
61      * Pixels from colon and associated controls (RadioButton,
62      * CheckBox)
63      * /
64      * private final static Insets associatedRegularSpacing = new Insets(8,8,8,8);
65      * private final static Insets associatedSmallSpacing = new Insets(6,6,6,6);
66      * private final static Insets associatedMiniSpacing = new Insets(5,5,5,5);
67      */
createUI(JComponent c)68     public static ComponentUI createUI(JComponent c) {
69         return labelUI;
70     }
71 
installDefaults(JLabel b)72     protected void installDefaults(JLabel b) {
73         super.installDefaults(b);
74 
75         // load shared instance defaults
76         LookAndFeel.installBorder(b, "Label.border");
77 
78         // FIXME - Very, very dirty trick to achieve small labels on sliders
79         //         This hack should be removed, when we implement a SliderUI
80         //         on our own.
81         if (b.getClass().getName().endsWith("LabelUIResource")) {
82             b.setFont(UIManager.getFont("Slider.labelFont"));
83         }
84     }
85 
paint(Graphics gr, JComponent c)86     public void paint(Graphics gr, JComponent c) {
87         Graphics2D g = (Graphics2D) gr;
88         Object oldHints = QuaquaUtilities.beginGraphics(g);
89 
90         // Paint background again so that the texture paint is drawn
91         if (c.isOpaque()) {
92             g.setPaint(PaintableColor.getPaint(c.getBackground(), c));
93             g.fillRect(0, 0, c.getWidth(), c.getHeight());
94         }
95 
96         // Paint background border
97         Border b = c.getBorder();
98         if (b != null && b instanceof BackgroundBorder) {
99             ((BackgroundBorder) b).getBackgroundBorder().paintBorder(c, g, 0, 0, c.getWidth(), c.getHeight());
100         }
101 
102         super.paint(g, c);
103         QuaquaUtilities.endGraphics(g, oldHints);
104         Debug.paint(g, c, this);
105     }
106 
107     /**
108      * Paint label with disabled text color.
109      *
110      * @see #paint
111      * @see #paintEnabledText
112      */
paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)113     protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY) {
114         // Make sure we render with the right drawing properties and make sure
115         // we can edit them by client properties
116         Font font = l.getFont();
117         Color foreground = UIManager.getColor("Label.disabledForeground");
118         int accChar = -1; //l.getDisplayedMnemonicIndex();
119 
120         String style = (String) l.getClientProperty("Quaqua.Label.style");
121         if (style != null) {
122             boolean selected = style.endsWith("Selected");
123 
124             if ((style.equals("category") || style.equals("categorySelected"))
125                     && UIManager.getFont("Tree.category.font.sideBar") != null
126                     && UIManager.getColor("Tree.category.foreground.sideBar") != null) {
127 
128                 s = s.toUpperCase();
129                 font = UIManager.getFont("Tree.category.font.sideBar");
130                 style = (selected) ? "shadow" : "emboss";
131             } else if ((style.equals("row") || style.equals("rowSelected"))
132                     && UIManager.getFont("Tree.font.sideBar") != null
133                     && UIManager.getFont("Tree.font.selected.sideBar") != null
134                 && UIManager.getColor("Tree.foreground.sideBar") != null) {
135 
136                 font = selected ? UIManager
137                         .getFont("Tree.font.selected.sideBar") : UIManager
138                         .getFont("Tree.font.sideBar");
139                 style = selected ? "shadow" : null;
140             }
141 
142             if (style != null && style.equals("emboss")
143                     && UIManager.getColor("Label.embossForeground") != null) {
144                 g.setFont(font);
145                 g.setColor(UIManager.getColor("Label.embossForeground"));
146                 QuaquaUtilities.drawString(g, s, accChar, textX, textY + 1);
147             } else if (style != null && style.equals("shadow")
148                     && UIManager.getColor("Label.shadowForeground") != null) {
149                 g.setFont(font);
150                 g.setColor(UIManager.getColor("Label.shadowForeground"));
151                 QuaquaUtilities.drawString(g, s, accChar, textX, textY + 1);
152             }
153         }
154 
155         g.setFont(font);
156         g.setColor(foreground);
157         QuaquaUtilities.drawString(g, s, accChar,
158                 textX, textY);
159     }
160 
paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)161     protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY) {
162         int mnemIndex = l.getDisplayedMnemonicIndex();
163 
164         // Make sure we render with the right drawing properties and make sure
165         // we can edit them by client properties
166         Font font = l.getFont();
167         Color foreground = l.getForeground();
168 
169         String style = (String) l.getClientProperty("Quaqua.Label.style");
170         if (style != null) {
171             boolean selected = style.endsWith("Selected");
172 
173             if ((style.equals("category") || style.equals("categorySelected"))
174                     && UIManager.getFont("Tree.category.font.sideBar") != null
175                     && UIManager.getColor("Tree.category.foreground.sideBar") != null) {
176 
177                 s = s.toUpperCase();
178                 font = UIManager.getFont("Tree.category.font.sideBar");
179                 foreground = UIManager
180                         .getColor("Tree.category.foreground.sideBar");
181                 if (foreground instanceof InactivatableColorUIResource)
182                     ((InactivatableColorUIResource) foreground)
183                             .setActive(selected);
184                 style = (selected) ? "shadow" : "emboss";
185 
186             } else if ((style.equals("row") || style.equals("rowSelected"))
187                     && UIManager.getFont("Tree.font.sideBar") != null
188                     && UIManager.getColor("Tree.foreground.sideBar") != null) {
189 
190                 font = UIManager.getFont("Tree.font.sideBar");
191                 foreground = UIManager.getColor("Tree.foreground.sideBar");
192                 if (foreground instanceof InactivatableColorUIResource)
193                     ((InactivatableColorUIResource) foreground)
194                             .setActive(selected);
195                 style = (selected) ? "shadow" : null;
196             }
197 
198             if (style != null && style.equals("emboss")
199                     && UIManager.getColor("Label.embossForeground") != null) {
200                 g.setFont(font);
201                 g.setColor(UIManager.getColor("Label.embossForeground"));
202                 QuaquaUtilities.drawString(g, s, mnemIndex, textX, textY + 1);
203             } else if (style != null && style.equals("shadow")
204                     && UIManager.getColor("Label.shadowForeground") != null) {
205                 g.setFont(font);
206                 g.setColor(UIManager.getColor("Label.shadowForeground"));
207                 QuaquaUtilities.drawString(g, s, mnemIndex, textX, textY + 1);
208             }
209         }
210 
211         g.setFont(font);
212         g.setColor(foreground);
213         QuaquaUtilities.drawString(g, s, mnemIndex, textX, textY);
214     //SwingUtilities2.drawStringUnderlineCharAt(l, g, s, mnemIndex,
215     //                                             textX, textY);
216     }
217 
218     /**
219      * Forwards the call to SwingUtilities.layoutCompoundLabel().
220      * This method is here so that a subclass could do Label specific
221      * layout and to shorten the method name a little.
222      *
223      * @see SwingUtilities#layoutCompoundLabel
224      */
layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR)225     protected String layoutCL(
226             JLabel label,
227             FontMetrics fontMetrics,
228             String text,
229             Icon icon,
230             Rectangle viewR,
231             Rectangle iconR,
232             Rectangle textR) {
233         return SwingUtilities.layoutCompoundLabel(
234                 (JComponent) label,
235                 fontMetrics,
236                 text,
237                 icon,
238                 label.getVerticalAlignment(),
239                 label.getHorizontalAlignment(),
240                 label.getVerticalTextPosition(),
241                 label.getHorizontalTextPosition(),
242                 viewR,
243                 iconR,
244                 textR,
245                 label.getIconTextGap());
246     }
247 
getBaseline(JComponent c, int width, int height)248     public int getBaseline(JComponent c, int width, int height) {
249         Rectangle vb = getVisualBounds(c, VisuallyLayoutable.TEXT_BOUNDS, width, height);
250         return (vb == null) ? -1 : vb.y + vb.height;
251     }
252 
getVisualBounds(JComponent c, int type, int width, int height)253     public Rectangle getVisualBounds(JComponent c, int type, int width, int height) {
254         Rectangle rect = new Rectangle(0, 0, width, height);
255         if (type == VisuallyLayoutable.CLIP_BOUNDS) {
256             return rect;
257         }
258 
259         JLabel b = (JLabel) c;
260         String text = b.getText();
261         boolean isEmpty = (text == null || text.length() == 0);
262         if (isEmpty) {
263             text = " ";
264         }
265         Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();
266 
267         Font f = c.getFont();
268         FontMetrics fm = c.getFontMetrics(f);
269         Insets insets = c.getInsets(viewInsets);
270 
271         viewR.x = insets.left;
272         viewR.y = insets.top;
273         viewR.width = width - (insets.left + insets.right);
274         viewR.height = height - (insets.top + insets.bottom);
275 
276         iconR.x = iconR.y = iconR.width = iconR.height = 0;
277         textR.x = textR.y = textR.width = textR.height = 0;
278 
279         String clippedText =
280                 layoutCL(b, fm, text, icon, viewR, iconR, textR);
281 
282         Rectangle textBounds = Fonts.getPerceivedBounds(text, f, c);
283         if (isEmpty) {
284             textBounds.width = 0;
285         }
286         int ascent = fm.getAscent();
287         textR.x += textBounds.x;
288         textR.width = textBounds.width;
289         textR.y += ascent + textBounds.y;
290         textR.height -= fm.getHeight() - textBounds.height;
291 
292         // Determine rect rectangle
293         switch (type) {
294             case VisuallyLayoutable.COMPONENT_BOUNDS:
295                 if (icon != null) {
296                     rect = textR.union(iconR);
297                 } else {
298                     rect.setBounds(textR);
299                 }
300                 break;
301             case VisuallyLayoutable.TEXT_BOUNDS:
302                 if (text == null) {
303                     return rect;
304                 }
305                 rect.setBounds(textR);
306                 break;
307         }
308 
309         return rect;
310     }
311 
propertyChange(PropertyChangeEvent evt)312     public void propertyChange(PropertyChangeEvent evt) {
313         String name = evt.getPropertyName();
314 
315         if (name.equals("JComponent.sizeVariant")) {
316             QuaquaUtilities.applySizeVariant((JLabel) evt.getSource());
317         } else {
318             super.propertyChange(evt);
319         }
320     }
321 }
322