1. /*
  2. * @(#)LookAndFeel.java 1.24 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;
  11. import java.awt.Font;
  12. import java.awt.event.InputEvent;
  13. import java.awt.event.KeyEvent;
  14. import java.awt.Color;
  15. import java.awt.SystemColor;
  16. import javax.swing.text.*;
  17. import javax.swing.border.*;
  18. import javax.swing.plaf.*;
  19. import java.net.URL;
  20. import java.io.*;
  21. import java.util.StringTokenizer;
  22. /**
  23. * Completely characterizes a look and feel from the point of view
  24. * of the pluggable look and feel components.
  25. *
  26. * @version 1.24 02/02/00
  27. * @author Tom Ball
  28. * @author Hans Muller
  29. */
  30. public abstract class LookAndFeel
  31. {
  32. /**
  33. * Convenience method for initializing a component's foreground
  34. * and background color properties with values from the current
  35. * defaults table. The properties are only set if the current
  36. * value is either null or a UIResource.
  37. *
  38. * @param c the target component for installing default color/font properties
  39. * @param defaultBgName the key for the default background
  40. * @param defaultFgName the key for the default foreground
  41. *
  42. * @see #installColorsAndFont
  43. * @see UIManager#getColor
  44. */
  45. public static void installColors(JComponent c,
  46. String defaultBgName,
  47. String defaultFgName)
  48. {
  49. Color bg = c.getBackground();
  50. if (bg == null || bg instanceof UIResource) {
  51. c.setBackground(UIManager.getColor(defaultBgName));
  52. }
  53. Color fg = c.getForeground();
  54. if (fg == null || fg instanceof UIResource) {
  55. c.setForeground(UIManager.getColor(defaultFgName));
  56. }
  57. }
  58. /**
  59. * Convenience method for initializing a components foreground
  60. * background and font properties with values from the current
  61. * defaults table. The properties are only set if the current
  62. * value is either null or a UIResource.
  63. *
  64. * @param c the target component for installing default color/font properties
  65. * @param defaultBgName the key for the default background
  66. * @param defaultFgName the key for the default foreground
  67. * @param defaultFontName the key for the default font
  68. *
  69. * @see #installColors
  70. * @see UIManager#getColor
  71. * @see UIManager#getFont
  72. */
  73. public static void installColorsAndFont(JComponent c,
  74. String defaultBgName,
  75. String defaultFgName,
  76. String defaultFontName) {
  77. Font f = c.getFont();
  78. if (f == null || f instanceof UIResource) {
  79. c.setFont(UIManager.getFont(defaultFontName));
  80. }
  81. installColors(c, defaultBgName, defaultFgName);
  82. }
  83. /**
  84. * Convenience method for installing a component's default Border
  85. * object on the specified component if either the border is
  86. * currently null or already an instance of UIResource.
  87. * @param c the target component for installing default border
  88. * @param defaultBorderName the key specifying the default border
  89. */
  90. public static void installBorder(JComponent c, String defaultBorderName) {
  91. Border b = c.getBorder();
  92. if (b == null || b instanceof UIResource) {
  93. c.setBorder(UIManager.getBorder(defaultBorderName));
  94. }
  95. }
  96. /**
  97. * Convenience method for un-installing a component's default
  98. * border on the specified component if the border is
  99. * currently an instance of UIResource.
  100. * @param c the target component for uninstalling default border
  101. */
  102. public static void uninstallBorder(JComponent c) {
  103. if (c.getBorder() instanceof UIResource) {
  104. c.setBorder(null);
  105. }
  106. }
  107. /**
  108. * Convenience method for building lists of KeyBindings.
  109. * <p>
  110. * Return an array of KeyBindings, one for each KeyStroke,Action pair
  111. * in <b>keyBindingList</b>. A KeyStroke can either be a string in
  112. * the format specified by the <code>KeyStroke.getKeyStroke</code>
  113. * method or a KeyStroke object.
  114. * <p>
  115. * Actions are strings. Here's an example:
  116. * <pre>
  117. * JTextComponent.KeyBinding[] multilineBindings = makeKeyBindings( new Object[] {
  118. * "UP", DefaultEditorKit.upAction,
  119. * "DOWN", DefaultEditorKit.downAction,
  120. * "PAGE_UP", DefaultEditorKit.pageUpAction,
  121. * "PAGE_DOWN", DefaultEditorKit.pageDownAction,
  122. * "ENTER", DefaultEditorKit.insertBreakAction,
  123. * "TAB", DefaultEditorKit.insertTabAction
  124. * });
  125. * </pre>
  126. *
  127. * @param keyBindingList an array of KeyStroke,Action pairs
  128. * @return an array of KeyBindings
  129. */
  130. public static JTextComponent.KeyBinding[] makeKeyBindings(Object[] keyBindingList)
  131. {
  132. JTextComponent.KeyBinding[] rv = new JTextComponent.KeyBinding[keyBindingList.length / 2];
  133. for(int i = 0; i < keyBindingList.length; i += 2) {
  134. KeyStroke keystroke = (keyBindingList[i] instanceof KeyStroke)
  135. ? (KeyStroke)keyBindingList[i]
  136. : KeyStroke.getKeyStroke((String)keyBindingList[i]);
  137. String action = (String)keyBindingList[i+1];
  138. rv[i / 2] = new JTextComponent.KeyBinding(keystroke, action);
  139. }
  140. return rv;
  141. }
  142. /**
  143. * Creates a InputMap from <code>keys</code>. <code>keys</code>
  144. * describes the InputMap, with every even number item being a String
  145. * giving the KeyStroke as speced in
  146. * <code>KeyStroke.getKeyStroke(String)</code>
  147. * (or a KeyStroke), and every odd number item the Object
  148. * used to determine the associated Action in an ActionMap.
  149. *
  150. * @since 1.3
  151. */
  152. public static InputMap makeInputMap(Object[] keys) {
  153. InputMap retMap = new InputMapUIResource();
  154. loadKeyBindings(retMap, keys);
  155. return retMap;
  156. }
  157. /**
  158. * Creates a ComponentInputMap from <code>keys</code>. <code>keys</code>
  159. * describes the InputMap, with every even number item being a String
  160. * giving
  161. * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
  162. * (or a KeyStroke), and every odd number item the Object
  163. * used to determine the associated Action in an ActionMap.
  164. *
  165. * @since 1.3
  166. */
  167. public static ComponentInputMap makeComponentInputMap(JComponent c,
  168. Object[] keys) {
  169. ComponentInputMap retMap = new ComponentInputMapUIResource(c);
  170. loadKeyBindings(retMap, keys);
  171. return retMap;
  172. }
  173. /**
  174. * Loads the bindings in <code>keys</code> into <code>retMap</code>.
  175. * This does not remove any existing bindings in <code>retMap</code>.
  176. * <code>keys</code>
  177. * describes the InputMap, with every even number item being a String
  178. * giving
  179. * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
  180. * (or a KeyStroke), and every odd number item the Object
  181. * used to determine the associated Action in an ActionMap.
  182. *
  183. * @since 1.3
  184. */
  185. public static void loadKeyBindings(InputMap retMap, Object[] keys) {
  186. if (keys != null) {
  187. for (int counter = 0, maxCounter = keys.length;
  188. counter < maxCounter; counter++) {
  189. Object keyStrokeO = keys[counter++];
  190. KeyStroke ks = (keyStrokeO instanceof KeyStroke) ?
  191. (KeyStroke)keyStrokeO :
  192. KeyStroke.getKeyStroke((String)keyStrokeO);
  193. retMap.put(ks, keys[counter]);
  194. }
  195. }
  196. }
  197. /**
  198. * Utility method that creates a UIDefaults.LazyValue that creates
  199. * an ImageIcon UIResource for the specified <code>gifFile</code>
  200. * filename.
  201. */
  202. public static Object makeIcon(final Class baseClass, final String gifFile) {
  203. return new UIDefaults.LazyValue() {
  204. public Object createValue(UIDefaults table) {
  205. /* Copy resource into a byte array. This is
  206. * necessary because several browsers consider
  207. * Class.getResource a security risk because it
  208. * can be used to load additional classes.
  209. * Class.getResourceAsStream just returns raw
  210. * bytes, which we can convert to an image.
  211. */
  212. final byte[][] buffer = new byte[1][];
  213. SwingUtilities.doPrivileged(new Runnable() {
  214. public void run() {
  215. try {
  216. InputStream resource =
  217. baseClass.getResourceAsStream(gifFile);
  218. if (resource == null) {
  219. return;
  220. }
  221. BufferedInputStream in =
  222. new BufferedInputStream(resource);
  223. ByteArrayOutputStream out =
  224. new ByteArrayOutputStream(1024);
  225. buffer[0] = new byte[1024];
  226. int n;
  227. while ((n = in.read(buffer[0])) > 0) {
  228. out.write(buffer[0], 0, n);
  229. }
  230. in.close();
  231. out.flush();
  232. buffer[0] = out.toByteArray();
  233. } catch (IOException ioe) {
  234. System.err.println(ioe.toString());
  235. return;
  236. }
  237. }
  238. });
  239. if (buffer[0] == null) {
  240. System.err.println(baseClass.getName() + "/" +
  241. gifFile + " not found.");
  242. return null;
  243. }
  244. if (buffer[0].length == 0) {
  245. System.err.println("warning: " + gifFile +
  246. " is zero-length");
  247. return null;
  248. }
  249. return new IconUIResource(new ImageIcon(buffer[0]));
  250. }
  251. };
  252. }
  253. /**
  254. * Return a short string that identifies this look and feel, e.g.
  255. * "CDE/Motif". This string should be appropriate for a menu item.
  256. * Distinct look and feels should have different names, e.g.
  257. * a subclass of MotifLookAndFeel that changes the way a few components
  258. * are rendered should be called "CDE/Motif My Way"; something
  259. * that would be useful to a user trying to select a L&F from a list
  260. * of names.
  261. */
  262. public abstract String getName();
  263. /**
  264. * Return a string that identifies this look and feel. This string
  265. * will be used by applications/services that want to recognize
  266. * well known look and feel implementations. Presently
  267. * the well known names are "Motif", "Windows", "Mac", "Metal". Note
  268. * that a LookAndFeel derived from a well known superclass
  269. * that doesn't make any fundamental changes to the look or feel
  270. * shouldn't override this method.
  271. */
  272. public abstract String getID();
  273. /**
  274. * Return a one line description of this look and feel implementation,
  275. * e.g. "The CDE/Motif Look and Feel". This string is intended for
  276. * the user, e.g. in the title of a window or in a ToolTip message.
  277. */
  278. public abstract String getDescription();
  279. /**
  280. * If the underlying platform has a "native" look and feel, and this
  281. * is an implementation of it, return true. For example a CDE/Motif
  282. * look and implementation would return true when the underlying
  283. * platform was Solaris.
  284. */
  285. public abstract boolean isNativeLookAndFeel();
  286. /**
  287. * Return true if the underlying platform supports and or permits
  288. * this look and feel. This method returns false if the look
  289. * and feel depends on special resources or legal agreements that
  290. * aren't defined for the current platform.
  291. *
  292. * @see UIManager#setLookAndFeel
  293. */
  294. public abstract boolean isSupportedLookAndFeel();
  295. /**
  296. * UIManager.setLookAndFeel calls this method before the first
  297. * call (and typically the only call) to getDefaults(). Subclasses
  298. * should do any one-time setup they need here, rather than
  299. * in a static initializer, because look and feel class objects
  300. * may be loaded just to discover that isSupportedLookAndFeel()
  301. * returns false.
  302. *
  303. * @see #uninitialize
  304. * @see UIManager#setLookAndFeel
  305. */
  306. public void initialize() {
  307. }
  308. /**
  309. * UIManager.setLookAndFeel calls this method just before we're
  310. * replaced by a new default look and feel. Subclasses may
  311. * choose to free up some resources here.
  312. *
  313. * @see #initialize
  314. */
  315. public void uninitialize() {
  316. }
  317. /**
  318. * This method is called once by UIManager.setLookAndFeel to create
  319. * the look and feel specific defaults table. Other applications,
  320. * for example an application builder, may also call this method.
  321. *
  322. * @see #initialize
  323. * @see #uninitialize
  324. * @see UIManager#setLookAndFeel
  325. */
  326. public UIDefaults getDefaults() {
  327. return null;
  328. }
  329. /**
  330. * Returns a string that displays and identifies this
  331. * object's properties.
  332. *
  333. * @return a String representation of this object
  334. */
  335. public String toString() {
  336. return "[" + getDescription() + " - " + getClass().getName() + "]";
  337. }
  338. }