1. /*
  2. * @(#)BasicLabelUI.java 1.69 00/02/02
  3. *
  4. * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package javax.swing.plaf.basic;
  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.69 02/02/00
  33. * @author Hans Muller
  34. */
  35. public class BasicLabelUI extends LabelUI implements PropertyChangeListener
  36. {
  37. protected static BasicLabelUI labelUI = new BasicLabelUI();
  38. /**
  39. * Forwards the call to SwingUtilities.layoutCompoundLabel().
  40. * This method is here so that a subclass could do Label specific
  41. * layout and to shorten the method name a little.
  42. *
  43. * @see SwingUtilities#layoutCompoundLabel
  44. */
  45. protected String layoutCL(
  46. JLabel label,
  47. FontMetrics fontMetrics,
  48. String text,
  49. Icon icon,
  50. Rectangle viewR,
  51. Rectangle iconR,
  52. Rectangle textR)
  53. {
  54. return SwingUtilities.layoutCompoundLabel(
  55. (JComponent) label,
  56. fontMetrics,
  57. text,
  58. icon,
  59. label.getVerticalAlignment(),
  60. label.getHorizontalAlignment(),
  61. label.getVerticalTextPosition(),
  62. label.getHorizontalTextPosition(),
  63. viewR,
  64. iconR,
  65. textR,
  66. label.getIconTextGap());
  67. }
  68. /**
  69. * Paint clippedText at textX, textY with the labels foreground color.
  70. *
  71. * @see #paint
  72. * @see #paintDisabledText
  73. */
  74. protected void paintEnabledText(JLabel l, Graphics g, String s, int textX, int textY)
  75. {
  76. int accChar = l.getDisplayedMnemonic();
  77. g.setColor(l.getForeground());
  78. BasicGraphicsUtils.drawString(g, s, accChar, textX, textY);
  79. }
  80. /**
  81. * Paint clippedText at textX, textY with background.lighter() and then
  82. * shifted down and to the right by one pixel with background.darker().
  83. *
  84. * @see #paint
  85. * @see #paintEnabledText
  86. */
  87. protected void paintDisabledText(JLabel l, Graphics g, String s, int textX, int textY)
  88. {
  89. int accChar = l.getDisplayedMnemonic();
  90. Color background = l.getBackground();
  91. g.setColor(background.brighter());
  92. BasicGraphicsUtils.drawString(g, s, accChar, textX + 1, textY + 1);
  93. g.setColor(background.darker());
  94. BasicGraphicsUtils.drawString(g, s, accChar, 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. paintViewInsets = c.getInsets(paintViewInsets);
  125. paintViewR.x = paintViewInsets.left;
  126. paintViewR.y = paintViewInsets.top;
  127. paintViewR.width = c.getWidth() - (paintViewInsets.left + paintViewInsets.right);
  128. paintViewR.height = c.getHeight() - (paintViewInsets.top + paintViewInsets.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.getIcon();
  165. Insets insets = label.getInsets(viewInsets);
  166. Font font = label.getFont();
  167. int dx = insets.left + insets.right;
  168. int dy = insets.top + insets.bottom;
  169. if ((icon == null) &&
  170. ((text == null) ||
  171. ((text != null) && (font == null)))) {
  172. return new Dimension(dx, dy);
  173. }
  174. else if ((text == null) || ((icon != null) && (font == null))) {
  175. return new Dimension(icon.getIconWidth() + dx,
  176. icon.getIconHeight() + dy);
  177. }
  178. else {
  179. FontMetrics fm = label.getToolkit().getFontMetrics(font);
  180. iconR.x = iconR.y = iconR.width = iconR.height = 0;
  181. textR.x = textR.y = textR.width = textR.height = 0;
  182. viewR.x = dx;
  183. viewR.y = dy;
  184. viewR.width = viewR.height = Short.MAX_VALUE;
  185. layoutCL(label, fm, text, icon, viewR, iconR, textR);
  186. int x1 = Math.min(iconR.x, textR.x);
  187. int x2 = Math.max(iconR.x + iconR.width, textR.x + textR.width);
  188. int y1 = Math.min(iconR.y, textR.y);
  189. int y2 = Math.max(iconR.y + iconR.height, textR.y + textR.height);
  190. Dimension rv = new Dimension(x2 - x1, y2 - y1);
  191. rv.width += dx;
  192. rv.height += dy;
  193. return rv;
  194. }
  195. }
  196. /**
  197. * @return getPreferredSize(c)
  198. */
  199. public Dimension getMinimumSize(JComponent c) {
  200. Dimension d = getPreferredSize(c);
  201. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  202. if (v != null) {
  203. d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
  204. }
  205. return d;
  206. }
  207. /**
  208. * @return getPreferredSize(c)
  209. */
  210. public Dimension getMaximumSize(JComponent c) {
  211. Dimension d = getPreferredSize(c);
  212. View v = (View) c.getClientProperty(BasicHTML.propertyKey);
  213. if (v != null) {
  214. d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
  215. }
  216. return d;
  217. }
  218. public void installUI(JComponent c) {
  219. installDefaults((JLabel)c);
  220. installComponents((JLabel)c);
  221. installListeners((JLabel)c);
  222. installKeyboardActions((JLabel)c);
  223. }
  224. public void uninstallUI(JComponent c) {
  225. uninstallDefaults((JLabel)c);
  226. uninstallComponents((JLabel)c);
  227. uninstallListeners((JLabel)c);
  228. uninstallKeyboardActions((JLabel)c);
  229. }
  230. // below is my overly aggressive version of this (It subtlely breaks UI switching)
  231. // I don't have time to fix this properly now, but I'll come back to it for a future release
  232. /* private Color defaultForeground = null;
  233. private Color defaultBackground = null;
  234. private Font defaultFont = null;
  235. protected void installDefaults(JLabel c){
  236. if (defaultForeground == null) {
  237. defaultForeground = UIManager.getColor("Label.foreground");
  238. defaultBackground = UIManager.getColor("Label.background");
  239. defaultFont = UIManager.getFont("Label.font");
  240. }
  241. Color currentForeground = c.getForeground();
  242. Color currentBackground = c.getBackground();
  243. Font currentFont = c.getFont();
  244. if (currentForeground == null || currentForeground instanceof UIResource) {
  245. c.setForeground(defaultForeground);
  246. }
  247. if (currentBackground == null || currentBackground instanceof UIResource) {
  248. c.setBackground(defaultBackground);
  249. }
  250. if (currentFont == null || currentFont instanceof UIResource) {
  251. c.setFont(defaultFont);
  252. }
  253. }*/
  254. /* old version - simple, but slow... */
  255. protected void installDefaults(JLabel c){
  256. LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground", "Label.font");
  257. }
  258. protected void installListeners(JLabel c){
  259. c.addPropertyChangeListener(this);
  260. }
  261. protected void installComponents(JLabel c){
  262. BasicHTML.updateRenderer(c, c.getText());
  263. }
  264. protected void installKeyboardActions(JLabel l) {
  265. int dka = l.getDisplayedMnemonic();
  266. Component lf = l.getLabelFor();
  267. if ((dka != 0) && (lf != null)) {
  268. ActionMap map = SwingUtilities.getUIActionMap(l);
  269. if (map == null) {
  270. map = createActionMap();
  271. if (map != null) {
  272. SwingUtilities.replaceUIActionMap(l, map);
  273. UIManager.put("Label.actionMap", map);
  274. }
  275. }
  276. InputMap inputMap = SwingUtilities.getUIInputMap
  277. (l, JComponent.WHEN_IN_FOCUSED_WINDOW);
  278. if (inputMap == null) {
  279. inputMap = new ComponentInputMapUIResource(l);
  280. SwingUtilities.replaceUIInputMap(l,
  281. JComponent.WHEN_IN_FOCUSED_WINDOW, inputMap);
  282. }
  283. inputMap.clear();
  284. inputMap.put(KeyStroke.getKeyStroke(dka, ActionEvent.ALT_MASK,
  285. false), "press");
  286. }
  287. else {
  288. InputMap inputMap = SwingUtilities.getUIInputMap
  289. (l, JComponent.WHEN_IN_FOCUSED_WINDOW);
  290. if (inputMap != null) {
  291. inputMap.clear();
  292. }
  293. }
  294. }
  295. ActionMap createActionMap() {
  296. ActionMap map = new ActionMapUIResource();
  297. map.put("press", new PressAction());
  298. map.put("release", new ReleaseAction());
  299. return map;
  300. }
  301. protected void uninstallDefaults(JLabel c){
  302. }
  303. protected void uninstallListeners(JLabel c){
  304. c.removePropertyChangeListener(this);
  305. }
  306. protected void uninstallComponents(JLabel c){
  307. BasicHTML.updateRenderer(c, "");
  308. }
  309. protected void uninstallKeyboardActions(JLabel c) {
  310. SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
  311. SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_IN_FOCUSED_WINDOW,
  312. null);
  313. SwingUtilities.replaceUIActionMap(c, null);
  314. }
  315. public static ComponentUI createUI(JComponent c) {
  316. return labelUI;
  317. }
  318. public void propertyChange(PropertyChangeEvent e) {
  319. String name = e.getPropertyName();
  320. if (name.equals("text")) {
  321. // remove the old html view client property if one
  322. // existed, and install a new one if the text installed
  323. // into the JLabel is html source.
  324. JLabel lbl = ((JLabel) e.getSource());
  325. String text = lbl.getText();
  326. BasicHTML.updateRenderer(lbl, text);
  327. }
  328. else if (name.equals("labelFor") ||
  329. name.equals("displayedMnemonic")) {
  330. installKeyboardActions((JLabel) e.getSource());
  331. }
  332. }
  333. // When the accelerator is pressed, temporarily make the JLabel
  334. // focusTraversable by registering a WHEN_FOCUSED action for the
  335. // release of the accelerator. Then give it focus so it can
  336. // prevent unwanted keyTyped events from getting to other components.
  337. static class PressAction extends AbstractAction {
  338. PressAction() {
  339. }
  340. public void actionPerformed(ActionEvent e) {
  341. JLabel label = (JLabel)e.getSource();
  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 = new InputMapUIResource();
  347. SwingUtilities.replaceUIInputMap(label, JComponent.WHEN_FOCUSED, inputMap);
  348. }
  349. int dka = label.getDisplayedMnemonic();
  350. inputMap.put(KeyStroke.getKeyStroke(dka, ActionEvent.ALT_MASK, true), "release");
  351. // Need this if the accelerator is released before the ALT key
  352. inputMap.put(KeyStroke.getKeyStroke(0, ActionEvent.ALT_MASK, true), "release");
  353. Component owner = label.getLabelFor();
  354. label.requestFocus();
  355. }
  356. }
  357. }
  358. // On the release of the accelerator, remove the keyboard action
  359. // that allows the label to take focus and then give focus to the
  360. // labelFor component.
  361. static class ReleaseAction extends AbstractAction {
  362. ReleaseAction() {
  363. }
  364. public void actionPerformed(ActionEvent e) {
  365. JLabel label = (JLabel)e.getSource();
  366. Component labelFor = label.getLabelFor();
  367. if(labelFor != null && labelFor.isEnabled()) {
  368. InputMap inputMap = SwingUtilities.getUIInputMap(label, JComponent.WHEN_FOCUSED);
  369. if (inputMap != null) {
  370. // inputMap should never be null.
  371. inputMap.remove(KeyStroke.getKeyStroke (label.getDisplayedMnemonic(), ActionEvent.ALT_MASK, true));
  372. inputMap.remove(KeyStroke.getKeyStroke(0, ActionEvent.ALT_MASK, true));
  373. }
  374. label.getLabelFor().requestFocus();
  375. }
  376. }
  377. }
  378. }