1. /*
  2. * @(#)LookAndFeel.java 1.36 04/05/05
  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;
  8. import java.awt.Font;
  9. import java.awt.event.InputEvent;
  10. import java.awt.event.KeyEvent;
  11. import java.awt.Color;
  12. import java.awt.Component;
  13. import java.awt.SystemColor;
  14. import java.awt.Toolkit;
  15. import javax.swing.text.*;
  16. import javax.swing.border.*;
  17. import javax.swing.plaf.*;
  18. import java.net.URL;
  19. import java.io.*;
  20. import java.util.StringTokenizer;
  21. /**
  22. * Completely characterizes a look and feel from the point of view
  23. * of the pluggable look and feel components.
  24. *
  25. * @version 1.36 05/05/04
  26. * @author Tom Ball
  27. * @author Hans Muller
  28. */
  29. public abstract class LookAndFeel
  30. {
  31. /**
  32. * Convenience method for initializing a component's foreground
  33. * and background color properties with values from the current
  34. * defaults table. The properties are only set if the current
  35. * value is either null or a UIResource.
  36. *
  37. * @param c the target component for installing default color/font properties
  38. * @param defaultBgName the key for the default background
  39. * @param defaultFgName the key for the default foreground
  40. *
  41. * @see #installColorsAndFont
  42. * @see UIManager#getColor
  43. */
  44. public static void installColors(JComponent c,
  45. String defaultBgName,
  46. String defaultFgName)
  47. {
  48. Color bg = c.getBackground();
  49. if (bg == null || bg instanceof UIResource) {
  50. c.setBackground(UIManager.getColor(defaultBgName));
  51. }
  52. Color fg = c.getForeground();
  53. if (fg == null || fg instanceof UIResource) {
  54. c.setForeground(UIManager.getColor(defaultFgName));
  55. }
  56. }
  57. /**
  58. * Convenience method for initializing a components foreground
  59. * background and font properties with values from the current
  60. * defaults table. The properties are only set if the current
  61. * value is either null or a UIResource.
  62. *
  63. * @param c the target component for installing default color/font properties
  64. * @param defaultBgName the key for the default background
  65. * @param defaultFgName the key for the default foreground
  66. * @param defaultFontName the key for the default font
  67. *
  68. * @see #installColors
  69. * @see UIManager#getColor
  70. * @see UIManager#getFont
  71. */
  72. public static void installColorsAndFont(JComponent c,
  73. String defaultBgName,
  74. String defaultFgName,
  75. String defaultFontName) {
  76. Font f = c.getFont();
  77. if (f == null || f instanceof UIResource) {
  78. c.setFont(UIManager.getFont(defaultFontName));
  79. }
  80. installColors(c, defaultBgName, defaultFgName);
  81. }
  82. /**
  83. * Convenience method for installing a component's default Border
  84. * object on the specified component if either the border is
  85. * currently null or already an instance of UIResource.
  86. * @param c the target component for installing default border
  87. * @param defaultBorderName the key specifying the default border
  88. */
  89. public static void installBorder(JComponent c, String defaultBorderName) {
  90. Border b = c.getBorder();
  91. if (b == null || b instanceof UIResource) {
  92. c.setBorder(UIManager.getBorder(defaultBorderName));
  93. }
  94. }
  95. /**
  96. * Convenience method for un-installing a component's default
  97. * border on the specified component if the border is
  98. * currently an instance of UIResource.
  99. * @param c the target component for uninstalling default border
  100. */
  101. public static void uninstallBorder(JComponent c) {
  102. if (c.getBorder() instanceof UIResource) {
  103. c.setBorder(null);
  104. }
  105. }
  106. /**
  107. * Convenience method for installing a property with the specified name
  108. * and value on a component if that property has not already been set
  109. * by the client program. This method is intended to be used by
  110. * UI delegate instances that need to specify a default value for a
  111. * property of primitive type (boolean, int, ..), but do not wish
  112. * to override a value set by the client. Since primitive property
  113. * values cannot be wrapped with the UIResource marker, this method
  114. * uses private state to determine whether the property has been set
  115. * by the client.
  116. * @throws IllegalArgumentException if the specified property is not
  117. * one which can be set using this method
  118. * @throws ClassCastException may be thrown if the property value
  119. * specified does not match the property's type
  120. * @throws NullPointerException may be thrown if c or propertyValue is null
  121. * @param c the target component for installing the property
  122. * @param propertyName String containing the name of the property to be set
  123. * @param propertyValue Object containing the value of the property
  124. */
  125. public static void installProperty(JComponent c,
  126. String propertyName, Object propertyValue) {
  127. c.setUIProperty(propertyName, propertyValue);
  128. }
  129. /**
  130. * Convenience method for building lists of KeyBindings.
  131. * <p>
  132. * Return an array of KeyBindings, one for each KeyStroke,Action pair
  133. * in <b>keyBindingList</b>. A KeyStroke can either be a string in
  134. * the format specified by the <code>KeyStroke.getKeyStroke</code>
  135. * method or a KeyStroke object.
  136. * <p>
  137. * Actions are strings. Here's an example:
  138. * <pre>
  139. * JTextComponent.KeyBinding[] multilineBindings = makeKeyBindings( new Object[] {
  140. * "UP", DefaultEditorKit.upAction,
  141. * "DOWN", DefaultEditorKit.downAction,
  142. * "PAGE_UP", DefaultEditorKit.pageUpAction,
  143. * "PAGE_DOWN", DefaultEditorKit.pageDownAction,
  144. * "ENTER", DefaultEditorKit.insertBreakAction,
  145. * "TAB", DefaultEditorKit.insertTabAction
  146. * });
  147. * </pre>
  148. *
  149. * @param keyBindingList an array of KeyStroke,Action pairs
  150. * @return an array of KeyBindings
  151. */
  152. public static JTextComponent.KeyBinding[] makeKeyBindings(Object[] keyBindingList)
  153. {
  154. JTextComponent.KeyBinding[] rv = new JTextComponent.KeyBinding[keyBindingList.length / 2];
  155. for(int i = 0; i < keyBindingList.length; i += 2) {
  156. KeyStroke keystroke = (keyBindingList[i] instanceof KeyStroke)
  157. ? (KeyStroke)keyBindingList[i]
  158. : KeyStroke.getKeyStroke((String)keyBindingList[i]);
  159. String action = (String)keyBindingList[i+1];
  160. rv[i / 2] = new JTextComponent.KeyBinding(keystroke, action);
  161. }
  162. return rv;
  163. }
  164. /**
  165. * Creates a InputMap from <code>keys</code>. <code>keys</code>
  166. * describes the InputMap, with every even number item being a String
  167. * giving the KeyStroke as speced in
  168. * <code>KeyStroke.getKeyStroke(String)</code>
  169. * (or a KeyStroke), and every odd number item the Object
  170. * used to determine the associated Action in an ActionMap.
  171. *
  172. * @since 1.3
  173. */
  174. public static InputMap makeInputMap(Object[] keys) {
  175. InputMap retMap = new InputMapUIResource();
  176. loadKeyBindings(retMap, keys);
  177. return retMap;
  178. }
  179. /**
  180. * Creates a ComponentInputMap from <code>keys</code>. <code>keys</code>
  181. * describes the InputMap, with every even number item being a String
  182. * giving
  183. * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
  184. * (or a KeyStroke), and every odd number item the Object
  185. * used to determine the associated Action in an ActionMap.
  186. *
  187. * @since 1.3
  188. */
  189. public static ComponentInputMap makeComponentInputMap(JComponent c,
  190. Object[] keys) {
  191. ComponentInputMap retMap = new ComponentInputMapUIResource(c);
  192. loadKeyBindings(retMap, keys);
  193. return retMap;
  194. }
  195. /**
  196. * Loads the bindings in <code>keys</code> into <code>retMap</code>.
  197. * This does not remove any existing bindings in <code>retMap</code>.
  198. * <code>keys</code>
  199. * describes the InputMap, with every even number item being a String
  200. * giving
  201. * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
  202. * (or a KeyStroke), and every odd number item the Object
  203. * used to determine the associated Action in an ActionMap.
  204. *
  205. * @since 1.3
  206. */
  207. public static void loadKeyBindings(InputMap retMap, Object[] keys) {
  208. if (keys != null) {
  209. for (int counter = 0, maxCounter = keys.length;
  210. counter < maxCounter; counter++) {
  211. Object keyStrokeO = keys[counter++];
  212. KeyStroke ks = (keyStrokeO instanceof KeyStroke) ?
  213. (KeyStroke)keyStrokeO :
  214. KeyStroke.getKeyStroke((String)keyStrokeO);
  215. retMap.put(ks, keys[counter]);
  216. }
  217. }
  218. }
  219. /**
  220. * Utility method that creates a UIDefaults.LazyValue that creates
  221. * an ImageIcon UIResource for the specified <code>gifFile</code>
  222. * filename.
  223. */
  224. public static Object makeIcon(final Class<?> baseClass, final String gifFile) {
  225. return new UIDefaults.LazyValue() {
  226. public Object createValue(UIDefaults table) {
  227. /* Copy resource into a byte array. This is
  228. * necessary because several browsers consider
  229. * Class.getResource a security risk because it
  230. * can be used to load additional classes.
  231. * Class.getResourceAsStream just returns raw
  232. * bytes, which we can convert to an image.
  233. */
  234. byte[] buffer = (byte[])
  235. java.security.AccessController.doPrivileged(
  236. new java.security.PrivilegedAction() {
  237. public Object run() {
  238. try {
  239. InputStream resource =
  240. baseClass.getResourceAsStream(gifFile);
  241. if (resource == null) {
  242. return null;
  243. }
  244. BufferedInputStream in =
  245. new BufferedInputStream(resource);
  246. ByteArrayOutputStream out =
  247. new ByteArrayOutputStream(1024);
  248. byte[] buffer = new byte[1024];
  249. int n;
  250. while ((n = in.read(buffer)) > 0) {
  251. out.write(buffer, 0, n);
  252. }
  253. in.close();
  254. out.flush();
  255. return out.toByteArray();
  256. } catch (IOException ioe) {
  257. System.err.println(ioe.toString());
  258. }
  259. return null;
  260. }
  261. });
  262. if (buffer == null) {
  263. System.err.println(baseClass.getName() + "/" +
  264. gifFile + " not found.");
  265. return null;
  266. }
  267. if (buffer.length == 0) {
  268. System.err.println("warning: " + gifFile +
  269. " is zero-length");
  270. return null;
  271. }
  272. return new IconUIResource(new ImageIcon(buffer));
  273. }
  274. };
  275. }
  276. /**
  277. * Invoked when the user attempts an invalid operation,
  278. * such as pasting into an uneditable <code>JTextField</code>
  279. * that has focus. The default implementation beeps. Subclasses
  280. * that wish different behavior should override this and provide
  281. * the additional feedback.
  282. *
  283. * @param component the <code>Component</code> the error occurred in,
  284. * may be <code>null</code>
  285. * indicating the error condition is not directly
  286. * associated with a <code>Component</code>
  287. * @since 1.4
  288. */
  289. public void provideErrorFeedback(Component component) {
  290. Toolkit toolkit = null;
  291. if (component != null) {
  292. toolkit = component.getToolkit();
  293. } else {
  294. toolkit = Toolkit.getDefaultToolkit();
  295. }
  296. toolkit.beep();
  297. } // provideErrorFeedback()
  298. /**
  299. * Returns the value of the specified system desktop property by
  300. * invoking <code>Toolkit.getDefaultToolkit().getDesktopProperty()</code>.
  301. * If the current value of the specified property is null, the
  302. * fallbackValue is returned.
  303. * @param systemPropertyName the name of the system desktop property being queried
  304. * @param fallbackValue the object to be returned as the value if the system value is null
  305. * @return the current value of the desktop property
  306. *
  307. * @see java.awt.Toolkit#getDesktopProperty
  308. *
  309. */
  310. public static Object getDesktopPropertyValue(String systemPropertyName, Object fallbackValue) {
  311. Object value = Toolkit.getDefaultToolkit().getDesktopProperty(systemPropertyName);
  312. if (value == null) {
  313. return fallbackValue;
  314. } else if (value instanceof Color) {
  315. return new ColorUIResource((Color)value);
  316. } else if (value instanceof Font) {
  317. return new FontUIResource((Font)value);
  318. }
  319. return value;
  320. }
  321. /**
  322. * Returns an <code>Icon</code> with a disabled appearance.
  323. * This method is used to generate a disabled <code>Icon</code> when
  324. * one has not been specified. For example, if you create a
  325. * <code>JButton</code> and only specify an <code>Icon</code> via
  326. * <code>setIcon</code> this method will be called to generate the
  327. * disabled <code>Icon</code>. If null is passed as <code>icon</code>
  328. * this method returns null.
  329. * <p>
  330. * Some look and feels might not render the disabled Icon, in which
  331. * case they will ignore this.
  332. *
  333. * @param component JComponent that will display the Icon, may be null
  334. * @param icon Icon to generate disable icon from.
  335. * @return Disabled icon, or null if a suitable Icon can not be
  336. * generated.
  337. * @since 1.5
  338. */
  339. public Icon getDisabledIcon(JComponent component, Icon icon) {
  340. if (icon instanceof ImageIcon) {
  341. return new IconUIResource(new ImageIcon(GrayFilter.
  342. createDisabledImage(((ImageIcon)icon).getImage())));
  343. }
  344. return null;
  345. }
  346. /**
  347. * Returns an <code>Icon</code> for use by disabled
  348. * components that are also selected. This method is used to generate an
  349. * <code>Icon</code> for components that are in both the disabled and
  350. * selected states but do not have a specific <code>Icon</code> for this
  351. * state. For example, if you create a <code>JButton</code> and only
  352. * specify an <code>Icon</code> via <code>setIcon</code> this method
  353. * will be called to generate the disabled and selected
  354. * <code>Icon</code>. If null is passed as <code>icon</code> this method
  355. * returns null.
  356. * <p>
  357. * Some look and feels might not render the disabled and selected Icon,
  358. * in which case they will ignore this.
  359. *
  360. * @param component JComponent that will display the Icon, may be null
  361. * @param icon Icon to generate disabled and selected icon from.
  362. * @return Disabled and Selected icon, or null if a suitable Icon can not
  363. * be generated.
  364. * @since 1.5
  365. */
  366. public Icon getDisabledSelectedIcon(JComponent component, Icon icon) {
  367. return getDisabledIcon(component, icon);
  368. }
  369. /**
  370. * Return a short string that identifies this look and feel, e.g.
  371. * "CDE/Motif". This string should be appropriate for a menu item.
  372. * Distinct look and feels should have different names, e.g.
  373. * a subclass of MotifLookAndFeel that changes the way a few components
  374. * are rendered should be called "CDE/Motif My Way"; something
  375. * that would be useful to a user trying to select a L&F from a list
  376. * of names.
  377. */
  378. public abstract String getName();
  379. /**
  380. * Return a string that identifies this look and feel. This string
  381. * will be used by applications/services that want to recognize
  382. * well known look and feel implementations. Presently
  383. * the well known names are "Motif", "Windows", "Mac", "Metal". Note
  384. * that a LookAndFeel derived from a well known superclass
  385. * that doesn't make any fundamental changes to the look or feel
  386. * shouldn't override this method.
  387. */
  388. public abstract String getID();
  389. /**
  390. * Return a one line description of this look and feel implementation,
  391. * e.g. "The CDE/Motif Look and Feel". This string is intended for
  392. * the user, e.g. in the title of a window or in a ToolTip message.
  393. */
  394. public abstract String getDescription();
  395. /**
  396. * Returns true if the <code>LookAndFeel</code> returned
  397. * <code>RootPaneUI</code> instances support providing Window decorations
  398. * in a <code>JRootPane</code>.
  399. * <p>
  400. * The default implementation returns false, subclasses that support
  401. * Window decorations should override this and return true.
  402. *
  403. * @return True if the RootPaneUI instances created support client side
  404. * decorations
  405. * @see JDialog#setDefaultLookAndFeelDecorated
  406. * @see JFrame#setDefaultLookAndFeelDecorated
  407. * @see JRootPane#setWindowDecorationStyle
  408. * @since 1.4
  409. */
  410. public boolean getSupportsWindowDecorations() {
  411. return false;
  412. }
  413. /**
  414. * If the underlying platform has a "native" look and feel, and this
  415. * is an implementation of it, return true. For example a CDE/Motif
  416. * look and implementation would return true when the underlying
  417. * platform was Solaris.
  418. */
  419. public abstract boolean isNativeLookAndFeel();
  420. /**
  421. * Return true if the underlying platform supports and or permits
  422. * this look and feel. This method returns false if the look
  423. * and feel depends on special resources or legal agreements that
  424. * aren't defined for the current platform.
  425. *
  426. * @see UIManager#setLookAndFeel
  427. */
  428. public abstract boolean isSupportedLookAndFeel();
  429. /**
  430. * UIManager.setLookAndFeel calls this method before the first
  431. * call (and typically the only call) to getDefaults(). Subclasses
  432. * should do any one-time setup they need here, rather than
  433. * in a static initializer, because look and feel class objects
  434. * may be loaded just to discover that isSupportedLookAndFeel()
  435. * returns false.
  436. *
  437. * @see #uninitialize
  438. * @see UIManager#setLookAndFeel
  439. */
  440. public void initialize() {
  441. }
  442. /**
  443. * UIManager.setLookAndFeel calls this method just before we're
  444. * replaced by a new default look and feel. Subclasses may
  445. * choose to free up some resources here.
  446. *
  447. * @see #initialize
  448. */
  449. public void uninitialize() {
  450. }
  451. /**
  452. * This method is called once by UIManager.setLookAndFeel to create
  453. * the look and feel specific defaults table. Other applications,
  454. * for example an application builder, may also call this method.
  455. *
  456. * @see #initialize
  457. * @see #uninitialize
  458. * @see UIManager#setLookAndFeel
  459. */
  460. public UIDefaults getDefaults() {
  461. return null;
  462. }
  463. /**
  464. * Returns a string that displays and identifies this
  465. * object's properties.
  466. *
  467. * @return a String representation of this object
  468. */
  469. public String toString() {
  470. return "[" + getDescription() + " - " + getClass().getName() + "]";
  471. }
  472. }