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