1. /*
  2. * @(#)LookAndFeel.java 1.30 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;
  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.30 01/23/03
  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 building lists of KeyBindings.
  108. * <p>
  109. * Return an array of KeyBindings, one for each KeyStroke,Action pair
  110. * in <b>keyBindingList</b>. A KeyStroke can either be a string in
  111. * the format specified by the <code>KeyStroke.getKeyStroke</code>
  112. * method or a KeyStroke object.
  113. * <p>
  114. * Actions are strings. Here's an example:
  115. * <pre>
  116. * JTextComponent.KeyBinding[] multilineBindings = makeKeyBindings( new Object[] {
  117. * "UP", DefaultEditorKit.upAction,
  118. * "DOWN", DefaultEditorKit.downAction,
  119. * "PAGE_UP", DefaultEditorKit.pageUpAction,
  120. * "PAGE_DOWN", DefaultEditorKit.pageDownAction,
  121. * "ENTER", DefaultEditorKit.insertBreakAction,
  122. * "TAB", DefaultEditorKit.insertTabAction
  123. * });
  124. * </pre>
  125. *
  126. * @param keyBindingList an array of KeyStroke,Action pairs
  127. * @return an array of KeyBindings
  128. */
  129. public static JTextComponent.KeyBinding[] makeKeyBindings(Object[] keyBindingList)
  130. {
  131. JTextComponent.KeyBinding[] rv = new JTextComponent.KeyBinding[keyBindingList.length / 2];
  132. for(int i = 0; i < keyBindingList.length; i += 2) {
  133. KeyStroke keystroke = (keyBindingList[i] instanceof KeyStroke)
  134. ? (KeyStroke)keyBindingList[i]
  135. : KeyStroke.getKeyStroke((String)keyBindingList[i]);
  136. String action = (String)keyBindingList[i+1];
  137. rv[i / 2] = new JTextComponent.KeyBinding(keystroke, action);
  138. }
  139. return rv;
  140. }
  141. /**
  142. * Creates a InputMap from <code>keys</code>. <code>keys</code>
  143. * describes the InputMap, with every even number item being a String
  144. * giving the KeyStroke as speced in
  145. * <code>KeyStroke.getKeyStroke(String)</code>
  146. * (or a KeyStroke), and every odd number item the Object
  147. * used to determine the associated Action in an ActionMap.
  148. *
  149. * @since 1.3
  150. */
  151. public static InputMap makeInputMap(Object[] keys) {
  152. InputMap retMap = new InputMapUIResource();
  153. loadKeyBindings(retMap, keys);
  154. return retMap;
  155. }
  156. /**
  157. * Creates a ComponentInputMap from <code>keys</code>. <code>keys</code>
  158. * describes the InputMap, with every even number item being a String
  159. * giving
  160. * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
  161. * (or a KeyStroke), and every odd number item the Object
  162. * used to determine the associated Action in an ActionMap.
  163. *
  164. * @since 1.3
  165. */
  166. public static ComponentInputMap makeComponentInputMap(JComponent c,
  167. Object[] keys) {
  168. ComponentInputMap retMap = new ComponentInputMapUIResource(c);
  169. loadKeyBindings(retMap, keys);
  170. return retMap;
  171. }
  172. /**
  173. * Loads the bindings in <code>keys</code> into <code>retMap</code>.
  174. * This does not remove any existing bindings in <code>retMap</code>.
  175. * <code>keys</code>
  176. * describes the InputMap, with every even number item being a String
  177. * giving
  178. * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
  179. * (or a KeyStroke), and every odd number item the Object
  180. * used to determine the associated Action in an ActionMap.
  181. *
  182. * @since 1.3
  183. */
  184. public static void loadKeyBindings(InputMap retMap, Object[] keys) {
  185. if (keys != null) {
  186. for (int counter = 0, maxCounter = keys.length;
  187. counter < maxCounter; counter++) {
  188. Object keyStrokeO = keys[counter++];
  189. KeyStroke ks = (keyStrokeO instanceof KeyStroke) ?
  190. (KeyStroke)keyStrokeO :
  191. KeyStroke.getKeyStroke((String)keyStrokeO);
  192. retMap.put(ks, keys[counter]);
  193. }
  194. }
  195. }
  196. /**
  197. * Utility method that creates a UIDefaults.LazyValue that creates
  198. * an ImageIcon UIResource for the specified <code>gifFile</code>
  199. * filename.
  200. */
  201. public static Object makeIcon(final Class baseClass, final String gifFile) {
  202. return new UIDefaults.LazyValue() {
  203. public Object createValue(UIDefaults table) {
  204. /* Copy resource into a byte array. This is
  205. * necessary because several browsers consider
  206. * Class.getResource a security risk because it
  207. * can be used to load additional classes.
  208. * Class.getResourceAsStream just returns raw
  209. * bytes, which we can convert to an image.
  210. */
  211. final byte[][] buffer = new byte[1][];
  212. SwingUtilities.doPrivileged(new Runnable() {
  213. public void run() {
  214. try {
  215. InputStream resource =
  216. baseClass.getResourceAsStream(gifFile);
  217. if (resource == null) {
  218. return;
  219. }
  220. BufferedInputStream in =
  221. new BufferedInputStream(resource);
  222. ByteArrayOutputStream out =
  223. new ByteArrayOutputStream(1024);
  224. buffer[0] = new byte[1024];
  225. int n;
  226. while ((n = in.read(buffer[0])) > 0) {
  227. out.write(buffer[0], 0, n);
  228. }
  229. in.close();
  230. out.flush();
  231. buffer[0] = out.toByteArray();
  232. } catch (IOException ioe) {
  233. System.err.println(ioe.toString());
  234. return;
  235. }
  236. }
  237. });
  238. if (buffer[0] == null) {
  239. System.err.println(baseClass.getName() + "/" +
  240. gifFile + " not found.");
  241. return null;
  242. }
  243. if (buffer[0].length == 0) {
  244. System.err.println("warning: " + gifFile +
  245. " is zero-length");
  246. return null;
  247. }
  248. return new IconUIResource(new ImageIcon(buffer[0]));
  249. }
  250. };
  251. }
  252. /**
  253. * Invoked when the user attempts an invalid operation,
  254. * such as pasting into an uneditable <code>JTextField</code>
  255. * that has focus. The default implementation beeps. Subclasses
  256. * that wish different behavior should override this and provide
  257. * the additional feedback.
  258. *
  259. * @param component Component the error occured in, may be null
  260. * indicating the error condition is not directly
  261. * associated with a <code>Component</code>.
  262. */
  263. public void provideErrorFeedback(Component component) {
  264. Toolkit toolkit = null;
  265. if (component != null) {
  266. toolkit = component.getToolkit();
  267. } else {
  268. toolkit = Toolkit.getDefaultToolkit();
  269. }
  270. toolkit.beep();
  271. } // provideErrorFeedback()
  272. /**
  273. * Returns the value of the specified system desktop property by
  274. * invoking <code>Toolkit.getDefaultToolkit().getDesktopProperty()</code>.
  275. * If the current value of the specified property is null, the
  276. * fallbackValue is returned.
  277. * @param systemPropertyName the name of the system desktop property being queried
  278. * @param fallbackValue the object to be returned as the value if the system value is null
  279. * @return the current value of the desktop property
  280. *
  281. * @see java.awt.Toolkit#getDesktopProperty
  282. *
  283. */
  284. public static Object getDesktopPropertyValue(String systemPropertyName, Object fallbackValue) {
  285. Object value = Toolkit.getDefaultToolkit().getDesktopProperty(systemPropertyName);
  286. if (value == null) {
  287. return fallbackValue;
  288. } else if (value instanceof Color) {
  289. return new ColorUIResource((Color)value);
  290. } else if (value instanceof Font) {
  291. return new FontUIResource((Font)value);
  292. }
  293. return value;
  294. }
  295. /**
  296. * Return a short string that identifies this look and feel, e.g.
  297. * "CDE/Motif". This string should be appropriate for a menu item.
  298. * Distinct look and feels should have different names, e.g.
  299. * a subclass of MotifLookAndFeel that changes the way a few components
  300. * are rendered should be called "CDE/Motif My Way"; something
  301. * that would be useful to a user trying to select a L&F from a list
  302. * of names.
  303. */
  304. public abstract String getName();
  305. /**
  306. * Return a string that identifies this look and feel. This string
  307. * will be used by applications/services that want to recognize
  308. * well known look and feel implementations. Presently
  309. * the well known names are "Motif", "Windows", "Mac", "Metal". Note
  310. * that a LookAndFeel derived from a well known superclass
  311. * that doesn't make any fundamental changes to the look or feel
  312. * shouldn't override this method.
  313. */
  314. public abstract String getID();
  315. /**
  316. * Return a one line description of this look and feel implementation,
  317. * e.g. "The CDE/Motif Look and Feel". This string is intended for
  318. * the user, e.g. in the title of a window or in a ToolTip message.
  319. */
  320. public abstract String getDescription();
  321. /**
  322. * Returns true if the <code>LookAndFeel</code> returned
  323. * <code>RootPaneUI</code> instances support providing Window decorations
  324. * in a <code>JRootPane</code>.
  325. * <p>
  326. * The default implementation returns false, subclasses that support
  327. * Window decorations should override this and return true.
  328. *
  329. * @return True if the RootPaneUI instances created support client side
  330. * decorations
  331. * @see JDialog#setDefaultLookAndFeelDecorated
  332. * @see JFrame#setDefaultLookAndFeelDecorated
  333. * @see JRootPane#setWindowDecorationStyle
  334. * @since 1.4
  335. */
  336. public boolean getSupportsWindowDecorations() {
  337. return false;
  338. }
  339. /**
  340. * If the underlying platform has a "native" look and feel, and this
  341. * is an implementation of it, return true. For example a CDE/Motif
  342. * look and implementation would return true when the underlying
  343. * platform was Solaris.
  344. */
  345. public abstract boolean isNativeLookAndFeel();
  346. /**
  347. * Return true if the underlying platform supports and or permits
  348. * this look and feel. This method returns false if the look
  349. * and feel depends on special resources or legal agreements that
  350. * aren't defined for the current platform.
  351. *
  352. * @see UIManager#setLookAndFeel
  353. */
  354. public abstract boolean isSupportedLookAndFeel();
  355. /**
  356. * UIManager.setLookAndFeel calls this method before the first
  357. * call (and typically the only call) to getDefaults(). Subclasses
  358. * should do any one-time setup they need here, rather than
  359. * in a static initializer, because look and feel class objects
  360. * may be loaded just to discover that isSupportedLookAndFeel()
  361. * returns false.
  362. *
  363. * @see #uninitialize
  364. * @see UIManager#setLookAndFeel
  365. */
  366. public void initialize() {
  367. }
  368. /**
  369. * UIManager.setLookAndFeel calls this method just before we're
  370. * replaced by a new default look and feel. Subclasses may
  371. * choose to free up some resources here.
  372. *
  373. * @see #initialize
  374. */
  375. public void uninitialize() {
  376. }
  377. /**
  378. * This method is called once by UIManager.setLookAndFeel to create
  379. * the look and feel specific defaults table. Other applications,
  380. * for example an application builder, may also call this method.
  381. *
  382. * @see #initialize
  383. * @see #uninitialize
  384. * @see UIManager#setLookAndFeel
  385. */
  386. public UIDefaults getDefaults() {
  387. return null;
  388. }
  389. /**
  390. * Returns a string that displays and identifies this
  391. * object's properties.
  392. *
  393. * @return a String representation of this object
  394. */
  395. public String toString() {
  396. return "[" + getDescription() + " - " + getClass().getName() + "]";
  397. }
  398. }