1. /*
  2. * @(#)UIManager.java 1.103 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.Component;
  9. import java.awt.Container;
  10. import java.awt.Window;
  11. import java.awt.Font;
  12. import java.awt.Color;
  13. import java.awt.Insets;
  14. import java.awt.Dimension;
  15. import java.awt.KeyboardFocusManager;
  16. import java.awt.KeyEventPostProcessor;
  17. import java.awt.event.KeyEvent;
  18. import java.security.AccessController;
  19. import javax.swing.plaf.ComponentUI;
  20. import javax.swing.border.Border;
  21. import javax.swing.event.SwingPropertyChangeSupport;
  22. import java.beans.PropertyChangeListener;
  23. import java.beans.PropertyChangeEvent;
  24. import java.io.FileOutputStream;
  25. import java.io.IOException;
  26. import java.io.ObjectOutputStream;
  27. import java.io.ObjectInputStream;
  28. import java.io.Serializable;
  29. import java.io.File;
  30. import java.io.FileInputStream;
  31. import java.io.BufferedInputStream;
  32. import java.util.Enumeration;
  33. import java.util.Hashtable;
  34. import java.util.Properties;
  35. import java.util.StringTokenizer;
  36. import java.util.Vector;
  37. import java.util.Locale;
  38. import sun.security.action.GetPropertyAction;
  39. /**
  40. * This class keeps track of the current look and feel and its
  41. * defaults.
  42. * The default look and feel class is chosen in the following manner:
  43. * <ol>
  44. * <li>If the system property <code>swing.defaultlaf</code> is
  45. * non-null, use it as the default look and feel class name.
  46. * <li>If the {@link java.util.Properties} file <code>swing.properties</code>
  47. * exists and contains the key <code>swing.defaultlaf</code>,
  48. * use its value as default look and feel class name. The location of
  49. * <code>swing.properties</code> may vary depending upon the
  50. * implementation of the Java platform. In Sun's implementation
  51. * this will reside in
  52. * <code>&java.home>/lib/swing.properties</code>. Refer to
  53. * the release notes of the implementation you are using for
  54. * further details.
  55. * <li>Otherwise use the Java look and feel.
  56. * </ol>
  57. * <p>
  58. * We manage three levels of defaults: user defaults, look
  59. * and feel defaults, system defaults. A call to <code>UIManager.get</code>
  60. * checks all three levels in order and returns the first non-<code>null</code>
  61. * value for a key, if any. A call to <code>UIManager.put</code> just
  62. * affects the user defaults. Note that a call to
  63. * <code>setLookAndFeel</code> doesn't affect the user defaults, it just
  64. * replaces the middle defaults "level".
  65. * <p>
  66. * <strong>Warning:</strong>
  67. * Serialized objects of this class will not be compatible with
  68. * future Swing releases. The current serialization support is
  69. * appropriate for short term storage or RMI between applications running
  70. * the same version of Swing. As of 1.4, support for long term storage
  71. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  72. * has been added to the <code>java.beans</code> package.
  73. * Please see {@link java.beans.XMLEncoder}.
  74. *
  75. * @see javax.swing.plaf.metal
  76. *
  77. * @version 1.103 01/23/03
  78. * @author Thomas Ball
  79. * @author Hans Muller
  80. */
  81. public class UIManager implements Serializable
  82. {
  83. /**
  84. * This class defines the state managed by the <code>UIManager</code>. For
  85. * Swing applications the fields in this class could just as well
  86. * be static members of <code>UIManager</code> however we give them
  87. * "AppContext"
  88. * scope instead so that applets (and potentially multiple lightweight
  89. * applications running in a single VM) have their own state. For example,
  90. * an applet can alter its look and feel, see <code>setLookAndFeel</code>.
  91. * Doing so has no affect on other applets (or the browser).
  92. */
  93. private static class LAFState
  94. {
  95. Properties swingProps;
  96. private UIDefaults[] tables = new UIDefaults[2];
  97. boolean initialized = false;
  98. MultiUIDefaults multiUIDefaults = new MultiUIDefaults(tables);
  99. LookAndFeel lookAndFeel;
  100. LookAndFeel multiLookAndFeel = null;
  101. Vector auxLookAndFeels = null;
  102. SwingPropertyChangeSupport changeSupport =
  103. new SwingPropertyChangeSupport(UIManager.class);
  104. UIDefaults getLookAndFeelDefaults() { return tables[0]; }
  105. void setLookAndFeelDefaults(UIDefaults x) { tables[0] = x; }
  106. UIDefaults getSystemDefaults() { return tables[1]; }
  107. void setSystemDefaults(UIDefaults x) { tables[1] = x; }
  108. }
  109. /**
  110. * The <code>AppContext</code> key for our one <code>LAFState</code> instance.
  111. */
  112. private static final Object lafStateACKey = new StringBuffer("LookAndFeel State");
  113. /* Lock object used in place of class object for synchronization. (4187686)
  114. */
  115. private static final Object classLock = new Object();
  116. /* Cache the last referenced LAFState to improve performance
  117. * when accessing it. The cache is based on last thread rather
  118. * than last AppContext because of the cost of looking up the
  119. * AppContext each time. Since most Swing UI work is on the
  120. * EventDispatchThread, this hits often enough to justify the
  121. * overhead. (4193032)
  122. */
  123. private static Thread currentLAFStateThread = null;
  124. private static LAFState currentLAFState = null;
  125. /**
  126. * Return the <code>LAFState</code> object, lazily create one if necessary.
  127. * All access to the <code>LAFState</code> fields is done via this method,
  128. * for example:
  129. * <pre>
  130. * getLAFState().initialized = true;
  131. * </pre>
  132. */
  133. private static LAFState getLAFState() {
  134. // First check whether we're running on the same thread as
  135. // the last request.
  136. Thread thisThread = Thread.currentThread();
  137. if (thisThread == currentLAFStateThread) {
  138. return currentLAFState;
  139. }
  140. LAFState rv = (LAFState)SwingUtilities.appContextGet(lafStateACKey);
  141. if (rv == null) {
  142. synchronized (classLock) {
  143. rv = (LAFState)SwingUtilities.appContextGet(lafStateACKey);
  144. if (rv == null) {
  145. SwingUtilities.appContextPut(lafStateACKey,
  146. (rv = new LAFState()));
  147. }
  148. }
  149. }
  150. currentLAFStateThread = thisThread;
  151. currentLAFState = rv;
  152. return rv;
  153. }
  154. /* Keys used for the properties file in <java.home>/lib/swing.properties.
  155. * See loadUserProperties(), initialize().
  156. */
  157. private static final String defaultLAFKey = "swing.defaultlaf";
  158. private static final String auxiliaryLAFsKey = "swing.auxiliarylaf";
  159. private static final String multiplexingLAFKey = "swing.plaf.multiplexinglaf";
  160. private static final String installedLAFsKey = "swing.installedlafs";
  161. private static final String disableMnemonicKey = "swing.disablenavaids";
  162. /**
  163. * Return a swing.properties file key for the attribute of specified
  164. * look and feel. The attr is either "name" or "class", a typical
  165. * key would be: "swing.installedlaf.windows.name"
  166. */
  167. private static String makeInstalledLAFKey(String laf, String attr) {
  168. return "swing.installedlaf." + laf + "." + attr;
  169. }
  170. /**
  171. * The filename for swing.properties is a path like this (Unix version):
  172. * <java.home>/lib/swing.properties. This method returns a bogus
  173. * filename if java.home isn't defined.
  174. */
  175. private static String makeSwingPropertiesFilename() {
  176. String sep = File.separator;
  177. return AccessController.doPrivileged(new GetPropertyAction(
  178. "java.home", "<java.home undefined>")) + sep + "lib" +
  179. sep + "swing.properties";
  180. }
  181. /**
  182. * Provides a little information about an installed
  183. * <code>LookAndFeel</code> for the sake of configuring a menu or
  184. * for initial application set up.
  185. *
  186. * @see UIManager#getInstalledLookAndFeels
  187. * @see LookAndFeel
  188. */
  189. public static class LookAndFeelInfo {
  190. private String name;
  191. private String className;
  192. /**
  193. * Constructs a <code>UIManager</code>s
  194. * <code>LookAndFeelInfo</code> object.
  195. *
  196. * @param name a <code>String</code> specifying the name of
  197. * the look and feel
  198. * @param className a <code>String</code> specifiying the name of
  199. * the class that implements the look and feel
  200. */
  201. public LookAndFeelInfo(String name, String className) {
  202. this.name = name;
  203. this.className = className;
  204. }
  205. /**
  206. * Returns the name of the look and feel in a form suitable
  207. * for a menu or other presentation
  208. * @return a <code>String</code> containing the name
  209. * @see LookAndFeel#getName
  210. */
  211. public String getName() {
  212. return name;
  213. }
  214. /**
  215. * Returns the name of the class that implements this look and feel.
  216. * @return the name of the class that implements this
  217. * <code>LookAndFeel</code>
  218. * @see LookAndFeel
  219. */
  220. public String getClassName() {
  221. return className;
  222. }
  223. /**
  224. * Returns a string that displays and identifies this
  225. * object's properties.
  226. *
  227. * @return a <code>String</code> representation of this object
  228. */
  229. public String toString() {
  230. return getClass().getName() + "[" + getName() + " " + getClassName() + "]";
  231. }
  232. }
  233. /**
  234. * The default value of <code>installedLAFS</code> is used when no
  235. * swing.properties
  236. * file is available or if the file doesn't contain a "swing.installedlafs"
  237. * property.
  238. *
  239. * @see #initializeInstalledLAFs
  240. */
  241. private static LookAndFeelInfo[] installedLAFs = {
  242. new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"),
  243. new LookAndFeelInfo("CDE/Motif", "com.sun.java.swing.plaf.motif.MotifLookAndFeel"),
  244. new LookAndFeelInfo("Windows", "com.sun.java.swing.plaf.windows.WindowsLookAndFeel")
  245. };
  246. /**
  247. * Returns an array of objects that provide some information about the
  248. * <code>LookAndFeel</code> implementations that have been installed with this
  249. * software development kit. The <code>LookAndFeel</code> info objects can
  250. * used by an application to construct a menu of look and feel options for
  251. * the user or to set the look and feel at start up time. Note that
  252. * we do not return the <code>LookAndFeel</code> classes themselves here to
  253. * avoid the cost of unnecessarily loading them.
  254. * <p>
  255. * Given a <code>LookAndFeelInfo</code> object one can set the current
  256. * look and feel like this:
  257. * <pre>
  258. * UIManager.setLookAndFeel(info.getClassName());
  259. * </pre>
  260. * @return an array of <code>LookAndFeelInfo</code> objects
  261. *
  262. * @see #setLookAndFeel
  263. */
  264. public static LookAndFeelInfo[] getInstalledLookAndFeels() {
  265. maybeInitialize();
  266. LookAndFeelInfo[] ilafs = installedLAFs;
  267. LookAndFeelInfo[] rv = new LookAndFeelInfo[ilafs.length];
  268. System.arraycopy(ilafs, 0, rv, 0, ilafs.length);
  269. return rv;
  270. }
  271. /**
  272. * Replaces the current array of installed <code>LookAndFeelInfos</code>.
  273. * @param infos new array of <code>LookAndFeelInfo</code> objects
  274. *
  275. * @see #getInstalledLookAndFeels
  276. */
  277. public static void setInstalledLookAndFeels(LookAndFeelInfo[] infos)
  278. throws SecurityException
  279. {
  280. LookAndFeelInfo[] newInfos = new LookAndFeelInfo[infos.length];
  281. System.arraycopy(infos, 0, newInfos, 0, infos.length);
  282. installedLAFs = newInfos;
  283. }
  284. /**
  285. * Adds the specified look and feel to the current array and
  286. * then calls {@link #setInstalledLookAndFeels}.
  287. * @param info a <code>LookAndFeelInfo</code> object that names the
  288. * look and feel and identifies that class that implements it
  289. */
  290. public static void installLookAndFeel(LookAndFeelInfo info) {
  291. LookAndFeelInfo[] infos = getInstalledLookAndFeels();
  292. LookAndFeelInfo[] newInfos = new LookAndFeelInfo[infos.length + 1];
  293. System.arraycopy(infos, 0, newInfos, 0, infos.length);
  294. newInfos[infos.length] = info;
  295. setInstalledLookAndFeels(newInfos);
  296. }
  297. /**
  298. * Creates a new look and feel and adds it to the current array.
  299. * Then calls {@link #setInstalledLookAndFeels}.
  300. *
  301. * @param name a <code>String</code> specifying the name of the
  302. * look and feel
  303. * @param className a <code>String</code> specifying the class name
  304. * that implements the look and feel
  305. */
  306. public static void installLookAndFeel(String name, String className) {
  307. installLookAndFeel(new LookAndFeelInfo(name, className));
  308. }
  309. /**
  310. * Returns the current default look and feel or <code>null</code>.
  311. *
  312. * @return the current default look and feel, or <code>null</code>
  313. * @see #setLookAndFeel
  314. */
  315. public static LookAndFeel getLookAndFeel() {
  316. maybeInitialize();
  317. return getLAFState().lookAndFeel;
  318. }
  319. /**
  320. * Sets the current default look and feel using a
  321. * <code>LookAndFeel</code> object.
  322. * <p>
  323. * This is a JavaBeans bound property.
  324. *
  325. * @param newLookAndFeel the <code>LookAndFeel</code> object
  326. * @exception UnsupportedLookAndFeelException if
  327. * <code>lnf.isSupportedLookAndFeel()</code> is false
  328. * @see #getLookAndFeel
  329. */
  330. public static void setLookAndFeel(LookAndFeel newLookAndFeel)
  331. throws UnsupportedLookAndFeelException
  332. {
  333. if ((newLookAndFeel != null) && !newLookAndFeel.isSupportedLookAndFeel()) {
  334. String s = newLookAndFeel.toString() + " not supported on this platform";
  335. throw new UnsupportedLookAndFeelException(s);
  336. }
  337. LookAndFeel oldLookAndFeel = getLAFState().lookAndFeel;
  338. if (oldLookAndFeel != null) {
  339. oldLookAndFeel.uninitialize();
  340. }
  341. getLAFState().lookAndFeel = newLookAndFeel;
  342. if (newLookAndFeel != null) {
  343. newLookAndFeel.initialize();
  344. getLAFState().setLookAndFeelDefaults(newLookAndFeel.getDefaults());
  345. }
  346. else {
  347. getLAFState().setLookAndFeelDefaults(null);
  348. }
  349. getLAFState().changeSupport.firePropertyChange("lookAndFeel", oldLookAndFeel, newLookAndFeel);
  350. }
  351. /**
  352. * Sets the current default look and feel using a class name.
  353. *
  354. * @param className a string specifying the name of the class that implements
  355. * the look and feel
  356. * @exception ClassNotFoundException if the <code>LookAndFeel</code>
  357. * class could not be found
  358. * @exception InstantiationException if a new instance of the class
  359. * couldn't be created
  360. * @exception IllegalAccessException if the class or initializer isn't accessible
  361. * @exception UnsupportedLookAndFeelException if
  362. * <code>lnf.isSupportedLookAndFeel()</code> is false
  363. */
  364. public static void setLookAndFeel(String className)
  365. throws ClassNotFoundException,
  366. InstantiationException,
  367. IllegalAccessException,
  368. UnsupportedLookAndFeelException
  369. {
  370. Class lnfClass = SwingUtilities.loadSystemClass(className);
  371. setLookAndFeel((LookAndFeel)(lnfClass.newInstance()));
  372. }
  373. /**
  374. * Returns the name of the <code>LookAndFeel</code> class that implements
  375. * the native systems look and feel if there is one, otherwise
  376. * the name of the default cross platform <code>LookAndFeel</code>
  377. * class.
  378. *
  379. * @return the <code>String</code> of the <code>LookAndFeel</code>
  380. * class
  381. *
  382. * @see #setLookAndFeel
  383. * @see #getCrossPlatformLookAndFeelClassName
  384. */
  385. public static String getSystemLookAndFeelClassName() {
  386. String osName = (String)AccessController.doPrivileged(
  387. new GetPropertyAction("os.name"));
  388. if (osName != null) {
  389. if (osName.indexOf("Windows") != -1) {
  390. return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
  391. }
  392. else if ((osName.indexOf("Solaris") != -1) ||
  393. (osName.indexOf("SunOS") != -1)) {
  394. return "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
  395. }
  396. else if (osName.indexOf("Mac") != -1 ) {
  397. return "com.sun.java.swing.plaf.mac.MacLookAndFeel";
  398. }
  399. }
  400. return getCrossPlatformLookAndFeelClassName();
  401. }
  402. /**
  403. * Returns the name of the <code>LookAndFeel</code> class that implements
  404. * the default cross platform look and feel -- the Java
  405. * Look and Feel (JLF).
  406. *
  407. * @return a string with the JLF implementation-class
  408. * @see #setLookAndFeel
  409. * @see #getSystemLookAndFeelClassName
  410. */
  411. public static String getCrossPlatformLookAndFeelClassName() {
  412. return "javax.swing.plaf.metal.MetalLookAndFeel";
  413. }
  414. /**
  415. * Returns the default values for this look and feel.
  416. *
  417. * @return a <code>UIDefaults</code> object containing the default values
  418. */
  419. public static UIDefaults getDefaults() {
  420. maybeInitialize();
  421. return getLAFState().multiUIDefaults;
  422. }
  423. /**
  424. * Returns a drawing font from the defaults table.
  425. *
  426. * @param key an <code>Object</code> specifying the font
  427. * @return the <code>Font</code> object
  428. */
  429. public static Font getFont(Object key) {
  430. return getDefaults().getFont(key);
  431. }
  432. /**
  433. * Returns a drawing font from the defaults table that is appropriate
  434. * for the given locale.
  435. *
  436. * @param key an <code>Object</code> specifying the font
  437. * @param l the <code>Locale</code> for which the font is desired
  438. * @return the <code>Font</code> object
  439. * @since 1.4
  440. */
  441. public static Font getFont(Object key, Locale l) {
  442. return getDefaults().getFont(key,l);
  443. }
  444. /**
  445. * Returns a drawing color from the defaults table.
  446. *
  447. * @param key an <code>Object</code> specifying the color
  448. * @return the <code>Color</code> object
  449. */
  450. public static Color getColor(Object key) {
  451. return getDefaults().getColor(key);
  452. }
  453. /**
  454. * Returns a drawing color from the defaults table that is appropriate
  455. * for the given locale.
  456. *
  457. * @param key an <code>Object</code> specifying the color
  458. * @param l the <code>Locale</code> for which the color is desired
  459. * @return the <code>Color</code> object
  460. * @since 1.4
  461. */
  462. public static Color getColor(Object key, Locale l) {
  463. return getDefaults().getColor(key,l);
  464. }
  465. /**
  466. * Returns an <code>Icon</code> from the defaults table.
  467. *
  468. * @param key an <code>Object</code> specifying the icon
  469. * @return the <code>Icon</code> object
  470. */
  471. public static Icon getIcon(Object key) {
  472. return getDefaults().getIcon(key);
  473. }
  474. /**
  475. * Returns an <code>Icon</code> from the defaults table that is appropriate
  476. * for the given locale.
  477. *
  478. * @param key an <code>Object</code> specifying the icon
  479. * @param l the <code>Locale</code> for which the icon is desired
  480. * @return the <code>Icon</code> object
  481. * @since 1.4
  482. */
  483. public static Icon getIcon(Object key, Locale l) {
  484. return getDefaults().getIcon(key,l);
  485. }
  486. /**
  487. * Returns a border from the defaults table.
  488. *
  489. * @param key an <code>Object</code> specifying the border
  490. * @return the <code>Border</code> object
  491. */
  492. public static Border getBorder(Object key) {
  493. return getDefaults().getBorder(key);
  494. }
  495. /**
  496. * Returns a border from the defaults table that is appropriate
  497. * for the given locale.
  498. *
  499. * @param key an <code>Object</code> specifying the border
  500. * @param l the <code>Locale</code> for which the border is desired
  501. * @return the <code>Border</code> object
  502. * @since 1.4
  503. */
  504. public static Border getBorder(Object key, Locale l) {
  505. return getDefaults().getBorder(key,l);
  506. }
  507. /**
  508. * Returns a string from the defaults table.
  509. *
  510. * @param key an <code>Object</code> specifying the string
  511. * @return the <code>String</code>
  512. */
  513. public static String getString(Object key) {
  514. return getDefaults().getString(key);
  515. }
  516. /**
  517. * Returns a string from the defaults table that is appropriate for the
  518. * given locale.
  519. *
  520. * @param key an <code>Object</code> specifying the string
  521. * @param l the <code>Locale</code> for which the string is desired
  522. * @return the <code>String</code>
  523. */
  524. public static String getString(Object key, Locale l) {
  525. return getDefaults().getString(key,l);
  526. }
  527. /**
  528. * Returns a string from the defaults table that is appropriate for the
  529. * given locale.
  530. *
  531. * @param key an <code>Object</code> specifying the string
  532. * @param c Component used to determine Locale, null implies use the
  533. * default Locale.
  534. * @return the <code>String</code>
  535. */
  536. static String getString(Object key, Component c) {
  537. Locale l = (c == null) ? Locale.getDefault() : c.getLocale();
  538. return getString(key, l);
  539. }
  540. /**
  541. * Returns an integer from the defaults table.
  542. *
  543. * @param key an <code>Object</code> specifying the int
  544. * @return the int
  545. */
  546. public static int getInt(Object key) {
  547. return getDefaults().getInt(key);
  548. }
  549. /**
  550. * Returns an integer from the defaults table that is appropriate
  551. * for the given locale.
  552. *
  553. * @param key an <code>Object</code> specifying the int
  554. * @param l the <code>Locale</code> for which the int is desired
  555. * @return the int
  556. * @since 1.4
  557. */
  558. public static int getInt(Object key, Locale l) {
  559. return getDefaults().getInt(key,l);
  560. }
  561. /**
  562. * Returns an integer from the defaults table. If <code>key</code> does
  563. * not map to a valid <code>Integer</code>, or can not be convered from
  564. * a <code>String</code> to an integer, <code>default</code> is
  565. * returned.
  566. *
  567. * @param key an <code>Object</code> specifying the int
  568. * @param defaultValue Returned value if <code>key</code> is not available,
  569. * or is not an Integer
  570. * @return the int
  571. */
  572. static int getInt(Object key, int defaultValue) {
  573. Object value = UIManager.get(key);
  574. if (value instanceof Integer) {
  575. return ((Integer)value).intValue();
  576. }
  577. if (value instanceof String) {
  578. try {
  579. return Integer.parseInt((String)value);
  580. } catch (NumberFormatException nfe) {}
  581. }
  582. return defaultValue;
  583. }
  584. /**
  585. * Returns a boolean from the defaults table which is associated with
  586. * the key value. If the key is not found or the key doesn't represent
  587. * a boolean value then false will be returned.
  588. *
  589. * @param key an <code>Object</code> specifying the key for the desired boolean value
  590. * @return the boolean value corresponding to the key
  591. * @since 1.4
  592. */
  593. public static boolean getBoolean(Object key) {
  594. return getDefaults().getBoolean(key);
  595. }
  596. /**
  597. * Returns a boolean from the defaults table which is associated with
  598. * the key value and the given <code>Locale</code>. If the key is not
  599. * found or the key doesn't represent
  600. * a boolean value then false will be returned.
  601. *
  602. * @param key an <code>Object</code> specifying the key for the desired
  603. * boolean value
  604. * @param l the <code>Locale</code> for which the boolean is desired
  605. * @return the boolean value corresponding to the key
  606. * @since 1.4
  607. */
  608. public static boolean getBoolean(Object key, Locale l) {
  609. return getDefaults().getBoolean(key,l);
  610. }
  611. /**
  612. * Returns an <code>Insets</code> object from the defaults table.
  613. *
  614. * @param key an <code>Object</code> specifying the <code>Insets</code> object
  615. * @return the <code>Insets</code> object
  616. */
  617. public static Insets getInsets(Object key) {
  618. return getDefaults().getInsets(key);
  619. }
  620. /**
  621. * Returns an <code>Insets</code> object from the defaults table that is
  622. * appropriate for the given locale.
  623. *
  624. * @param key an <code>Object</code> specifying the <code>Insets</code> object
  625. * @param l the <code>Locale</code> for which the object is desired
  626. * @return the <code>Insets</code> object
  627. * @since 1.4
  628. */
  629. public static Insets getInsets(Object key, Locale l) {
  630. return getDefaults().getInsets(key,l);
  631. }
  632. /**
  633. * Returns a dimension from the defaults table.
  634. *
  635. * @param key an <code>Object</code> specifying the dimension object
  636. * @return the <code>Dimension</code> object
  637. */
  638. public static Dimension getDimension(Object key) {
  639. return getDefaults().getDimension(key);
  640. }
  641. /**
  642. * Returns a dimension from the defaults table that is appropriate
  643. * for the given locale.
  644. *
  645. * @param key an <code>Object</code> specifying the dimension object
  646. * @param l the <code>Locale</code> for which the object is desired
  647. * @return the <code>Dimension</code> object
  648. * @since 1.4
  649. */
  650. public static Dimension getDimension(Object key, Locale l) {
  651. return getDefaults().getDimension(key,l);
  652. }
  653. /**
  654. * Returns an object from the defaults table.
  655. *
  656. * @param key an <code>Object</code> specifying the desired object
  657. * @return the <code>Object</code>
  658. */
  659. public static Object get(Object key) {
  660. return getDefaults().get(key);
  661. }
  662. /**
  663. * Returns an object from the defaults table that is appropriate for
  664. * the given locale.
  665. *
  666. * @param key an <code>Object</code> specifying the desired object
  667. * @param l the <code>Locale</code> for which the object is desired
  668. * @return the <code>Object</code>
  669. */
  670. public static Object get(Object key, Locale l) {
  671. return getDefaults().get(key,l);
  672. }
  673. /**
  674. * Stores an object in the defaults table.
  675. *
  676. * @param key an <code>Object</code> specifying the retrieval key
  677. * @param value the <code>Object</code> to store
  678. * @return the <code>Object</code> returned by {@link UIDefaults#put}
  679. */
  680. public static Object put(Object key, Object value) {
  681. return getDefaults().put(key, value);
  682. }
  683. /**
  684. * Returns the L&F object that renders the target component.
  685. *
  686. * @param target the <code>JComponent</code> to render
  687. * @return the <code>ComponentUI</code> object that renders the target component
  688. */
  689. public static ComponentUI getUI(JComponent target) {
  690. maybeInitialize();
  691. ComponentUI ui = null;
  692. LookAndFeel multiLAF = getLAFState().multiLookAndFeel;
  693. if (multiLAF != null) {
  694. // This can return null if the multiplexing look and feel
  695. // doesn't support a particular UI.
  696. ui = multiLAF.getDefaults().getUI(target);
  697. }
  698. if (ui == null) {
  699. ui = getDefaults().getUI(target);
  700. }
  701. return ui;
  702. }
  703. /**
  704. * Returns the default values for this look and feel.
  705. *
  706. * @return an <code>UIDefaults</code> object containing the default values
  707. */
  708. public static UIDefaults getLookAndFeelDefaults() {
  709. maybeInitialize();
  710. return getLAFState().getLookAndFeelDefaults();
  711. }
  712. /**
  713. * Finds the Multiplexing <code>LookAndFeel</code>.
  714. */
  715. private static LookAndFeel getMultiLookAndFeel() {
  716. LookAndFeel multiLookAndFeel = getLAFState().multiLookAndFeel;
  717. if (multiLookAndFeel == null) {
  718. String defaultName = "javax.swing.plaf.multi.MultiLookAndFeel";
  719. String className = getLAFState().swingProps.getProperty(multiplexingLAFKey, defaultName);
  720. try {
  721. Class lnfClass = SwingUtilities.loadSystemClass(className);
  722. multiLookAndFeel = (LookAndFeel)lnfClass.newInstance();
  723. } catch (Exception exc) {
  724. System.err.println("UIManager: failed loading " + className);
  725. }
  726. }
  727. return multiLookAndFeel;
  728. }
  729. /**
  730. * Adds a <code>LookAndFeel</code> to the list of auxiliary look and feels.
  731. * The auxiliary look and feels tell the multiplexing look and feel what
  732. * other <code>LookAndFeel</code> classes for a component instance are to be used
  733. * in addition to the default <code>LookAndFeel</code> class when creating a
  734. * multiplexing UI. The change will only take effect when a new
  735. * UI class is created or when the default look and feel is changed
  736. * on a component instance.
  737. * <p>Note these are not the same as the installed look and feels.
  738. *
  739. * @param laf the <code>LookAndFeel</code> object
  740. * @see #removeAuxiliaryLookAndFeel
  741. * @see #setLookAndFeel
  742. * @see #getAuxiliaryLookAndFeels
  743. * @see #getInstalledLookAndFeels
  744. */
  745. static public void addAuxiliaryLookAndFeel(LookAndFeel laf) {
  746. maybeInitialize();
  747. Vector v = getLAFState().auxLookAndFeels;
  748. if (v == null) {
  749. v = new Vector();
  750. }
  751. if (!v.contains(laf)) {
  752. v.addElement(laf);
  753. laf.initialize();
  754. getLAFState().auxLookAndFeels = v;
  755. if (getLAFState().multiLookAndFeel == null) {
  756. getLAFState().multiLookAndFeel = getMultiLookAndFeel();
  757. }
  758. }
  759. }
  760. /**
  761. * Removes a <code>LookAndFeel</code> from the list of auxiliary look and feels.
  762. * The auxiliary look and feels tell the multiplexing look and feel what
  763. * other <code>LookAndFeel</code> classes for a component instance are to be used
  764. * in addition to the default <code>LookAndFeel</code> class when creating a
  765. * multiplexing UI. The change will only take effect when a new
  766. * UI class is created or when the default look and feel is changed
  767. * on a component instance.
  768. * <p>Note these are not the same as the installed look and feels.
  769. * @return true if the <code>LookAndFeel</code> was removed from the list
  770. * @see #removeAuxiliaryLookAndFeel
  771. * @see #getAuxiliaryLookAndFeels
  772. * @see #setLookAndFeel
  773. * @see #getInstalledLookAndFeels
  774. */
  775. static public boolean removeAuxiliaryLookAndFeel(LookAndFeel laf) {
  776. maybeInitialize();
  777. boolean result;
  778. Vector v = getLAFState().auxLookAndFeels;
  779. if ((v == null) || (v.size() == 0)) {
  780. return false;
  781. }
  782. result = v.removeElement(laf);
  783. if (result) {
  784. if (v.size() == 0) {
  785. getLAFState().auxLookAndFeels = null;
  786. getLAFState().multiLookAndFeel = null;
  787. } else {
  788. getLAFState().auxLookAndFeels = v;
  789. }
  790. }
  791. laf.uninitialize();
  792. return result;
  793. }
  794. /**
  795. * Returns the list of auxiliary look and feels (can be <code>null</code>).
  796. * The auxiliary look and feels tell the multiplexing look and feel what
  797. * other <code>LookAndFeel</code> classes for a component instance are
  798. * to be used in addition to the default LookAndFeel class when creating a
  799. * multiplexing UI.
  800. * <p>Note these are not the same as the installed look and feels.
  801. *
  802. * @return list of auxiliary <code>LookAndFeel</code>s or <code>null</code>
  803. * @see #addAuxiliaryLookAndFeel
  804. * @see #removeAuxiliaryLookAndFeel
  805. * @see #setLookAndFeel
  806. * @see #getInstalledLookAndFeels
  807. */
  808. static public LookAndFeel[] getAuxiliaryLookAndFeels() {
  809. maybeInitialize();
  810. Vector v = getLAFState().auxLookAndFeels;
  811. if ((v == null) || (v.size() == 0)) {
  812. return null;
  813. }
  814. else {
  815. LookAndFeel[] rv = new LookAndFeel[v.size()];
  816. for (int i = 0; i < rv.length; i++) {
  817. rv[i] = (LookAndFeel)v.elementAt(i);
  818. }
  819. return rv;
  820. }
  821. }
  822. /**
  823. * Adds a <code>PropertyChangeListener</code> to the listener list.
  824. * The listener is registered for all properties.
  825. *
  826. * @param listener the <code>PropertyChangeListener</code> to be added
  827. * @see java.beans.PropertyChangeSupport
  828. */
  829. public static void addPropertyChangeListener(PropertyChangeListener listener)
  830. {
  831. synchronized (classLock) {
  832. getLAFState().changeSupport.addPropertyChangeListener(listener);
  833. }
  834. }
  835. /**
  836. * Removes a <code>PropertyChangeListener</code> from the listener list.
  837. * This removes a <code>PropertyChangeListener</code> that was registered
  838. * for all properties.
  839. *
  840. * @param listener the <code>PropertyChangeListener</code> to be removed
  841. * @see java.beans.PropertyChangeSupport
  842. */
  843. public static void removePropertyChangeListener(PropertyChangeListener listener)
  844. {
  845. synchronized (classLock) {
  846. getLAFState().changeSupport.removePropertyChangeListener(listener);
  847. }
  848. }
  849. /**
  850. * Returns an array of all the <code>PropertyChangeListener</code>s added
  851. * to this UIManager with addPropertyChangeListener().
  852. *
  853. * @return all of the <code>PropertyChangeListener</code>s added or an empty
  854. * array if no listeners have been added
  855. * @since 1.4
  856. */
  857. public static PropertyChangeListener[] getPropertyChangeListeners() {
  858. synchronized(classLock) {
  859. return getLAFState().changeSupport.getPropertyChangeListeners();
  860. }
  861. }
  862. private static Properties loadSwingProperties()
  863. {
  864. /* Don't bother checking for Swing properties if untrusted, as
  865. * there's no way to look them up without triggering SecurityExceptions.
  866. */
  867. if (UIManager.class.getClassLoader() != null) {
  868. return new Properties();
  869. }
  870. else {
  871. final Properties props = new Properties();
  872. SwingUtilities.doPrivileged(new Runnable() {
  873. public void run() {
  874. try {
  875. File file = new File(makeSwingPropertiesFilename());
  876. if (file.exists()) {
  877. // InputStream has been buffered in Properties
  878. // class
  879. FileInputStream ins = new FileInputStream(file);
  880. props.load(ins);
  881. ins.close();
  882. }
  883. }
  884. catch (Exception e) {
  885. // No such file, or file is otherwise non-readable.
  886. }
  887. // Check whether any properties were overridden at the
  888. // command line.
  889. checkProperty(props, defaultLAFKey);
  890. checkProperty(props, auxiliaryLAFsKey);
  891. checkProperty(props, multiplexingLAFKey);
  892. checkProperty(props, installedLAFsKey);
  893. checkProperty(props, disableMnemonicKey);
  894. }
  895. });
  896. return props;
  897. }
  898. }
  899. private static void checkProperty(Properties props, String key) {
  900. try {
  901. String value = System.getProperty(key);
  902. if (value != null) {
  903. props.put(key, value);
  904. }
  905. } catch (SecurityException e) {
  906. // If system won't give us a property, we don't want it!
  907. }
  908. }
  909. /**
  910. * If a swing.properties file exist and it has a swing.installedlafs property
  911. * then initialize the <code>installedLAFs</code> field.
  912. *
  913. * @see #getInstalledLookAndFeels
  914. */
  915. private static void initializeInstalledLAFs(Properties swingProps)
  916. {
  917. String ilafsString = swingProps.getProperty(installedLAFsKey);
  918. if (ilafsString == null) {
  919. return;
  920. }
  921. /* Create a vector that contains the value of the swing.installedlafs
  922. * property. For example given "swing.installedlafs=motif,windows"
  923. * lafs = {"motif", "windows"}.
  924. */
  925. Vector lafs = new Vector();
  926. StringTokenizer st = new StringTokenizer(ilafsString, ",", false);
  927. while (st.hasMoreTokens()) {
  928. lafs.addElement(st.nextToken());
  929. }
  930. /* Look up the name and class for each name in the "swing.installedlafs"
  931. * list. If they both exist then add a LookAndFeelInfo to
  932. * the installedLafs array.
  933. */
  934. Vector ilafs = new Vector(lafs.size());
  935. for(int i = 0; i < lafs.size(); i++) {
  936. String laf = (String)lafs.elementAt(i);
  937. String name = swingProps.getProperty(makeInstalledLAFKey(laf, "name"), laf);
  938. String cls = swingProps.getProperty(makeInstalledLAFKey(laf, "class"));
  939. if (cls != null) {
  940. ilafs.addElement(new LookAndFeelInfo(name, cls));
  941. }
  942. }
  943. installedLAFs = new LookAndFeelInfo[ilafs.size()];
  944. for(int i = 0; i < ilafs.size(); i++) {
  945. installedLAFs[i] = (LookAndFeelInfo)(ilafs.elementAt(i));
  946. }
  947. }
  948. /**
  949. * If the user has specified a default look and feel, use that.
  950. * Otherwise use the look and feel that's native to this platform.
  951. * If this code is called after the application has explicitly
  952. * set it's look and feel, do nothing.
  953. *
  954. * @see #maybeInitialize
  955. */
  956. private static void initializeDefaultLAF(Properties swingProps)
  957. {
  958. if (getLAFState().lookAndFeel != null) {
  959. return;
  960. }
  961. String metalLnf = getCrossPlatformLookAndFeelClassName();
  962. String lnfDefault = metalLnf;
  963. String lnfName = "<undefined>" ;
  964. try {
  965. lnfName = swingProps.getProperty(defaultLAFKey, lnfDefault);
  966. setLookAndFeel(lnfName);
  967. } catch (Exception e) {
  968. try {
  969. lnfName = swingProps.getProperty(defaultLAFKey, metalLnf);
  970. setLookAndFeel(lnfName);
  971. } catch (Exception e2) {
  972. throw new Error("can't load " + lnfName);
  973. }
  974. }
  975. }
  976. private static void initializeAuxiliaryLAFs(Properties swingProps)
  977. {
  978. String auxLookAndFeelNames = swingProps.getProperty(auxiliaryLAFsKey);
  979. if (auxLookAndFeelNames == null) {
  980. return;
  981. }
  982. Vector auxLookAndFeels = new Vector();
  983. StringTokenizer p = new StringTokenizer(auxLookAndFeelNames,",");
  984. String factoryName;
  985. /* Try to load each LookAndFeel subclass in the list.
  986. */
  987. while (p.hasMoreTokens()) {
  988. String className = p.nextToken();
  989. try {
  990. Class lnfClass = SwingUtilities.loadSystemClass(className);
  991. LookAndFeel newLAF = (LookAndFeel)lnfClass.newInstance();
  992. newLAF.initialize();
  993. auxLookAndFeels.addElement(newLAF);
  994. }
  995. catch (Exception e) {
  996. System.err.println("UIManager: failed loading auxiliary look and feel " + className);
  997. }
  998. }
  999. /* If there were problems and no auxiliary look and feels were
  1000. * loaded, make sure we reset auxLookAndFeels to null.
  1001. * Otherwise, we are going to use the MultiLookAndFeel to get
  1002. * all component UI's, so we need to load it now.
  1003. */
  1004. if (auxLookAndFeels.size() == 0) {
  1005. auxLookAndFeels = null;
  1006. }
  1007. else {
  1008. getLAFState().multiLookAndFeel = getMultiLookAndFeel();
  1009. if (getLAFState().multiLookAndFeel == null) {
  1010. auxLookAndFeels = null;
  1011. }
  1012. }
  1013. getLAFState().auxLookAndFeels = auxLookAndFeels;
  1014. }
  1015. private static void initializeSystemDefaults(Properties swingProps) {
  1016. Object defaults[] = {
  1017. "FocusManagerClassName", "javax.swing.DefaultFocusManager"
  1018. };
  1019. getLAFState().setSystemDefaults(new UIDefaults(defaults));
  1020. getLAFState().swingProps = swingProps;
  1021. }
  1022. /*
  1023. * This method is called before any code that depends on the
  1024. * <code>AppContext</code> specific LAFState object runs. When the AppContext
  1025. * corresponds to a set of applets it's possible for this method
  1026. * to be re-entered, which is why we grab a lock before calling
  1027. * initialize().
  1028. */
  1029. private static void maybeInitialize() {
  1030. synchronized (classLock) {
  1031. if (!getLAFState().initialized) {
  1032. getLAFState().initialized = true;
  1033. initialize();
  1034. }
  1035. }
  1036. }
  1037. /*
  1038. * Only called by maybeInitialize().
  1039. */
  1040. private static void initialize() {
  1041. Properties swingProps = loadSwingProperties();
  1042. try {
  1043. // We discourage the JIT during UI initialization.
  1044. // JITing here tends to be counter-productive.
  1045. java.lang.Compiler.disable();
  1046. initializeSystemDefaults(swingProps);
  1047. initializeDefaultLAF(swingProps);
  1048. initializeAuxiliaryLAFs(swingProps);
  1049. initializeInstalledLAFs(swingProps);
  1050. }
  1051. finally {
  1052. // Make sure to always re-enable the JIT.
  1053. java.lang.Compiler.enable();
  1054. }
  1055. // Enable the Swing default LayoutManager.
  1056. if (FocusManager.isFocusManagerEnabled()) {
  1057. KeyboardFocusManager.getCurrentKeyboardFocusManager().
  1058. setDefaultFocusTraversalPolicy(
  1059. new LayoutFocusTraversalPolicy());
  1060. }
  1061. // Install a hook that will be invoked if no one consumes the
  1062. // KeyEvent. If the source isn't a JComponent this will process
  1063. // key bindings, if the source is a JComponent it implies that
  1064. // processKeyEvent was already invoked and thus no need to process
  1065. // the bindings again, unless the Component is disabled, in which
  1066. // case KeyEvents will no longer be dispatched to it so that we
  1067. // handle it here.
  1068. KeyboardFocusManager.getCurrentKeyboardFocusManager().
  1069. addKeyEventPostProcessor(new KeyEventPostProcessor() {
  1070. public boolean postProcessKeyEvent(KeyEvent e) {
  1071. Component c = e.getComponent();
  1072. if ((!(c instanceof JComponent) ||
  1073. (c != null && !((JComponent)c).isEnabled())) &&
  1074. JComponent.KeyboardState.shouldProcess(e) &&
  1075. SwingUtilities.processKeyBindings(e)) {
  1076. e.consume();
  1077. return true;
  1078. }
  1079. return false;
  1080. }
  1081. });
  1082. }
  1083. }