1. /*
  2. * @(#)BasicLabelUI.java 1.77 03/01/23
  3. *
  4. * Copyright 2003 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.77 01/23/03
  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 mnemIndex = l.getDisplayedMnemonicIndex();
  74. g.setColor(l.getForeground());
  75. BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex,
  76. textX, textY);
  77. }
  78. /**
  79. * Paint clippedText at textX, textY with background.lighter() and then
  80. * shifted down and to the right by one pixel with background.darker().
  81. *
  82. * @see #paint
  83. * @see #paintEnabledText
  84. */
  85. protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)
  86. {
  87. int accChar = l.getDisplayedMnemonicIndex();
  88. Color background = l.getBackground();
  89. g.setColor(background.brighter());
  90. BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, accChar,
  91. textX + 1, textY + 1);
  92. g.setColor(background.darker());
  93. BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, accChar,
  94. textX, textY);
  95. }
  96. /* These rectangles/insets are allocated once for this shared LabelUI
  97. * implementation. Re-using rectangles rather than allocating
  98. * them in each paint call halved the time it took paint to run.
  99. */
  100. private static Rectangle paintIconR = new Rectangle();
  101. private static Rectangle paintTextR = new Rectangle();
  102. private static Rectangle paintViewR = new Rectangle();
  103. private static Insets paintViewInsets = new Insets(0, 0, 0, 0);
  104. /**
  105. * Paint the label text in the foreground color, if the label
  106. * is opaque then paint the entire background with the background
  107. * color. The Label text is drawn by paintEnabledText() or
  108. * paintDisabledText(). The locations of the label parts are computed
  109. * by layoutCL.
  110. *
  111. * @see #paintEnabledText
  112. * @see #paintDisabledText
  113. * @see #layoutCL
  114. */
  115. public void paint(Graphics g, JComponent c)
  116. {
  117. JLabel label = (JLabel)c;
  118. String text = label.getText();
  119. Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();
  120. if ((icon == null) && (text == null)) {
  121. return;
  122. }
  123. FontMetrics fm = g.getFontMetrics();
  124. Insets insets = c.getInsets(paintViewInsets);
  125. paintViewR.x = insets.left;
  126. paintViewR.y = insets.top;
  127. paintViewR.width = c.getWidth() - (insets.left + insets.right);
  128. paintViewR.height = c.getHeight() - (insets.top + insets.bottom);
  129. paintIconR.x = paintIconR.y = paintIconR.width = paintIconR.height = 0;
  130. paintTextR.x = paintTextR.y = paintTextR.width = paintTextR.height = 0;
  131. String clippedText =
  132. layoutCL(label, fm, text, icon, paintViewR, paintIconR, paintTextR);
  133. if (icon != null) {
  134. icon.paintIcon(c, g, paintIconR.x, paintIconR.y);
  135. }
  136. if (text != null) {
  137. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  138. if (v != null) {
  139. v.paint(g, paintTextR);
  140. } else {
  141. int textX = paintTextR.x;
  142. int textY = paintTextR.y + fm.getAscent();
  143. if (label.isEnabled()) {
  144. paintEnabledText(label, g, clippedText, textX, textY);
  145. }
  146. else {
  147. paintDisabledText(label, g, clippedText, textX, textY);
  148. }
  149. }
  150. }
  151. }
  152. /* These rectangles/insets are allocated once for this shared LabelUI
  153. * implementation. Re-using rectangles rather than allocating
  154. * them in each getPreferredSize call sped up the method substantially.
  155. */
  156. private static Rectangle iconR = new Rectangle();
  157. private static Rectangle textR = new Rectangle();
  158. private static Rectangle viewR = new Rectangle();
  159. private static Insets viewInsets = new Insets(0, 0, 0, 0);
  160. public Dimension getPreferredSize(JComponent c)
  161. {
  162. JLabel label = (JLabel)c;
  163. String text = label.getText();
  164. Icon icon = (label.isEnabled()) ? label.getIcon() :
  165. label.getDisabledIcon();
  166. Insets insets = label.getInsets(viewInsets);
  167. Font font = label.getFont();
  168. int dx = insets.left + insets.right;
  169. int dy = insets.top + insets.bottom;
  170. if ((icon == null) &&
  171. ((text == null) ||
  172. ((text != null) && (font == null)))) {
  173. return new Dimension(dx, dy);
  174. }
  175. else if ((text == null) || ((icon != null) && (font == null))) {
  176. return new Dimension(icon.getIconWidth() + dx,
  177. icon.getIconHeight() + dy);
  178. }
  179. else {
  180. FontMetrics fm = label.getToolkit().getFontMetrics(font);
  181. iconR.x = iconR.y = iconR.width = iconR.height = 0;
  182. textR.x = textR.y = textR.width = textR.height = 0;
  183. viewR.x = dx;
  184. viewR.y = dy;
  185. viewR.width = viewR.height = Short.MAX_VALUE;
  186. layoutCL(label, fm, text, icon, viewR, iconR, textR);
  187. int x1 = Math.min(iconR.x, textR.x);
  188. int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
  189. int y1 = Math.min(iconR.y, textR.y);
  190. int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
  191. Dimension rv = new Dimension(x2 - x1, y2 - y1);
  192. rv.width += dx;
  193. rv.height += dy;
  194. return rv;
  195. }
  196. }
  197. /**
  198. * @return getPreferredSize(c)
  199. */
  200. public Dimension getMinimumSize(JComponent c) {
  201. Dimension d = getPreferredSize(c);
  202. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  203. if (v != null) {
  204. d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
  205. }
  206. return d;
  207. }
  208. /**
  209. * @return getPreferredSize(c)
  210. */
  211. public Dimension getMaximumSize(JComponent c) {
  212. Dimension d = getPreferredSize(c);
  213. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  214. if (v != null) {
  215. d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
  216. }
  217. return d;
  218. }
  219. public void installUI(JComponent c) {
  220. installDefaults((JLabel)c);
  221. installComponents((JLabel)c);
  222. installListeners((JLabel)c);
  223. installKeyboardActions((JLabel)c);
  224. }
  225. public void uninstallUI(JComponent c) {
  226. uninstallDefaults((JLabel)c);
  227. uninstallComponents((JLabel)c);
  228. uninstallListeners((JLabel)c);
  229. uninstallKeyboardActions((JLabel)c);
  230. }
  231. // below is my overly aggressive version of this (It subtlely breaks UI switching)
  232. // I don't have time to fix this properly now, but I'll come back to it for a future release
  233. /* private Color defaultForeground = null;
  234. private Color defaultBackground = null;
  235. private Font defaultFont = null;
  236. protected void installDefaults(JLabel c){
  237. if (defaultForeground == null) {
  238. defaultForeground = UIManager.getColor("Label.foreground");
  239. defaultBackground = UIManager.getColor("Label.background");
  240. defaultFont = UIManager.getFont("Label.font");
  241. }
  242. Color currentForeground = c.getForeground();
  243. Color currentBackground = c.getBackground();
  244. Font currentFont = c.getFont();
  245. if (currentForeground == null || currentForeground instanceof UIResource) {
  246. c.setForeground(defaultForeground);
  247. }
  248. if (currentBackground == null || currentBackground instanceof UIResource) {
  249. c.setBackground(defaultBackground);
  250. }
  251. if (currentFont == null || currentFont instanceof UIResource) {
  252. c.setFont(defaultFont);
  253. }
  254. }*/
  255. /* old version - simple, but slow... */
  256. protected void installDefaults(JLabel c){
  257. LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground", "Label.font");
  258. }
  259. protected void installListeners(JLabel c){
  260. c.addPropertyChangeListener(this);
  261. }
  262. protected void installComponents(JLabel c){
  263. BasicHTML.updateRenderer(c, c.getText());
  264. }
  265. protected void installKeyboardActions(JLabel l) {
  266. int dka = l.getDisplayedMnemonic();
  267. Component lf = l.getLabelFor();
  268. if ((dka != 0) && (lf != null)) {
  269. ActionMap map = SwingUtilities.getUIActionMap(l);
  270. if (map == null) {
  271. map = createActionMap();
  272. if (map != null) {
  273. SwingUtilities.replaceUIActionMap(l, map);
  274. UIManager.getLookAndFeelDefaults().put("Label.actionMap",
  275. map);
  276. }
  277. }
  278. InputMap inputMap = SwingUtilities.getUIInputMap
  279. (l, JComponent.WHEN_IN_FOCUSED_WINDOW);
  280. if (inputMap == null) {
  281. inputMap = new ComponentInputMapUIResource(l);
  282. SwingUtilities.replaceUIInputMap(l,
  283. JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap);
  284. }
  285. inputMap.clear();
  286. inputMap.put(KeyStroke.getKeyStroke(dka, ActionEvent.ALT_MASK,
  287. false), "press");
  288. }
  289. else {
  290. InputMap inputMap = SwingUtilities.getUIInputMap
  291. (l, JComponent.WHEN_IN_FOCUSED_WINDOW);
  292. if (inputMap != null) {
  293. inputMap.clear();
  294. }
  295. }
  296. }
  297. ActionMap createActionMap() {
  298. ActionMap map = new ActionMapUIResource();
  299. map.put("press", new PressAction());
  300. map.put("release", new ReleaseAction());
  301. return map;
  302. }
  303. protected void uninstallDefaults(JLabel c){
  304. }
  305. protected void uninstallListeners(JLabel c){
  306. c.removePropertyChangeListener(this);
  307. }
  308. protected void uninstallComponents(JLabel c){
  309. BasicHTML.updateRenderer(c, "");
  310. }
  311. protected void uninstallKeyboardActions(JLabel c) {
  312. SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
  313. SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_IN_FOCUSED_WINDOW,
  314. null);
  315. SwingUtilities.replaceUIActionMap(c, null);
  316. }
  317. public static ComponentUI createUI(JComponent c) {
  318. return labelUI;
  319. }
  320. public void propertyChange(PropertyChangeEvent e) {
  321. String name = e.getPropertyName();
  322. if (name.equals("text") || "font".equals(name) ||
  323. "foreground".equals(name)) {
  324. // remove the old html view client property if one
  325. // existed, and install a new one if the text installed
  326. // into the JLabel is html source.
  327. JLabel lbl = ((JLabel) e.getSource());
  328. String text = lbl.getText();
  329. BasicHTML.updateRenderer(lbl, text);
  330. }
  331. else if (name.equals("labelFor") ||
  332. name.equals("displayedMnemonic")) {
  333. installKeyboardActions((JLabel) e.getSource());
  334. }
  335. }
  336. // When the accelerator is pressed, temporarily make the JLabel
  337. // focusTraversable by registering a WHEN_FOCUSED action for the
  338. // release of the accelerator. Then give it focus so it can
  339. // prevent unwanted keyTyped events from getting to other components.
  340. static class PressAction extends AbstractAction {
  341. PressAction() {
  342. }
  343. public void actionPerformed(ActionEvent e) {
  344. JLabel label = (JLabel)e.getSource();
  345. Component labelFor = label.getLabelFor();
  346. if(labelFor != null && labelFor.isEnabled()) {
  347. InputMap inputMap = SwingUtilities.getUIInputMap(label, JComponent.WHEN_FOCUSED);
  348. if (inputMap == null) {
  349. inputMap = new InputMapUIResource();
  350. SwingUtilities.replaceUIInputMap(label, JComponent.WHEN_FOCUSED, inputMap);
  351. }
  352. int dka = label.getDisplayedMnemonic();
  353. inputMap.put(KeyStroke.getKeyStroke(dka, ActionEvent.ALT_MASK, true), "release");
  354. // Need this if the accelerator is released before the ALT key
  355. inputMap.put(KeyStroke.getKeyStroke(0, ActionEvent.ALT_MASK, true), "release");
  356. Component owner = label.getLabelFor();
  357. label.requestFocus();
  358. }
  359. }
  360. }
  361. // On the release of the accelerator, remove the keyboard action
  362. // that allows the label to take focus and then give focus to the
  363. // labelFor component.
  364. static class ReleaseAction extends AbstractAction {
  365. ReleaseAction() {
  366. }
  367. public void actionPerformed(ActionEvent e) {
  368. JLabel label = (JLabel)e.getSource();
  369. Component labelFor = label.getLabelFor();
  370. if(labelFor != null && labelFor.isEnabled()) {
  371. InputMap inputMap = SwingUtilities.getUIInputMap(label, JComponent.WHEN_FOCUSED);
  372. if (inputMap != null) {
  373. // inputMap should never be null.
  374. inputMap.remove(KeyStroke.getKeyStroke (label.getDisplayedMnemonic(), ActionEvent.ALT_MASK, true));
  375. inputMap.remove(KeyStroke.getKeyStroke(0, ActionEvent.ALT_MASK, true));
  376. }
  377. label.getLabelFor().requestFocus();
  378. }
  379. }
  380. }
  381. }