1. /*
  2. * @(#)BasicLabelUI.java 1.66 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing.plaf.basic;
  8. import javax.swing.*;
  9. import javax.swing.plaf.*;
  10. import javax.swing.text.View;
  11. import java.awt.event.ActionEvent;
  12. import java.awt.event.ActionListener;
  13. import java.awt.Component;
  14. import java.awt.Container;
  15. import java.awt.Dimension;
  16. import java.awt.Rectangle;
  17. import java.awt.Insets;
  18. import java.awt.Color;
  19. import java.awt.Graphics;
  20. import java.awt.Font;
  21. import java.awt.FontMetrics;
  22. import java.beans.PropertyChangeEvent;
  23. import java.beans.PropertyChangeListener;
  24. /**
  25. * A Windows L&F implementation of LabelUI. This implementation
  26. * is completely static, i.e. there's only one UIView implementation
  27. * that's shared by all JLabel objects.
  28. *
  29. * @version 1.66 11/29/01
  30. * @author Hans Muller
  31. */
  32. public class BasicLabelUI extends LabelUI implements PropertyChangeListener
  33. {
  34. protected static BasicLabelUI labelUI = new BasicLabelUI();
  35. /**
  36. * Forwards the call to SwingUtilities.layoutCompoundLabel().
  37. * This method is here so that a subclass could do Label specific
  38. * layout and to shorten the method name a little.
  39. *
  40. * @see SwingUtilities#layoutCompoundLabel
  41. */
  42. protected String layoutCL(
  43. JLabel label,
  44. FontMetrics fontMetrics,
  45. String text,
  46. Icon icon,
  47. Rectangle viewR,
  48. Rectangle iconR,
  49. Rectangle textR)
  50. {
  51. return SwingUtilities.layoutCompoundLabel(
  52. (JComponent) label,
  53. fontMetrics,
  54. text,
  55. icon,
  56. label.getVerticalAlignment(),
  57. label.getHorizontalAlignment(),
  58. label.getVerticalTextPosition(),
  59. label.getHorizontalTextPosition(),
  60. viewR,
  61. iconR,
  62. textR,
  63. label.getIconTextGap());
  64. }
  65. /**
  66. * Paint clippedText at textX, textY with the labels foreground color.
  67. *
  68. * @see #paint
  69. * @see #paintDisabledText
  70. */
  71. protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)
  72. {
  73. int accChar = l.getDisplayedMnemonic();
  74. g.setColor(l.getForeground());
  75. BasicGraphicsUtils.drawString(g, s, accChar, textX, textY);
  76. }
  77. /**
  78. * Paint clippedText at textX, textY with background.lighter() and then
  79. * shifted down and to the right by one pixel with background.darker().
  80. *
  81. * @see #paint
  82. * @see #paintEnabledText
  83. */
  84. protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)
  85. {
  86. int accChar = l.getDisplayedMnemonic();
  87. Color background = l.getBackground();
  88. g.setColor(background.brighter());
  89. BasicGraphicsUtils.drawString(g, s, accChar, textX + 1, textY + 1);
  90. g.setColor(background.darker());
  91. BasicGraphicsUtils.drawString(g, s, accChar, textX, textY);
  92. }
  93. /* These rectangles/insets are allocated once for this shared LabelUI
  94. * implementation. Re-using rectangles rather than allocating
  95. * them in each paint call halved the time it took paint to run.
  96. */
  97. private static Rectangle paintIconR = new Rectangle();
  98. private static Rectangle paintTextR = new Rectangle();
  99. private static Rectangle paintViewR = new Rectangle();
  100. private static Insets paintViewInsets = new Insets(0, 0, 0, 0);
  101. /**
  102. * Paint the label text in the foreground color, if the label
  103. * is opaque then paint the entire background with the background
  104. * color. The Label text is drawn by paintEnabledText() or
  105. * paintDisabledText(). The locations of the label parts are computed
  106. * by layoutCL.
  107. *
  108. * @see #paintEnabledText
  109. * @see #paintDisabledText
  110. * @see #layoutCL
  111. */
  112. public void paint(Graphics g, JComponent c)
  113. {
  114. JLabel label = (JLabel)c;
  115. String text = label.getText();
  116. Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();
  117. if ((icon == null) && (text == null)) {
  118. return;
  119. }
  120. FontMetrics fm = g.getFontMetrics();
  121. paintViewInsets = c.getInsets(paintViewInsets);
  122. paintViewR.x = paintViewInsets.left;
  123. paintViewR.y = paintViewInsets.top;
  124. paintViewR.width = c.getWidth() - (paintViewInsets.left + paintViewInsets.right);
  125. paintViewR.height = c.getHeight() - (paintViewInsets.top + paintViewInsets.bottom);
  126. paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
  127. paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
  128. String clippedText =
  129. layoutCL(label, fm, text, icon, paintViewR, paintIconR, paintTextR);
  130. if (icon != null) {
  131. icon.paintIcon(c, g, paintIconR.x, paintIconR.y);
  132. }
  133. if (text != null) {
  134. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  135. if (v != null) {
  136. v.paint(g, paintTextR);
  137. } else {
  138. int textX = paintTextR.x;
  139. int textY = paintTextR.y + fm.getAscent();
  140. if (label.isEnabled()) {
  141. paintEnabledText(label, g, clippedText, textX, textY);
  142. }
  143. else {
  144. paintDisabledText(label, g, clippedText, textX, textY);
  145. }
  146. }
  147. }
  148. }
  149. /* These rectangles/insets are allocated once for this shared LabelUI
  150. * implementation. Re-using rectangles rather than allocating
  151. * them in each getPreferredSize call sped up the method substantially.
  152. */
  153. private static Rectangle iconR = new Rectangle();
  154. private static Rectangle textR = new Rectangle();
  155. private static Rectangle viewR = new Rectangle();
  156. private static Insets viewInsets = new Insets(0, 0, 0, 0);
  157. public Dimension getPreferredSize(JComponent c)
  158. {
  159. JLabel label = (JLabel)c;
  160. String text = label.getText();
  161. Icon icon = label.getIcon();
  162. Insets insets = label.getInsets(viewInsets);
  163. Font font = label.getFont();
  164. int dx = insets.left + insets.right;
  165. int dy = insets.top + insets.bottom;
  166. if ((icon == null) &&
  167. ((text == null) ||
  168. ((text != null) && (font == null)))) {
  169. return new Dimension(dx, dy);
  170. }
  171. else if ((text == null) || ((icon != null) && (font == null))) {
  172. return new Dimension(icon.getIconWidth() + dx,
  173. icon.getIconHeight() + dy);
  174. }
  175. else {
  176. FontMetrics fm = label.getToolkit().getFontMetrics(font);
  177. iconR.x = iconR.y = iconR.width = iconR.height = 0;
  178. textR.x = textR.y = textR.width = textR.height = 0;
  179. viewR.x = dx;
  180. viewR.y = dy;
  181. viewR.width = viewR.height = Short.MAX_VALUE;
  182. layoutCL(label, fm, text, icon, viewR, iconR, textR);
  183. int x1 = Math.min(iconR.x, textR.x);
  184. int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
  185. int y1 = Math.min(iconR.y, textR.y);
  186. int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
  187. Dimension rv = new Dimension(x2 - x1, y2 - y1);
  188. rv.width += dx;
  189. rv.height += dy;
  190. return rv;
  191. }
  192. }
  193. /**
  194. * @return getPreferredSize(c)
  195. */
  196. public Dimension getMinimumSize(JComponent c) {
  197. Dimension d = getPreferredSize(c);
  198. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  199. if (v != null) {
  200. d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
  201. }
  202. return d;
  203. }
  204. /**
  205. * @return getPreferredSize(c)
  206. */
  207. public Dimension getMaximumSize(JComponent c) {
  208. Dimension d = getPreferredSize(c);
  209. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  210. if (v != null) {
  211. d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
  212. }
  213. return d;
  214. }
  215. public void installUI(JComponent c) {
  216. installDefaults((JLabel)c);
  217. installComponents((JLabel)c);
  218. installListeners((JLabel)c);
  219. installKeyboardActions((JLabel)c);
  220. }
  221. public void uninstallUI(JComponent c) {
  222. uninstallDefaults((JLabel)c);
  223. uninstallComponents((JLabel)c);
  224. uninstallListeners((JLabel)c);
  225. uninstallKeyboardActions((JLabel)c);
  226. }
  227. // below is my overly aggressive version of this (It subtlely breaks UI switching)
  228. // I don't have time to fix this properly now, but I'll come back to it for a future release
  229. /* private Color defaultForeground = null;
  230. private Color defaultBackground = null;
  231. private Font defaultFont = null;
  232. protected void installDefaults(JLabel c){
  233. if (defaultForeground == null) {
  234. defaultForeground = UIManager.getColor("Label.foreground");
  235. defaultBackground = UIManager.getColor("Label.background");
  236. defaultFont = UIManager.getFont("Label.font");
  237. }
  238. Color currentForeground = c.getForeground();
  239. Color currentBackground = c.getBackground();
  240. Font currentFont = c.getFont();
  241. if (currentForeground == null || currentForeground instanceof UIResource) {
  242. c.setForeground(defaultForeground);
  243. }
  244. if (currentBackground == null || currentBackground instanceof UIResource) {
  245. c.setBackground(defaultBackground);
  246. }
  247. if (currentFont == null || currentFont instanceof UIResource) {
  248. c.setFont(defaultFont);
  249. }
  250. }*/
  251. /* old version - simple, but slow... */
  252. protected void installDefaults(JLabel c){
  253. LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground", "Label.font");
  254. }
  255. protected void installListeners(JLabel c){
  256. c.addPropertyChangeListener(this);
  257. }
  258. protected void installComponents(JLabel c){
  259. BasicHTML.updateRenderer(c, c.getText());
  260. }
  261. protected void installKeyboardActions(JLabel l) {
  262. int dka = l.getDisplayedMnemonic();
  263. Component lf = l.getLabelFor();
  264. l.resetKeyboardActions();
  265. if ((dka != 0) && (lf != null)) {
  266. l.registerKeyboardAction(
  267. new PressAction(l,lf),
  268. KeyStroke.getKeyStroke(dka,ActionEvent.ALT_MASK,false),
  269. JComponent.WHEN_IN_FOCUSED_WINDOW);
  270. }
  271. }
  272. protected void uninstallDefaults(JLabel c){
  273. }
  274. protected void uninstallListeners(JLabel c){
  275. c.removePropertyChangeListener(this);
  276. }
  277. protected void uninstallComponents(JLabel c){
  278. BasicHTML.updateRenderer(c, "");
  279. }
  280. protected void uninstallKeyboardActions(JLabel c) {
  281. c.resetKeyboardActions();
  282. }
  283. public static ComponentUI createUI(JComponent c) {
  284. return labelUI;
  285. }
  286. public void propertyChange(PropertyChangeEvent e) {
  287. String name = e.getPropertyName();
  288. if (e.getPropertyName().equals("labelFor") ||
  289. e.getPropertyName().equals("displayedMnemonic")) {
  290. installKeyboardActions((JLabel) e.getSource());
  291. } else if (name.equals("text")) {
  292. // remove the old html view client property if one
  293. // existed, and install a new one if the text installed
  294. // into the JLabel is html source.
  295. JLabel lbl = ((JLabel) e.getSource());
  296. String text = lbl.getText();
  297. BasicHTML.updateRenderer(lbl, text);
  298. }
  299. }
  300. // When the accelerator is pressed, temporarily make the JLabel
  301. // focusTraversable by registering a WHEN_FOCUSED action for the
  302. // release of the accelerator. Then give it focus so it can
  303. // prevent unwanted keyTyped events from getting to other components.
  304. static class PressAction implements ActionListener {
  305. JLabel owner;
  306. Component labelFor;
  307. PressAction(JLabel l, Component c) {
  308. owner = l;
  309. labelFor = c;
  310. }
  311. public void actionPerformed(ActionEvent e) {
  312. owner.registerKeyboardAction(
  313. new ReleaseAction(owner,labelFor),
  314. KeyStroke.getKeyStroke(owner.getDisplayedMnemonic(),
  315. ActionEvent.ALT_MASK,true),
  316. JComponent.WHEN_FOCUSED);
  317. // Need this if the accelerator is released before the ALT key
  318. //
  319. owner.registerKeyboardAction(
  320. new ReleaseAction(owner,labelFor),
  321. KeyStroke.getKeyStroke(0,ActionEvent.ALT_MASK,true),
  322. JComponent.WHEN_FOCUSED);
  323. owner.requestFocus();
  324. }
  325. }
  326. // On the release of the accelerator, remove the keyboard action
  327. // that allows the label to take focus and then give focus to the
  328. // labelFor component.
  329. static class ReleaseAction implements ActionListener {
  330. JLabel owner;
  331. Component labelFor;
  332. ReleaseAction(JLabel l, Component c) {
  333. owner = l;
  334. labelFor = c;
  335. }
  336. public void actionPerformed(ActionEvent e) {
  337. owner.unregisterKeyboardAction(
  338. KeyStroke.getKeyStroke(owner.getDisplayedMnemonic(),
  339. ActionEvent.ALT_MASK,true));
  340. owner.unregisterKeyboardAction(
  341. KeyStroke.getKeyStroke(0,ActionEvent.ALT_MASK,true));
  342. labelFor.requestFocus();
  343. }
  344. }
  345. }