1. /*
  2. * @(#)UIManager.java 1.84 00/03/06
  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.Container;
  12. import java.awt.Window;
  13. import java.awt.Font;
  14. import java.awt.Color;
  15. import java.awt.Insets;
  16. import java.awt.Dimension;
  17. import javax.swing.plaf.ComponentUI;
  18. import javax.swing.border.Border;
  19. import javax.swing.event.SwingPropertyChangeSupport;
  20. import java.beans.PropertyChangeListener;
  21. import java.beans.PropertyChangeEvent;
  22. import java.io.FileOutputStream;
  23. import java.io.IOException;
  24. import java.io.ObjectOutputStream;
  25. import java.io.ObjectInputStream;
  26. import java.io.Serializable;
  27. import java.io.File;
  28. import java.io.FileInputStream;
  29. import java.io.BufferedInputStream;
  30. import java.util.Enumeration;
  31. import java.util.Hashtable;
  32. import java.util.Properties;
  33. import java.util.StringTokenizer;
  34. import java.util.Vector;
  35. /**
  36. * This class keeps track of the current look and feel and its
  37. * defaults.
  38. * <p>
  39. * We manage three levels of defaults: user defaults, look
  40. * and feel defaults, system defaults. A call to UIManager.get()
  41. * checks all three levels in order and returns the first non-null
  42. * value for a key, if any. A call to UIManager.put() just
  43. * affects the user defaults. Note that a call to
  44. * setLookAndFeel() doesn't affect the user defaults, it just
  45. * replaces the middle defaults "level".
  46. * <p>
  47. * <strong>Warning:</strong>
  48. * Serialized objects of this class will not be compatible with
  49. * future Swing releases. The current serialization support is appropriate
  50. * for short term storage or RMI between applications running the same
  51. * version of Swing. A future release of Swing will provide support for
  52. * long term persistence.
  53. *
  54. * @version 1.84 03/06/00
  55. * @author Thomas Ball
  56. * @author Hans Muller
  57. */
  58. public class UIManager implements Serializable
  59. {
  60. /**
  61. * This class defines the state managed by the UIManager. For
  62. * Swing applications the fields in this class could just as well
  63. * be static members of UIManager however we give them "AppContext"
  64. * scope instead so that applets (and potentially multiple lightweight
  65. * applications running in a single VM) have their own state. For
  66. * example an applet can it's look and feel, see setLookAndFeel().
  67. * Doing so has no affect on other applets (or the browser).
  68. */
  69. private static class LAFState
  70. {
  71. Properties swingProps;
  72. private UIDefaults[] tables = new UIDefaults[2];
  73. boolean initialized = false;
  74. MultiUIDefaults multiUIDefaults = new MultiUIDefaults(tables);
  75. LookAndFeel lookAndFeel;
  76. LookAndFeel multiLookAndFeel = null;
  77. Vector auxLookAndFeels = null;
  78. SwingPropertyChangeSupport changeSupport =
  79. new SwingPropertyChangeSupport(UIManager.class);
  80. UIDefaults getLookAndFeelDefaults() { return tables[0]; }
  81. void setLookAndFeelDefaults(UIDefaults x) { tables[0] = x; }
  82. UIDefaults getSystemDefaults() { return tables[1]; }
  83. void setSystemDefaults(UIDefaults x) { tables[1] = x; }
  84. }
  85. /**
  86. * The AppContext key for our one LAFState instance.
  87. */
  88. private static final Object lafStateACKey = new StringBuffer("LookAndFeel State");
  89. /* Lock object used in place of class object for synchronization. (4187686)
  90. */
  91. private static final Object classLock = new Object();
  92. /* Cache the last referenced LAFState to improve performance
  93. * when accessing it. The cache is based on last thread rather
  94. * than last AppContext because of the cost of looking up the
  95. * AppContext each time. Since most Swing UI work is on the
  96. * EventDispatchThread, this hits often enough to justify the
  97. * overhead. (4193032)
  98. */
  99. private static Thread currentLAFStateThread = null;
  100. private static LAFState currentLAFState = null;
  101. /**
  102. * Return the LAFState object, lazily create one if neccessary. All access
  103. * to the LAFState fields is done via this method, for example:
  104. * <pre>
  105. * getLAFState().initialized = true;
  106. * </pre>
  107. */
  108. private static LAFState getLAFState() {
  109. // First check whether we're running on the same thread as
  110. // the last request.
  111. Thread thisThread = Thread.currentThread();
  112. if (thisThread == currentLAFStateThread) {
  113. return currentLAFState;
  114. }
  115. LAFState rv = (LAFState)SwingUtilities.appContextGet(lafStateACKey);
  116. if (rv == null) {
  117. synchronized (classLock) {
  118. rv = (LAFState)SwingUtilities.appContextGet(lafStateACKey);
  119. if (rv == null) {
  120. SwingUtilities.appContextPut(lafStateACKey,
  121. (rv = new LAFState()));
  122. }
  123. }
  124. }
  125. currentLAFStateThread = thisThread;
  126. currentLAFState = rv;
  127. return rv;
  128. }
  129. /* Keys used for the properties file in <java.home>/lib/swing.properties.
  130. * See loadUserProperties(), initialize().
  131. */
  132. private static final String defaultLAFKey = "swing.defaultlaf";
  133. private static final String auxiliaryLAFsKey = "swing.auxiliarylaf";
  134. private static final String multiplexingLAFKey = "swing.plaf.multiplexinglaf";
  135. private static final String installedLAFsKey = "swing.installedlafs";
  136. /**
  137. * Return a swing.properties file key for the attribute of specified
  138. * look and feel. The attr is either "name" or "class", a typical
  139. * key would be: "swing.installedlaf.windows.name"
  140. */
  141. private static String makeInstalledLAFKey(String laf, String attr) {
  142. return "swing.installedlaf." + laf + "." + attr;
  143. }
  144. /**
  145. * The filename for swing.properties is a path like this (Unix version):
  146. * <java.home>/lib/swing.properties. This method returns a bogus
  147. * filename if java.home isn't defined.
  148. */
  149. private static String makeSwingPropertiesFilename() {
  150. final String homeDir[] = new String[]{"<java.home undefined>"};
  151. SwingUtilities.doPrivileged(new Runnable() {
  152. public void run() {
  153. homeDir[0] = System.getProperty("java.home", "<java.home undefined>");
  154. }
  155. });
  156. String sep = File.separator;
  157. return homeDir[0] + sep + "lib" + sep + "swing.properties";
  158. }
  159. /**
  160. * Provide a little information about an installed LookAndFeel
  161. * for the sake of configuring a menu or for initial application
  162. * set up.
  163. *
  164. * @see UIManager#getInstalledLookAndFeels
  165. * @see LookAndFeel
  166. */
  167. public static class LookAndFeelInfo {
  168. private String name;
  169. private String className;
  170. /**
  171. * Constructs an UIManager$LookAndFeelInfo object.
  172. *
  173. * @param name a String specifying the name of the look and feel
  174. * @param className a String specifiying the name of the class that
  175. * implements the look and feel
  176. */
  177. public LookAndFeelInfo(String name, String className) {
  178. this.name = name;
  179. this.className = className;
  180. }
  181. /**
  182. * Returns the name of the look and feel in a form suitable
  183. * for a menu or other presentation
  184. * @return a String containing the name
  185. * @see LookAndFeel#getName
  186. */
  187. public String getName() {
  188. return name;
  189. }
  190. /**
  191. * Returns the name of the class that implements this look and feel.
  192. * @return the name of the class that implements this LookAndFeel
  193. * @see LookAndFeel
  194. */
  195. public String getClassName() {
  196. return className;
  197. }
  198. /**
  199. * Returns a string that displays and identifies this
  200. * object's properties.
  201. *
  202. * @return a String representation of this object
  203. */
  204. public String toString() {
  205. return getClass().getName() + "[" + getName() + " " + getClassName() + "]";
  206. }
  207. }
  208. /**
  209. * The default value of installedLAFS is used when no swing.properties
  210. * file is available or if the file doesn't contain a "swing.installedlafs"
  211. * property.
  212. *
  213. * @see #initializeInstalledLAFs
  214. */
  215. private static LookAndFeelInfo[] installedLAFs = {
  216. new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"),
  217. new LookAndFeelInfo("CDE/Motif", "com.sun.java.swing.plaf.motif.MotifLookAndFeel"),
  218. new LookAndFeelInfo("Windows", "com.sun.java.swing.plaf.windows.WindowsLookAndFeel")
  219. };
  220. /**
  221. * Return an array of objects that provide some information about the
  222. * LookAndFeel implementations that have been installed with this
  223. * software development kit. The LookAndFeel info objects can be used
  224. * by an application to construct a menu of look and feel options for
  225. * the user or to set the look and feel at start up time. Note that
  226. * we do not return the LookAndFeel classes themselves here to avoid the
  227. * cost of unnecessarily loading them.
  228. * <p>
  229. * Given a LookAndFeelInfo object one can set the current look and feel
  230. * like this:
  231. * <pre>
  232. * UIManager.setLookAndFeel(info.getClassName());
  233. * </pre>
  234. *
  235. * @see #setLookAndFeel
  236. */
  237. public static LookAndFeelInfo[] getInstalledLookAndFeels() {
  238. maybeInitialize();
  239. LookAndFeelInfo[] ilafs = installedLAFs;
  240. LookAndFeelInfo[] rv = new LookAndFeelInfo[ilafs.length];
  241. System.arraycopy(ilafs, 0, rv, 0, ilafs.length);
  242. return rv;
  243. }
  244. /**
  245. * Replaces the current array of installed LookAndFeelInfos.
  246. *
  247. * @see #getInstalledLookAndFeels
  248. */
  249. public static void setInstalledLookAndFeels(LookAndFeelInfo[] infos)
  250. throws SecurityException
  251. {
  252. LookAndFeelInfo[] newInfos = new LookAndFeelInfo[infos.length];
  253. System.arraycopy(infos, 0, newInfos, 0, infos.length);
  254. installedLAFs = newInfos;
  255. }
  256. /**
  257. * Adds the specified look and feel to the current array and
  258. * then calls {@link #setInstalledLookAndFeels}.
  259. * @param info a LookAndFeelInfo object that names the look and feel
  260. * and identifies that class that implements it
  261. */
  262. public static void installLookAndFeel(LookAndFeelInfo info) {
  263. LookAndFeelInfo[] infos = getInstalledLookAndFeels();
  264. LookAndFeelInfo[] newInfos = new LookAndFeelInfo[infos.length + 1];
  265. System.arraycopy(infos, 0, newInfos, 0, infos.length);
  266. newInfos[infos.length] = info;
  267. setInstalledLookAndFeels(newInfos);
  268. }
  269. /**
  270. * Creates a new look and feel and adds it to the current array.
  271. * Then calls {@link #setInstalledLookAndFeels}.
  272. *
  273. * @param name a String specifying the name of the look and feel
  274. * @param className a String specifying the class name that implements the
  275. * look and feel
  276. */
  277. public static void installLookAndFeel(String name, String className) {
  278. installLookAndFeel(new LookAndFeelInfo(name, className));
  279. }
  280. /**
  281. * Returns The current default look and feel, or null.
  282. *
  283. * @return The current default look and feel, or null.
  284. * @see #setLookAndFeel
  285. */
  286. public static LookAndFeel getLookAndFeel() {
  287. maybeInitialize();
  288. return getLAFState().lookAndFeel;
  289. }
  290. /**
  291. * Set the current default look and feel using a LookAndFeel object.
  292. * <p>
  293. * This is a JavaBeans bound property.
  294. *
  295. * @param newLookAndFeel the LookAndFeel object
  296. * @exception UnsupportedLookAndFeelException If <code>lnf.isSupportedLookAndFeel()</code> is false.
  297. * @see #getLookAndFeel
  298. */
  299. public static void setLookAndFeel(LookAndFeel newLookAndFeel)
  300. throws UnsupportedLookAndFeelException
  301. {
  302. if ((newLookAndFeel != null) && !newLookAndFeel.isSupportedLookAndFeel()) {
  303. String s = newLookAndFeel.toString() + " not supported on this platform";
  304. throw new UnsupportedLookAndFeelException(s);
  305. }
  306. LookAndFeel oldLookAndFeel = getLAFState().lookAndFeel;
  307. if (oldLookAndFeel != null) {
  308. oldLookAndFeel.uninitialize();
  309. }
  310. getLAFState().lookAndFeel = newLookAndFeel;
  311. if (newLookAndFeel != null) {
  312. newLookAndFeel.initialize();
  313. getLAFState().setLookAndFeelDefaults(newLookAndFeel.getDefaults());
  314. }
  315. else {
  316. getLAFState().setLookAndFeelDefaults(null);
  317. }
  318. getLAFState().changeSupport.firePropertyChange("lookAndFeel", oldLookAndFeel, newLookAndFeel);
  319. }
  320. /**
  321. * Set the current default look and feel using a class name.
  322. *
  323. * @param className a string specifying the name of the class that implements
  324. * the look and feel
  325. * @exception ClassNotFoundException If the LookAndFeel class could not be found.
  326. * @exception InstantiationException If a new instance of the class couldn't be creatd.
  327. * @exception IllegalAccessException If the class or initializer isn't accessible.
  328. * @exception UnsupportedLookAndFeelException If <code>lnf.isSupportedLookAndFeel()</code> is false.
  329. */
  330. public static void setLookAndFeel(String className)
  331. throws ClassNotFoundException,
  332. InstantiationException,
  333. IllegalAccessException,
  334. UnsupportedLookAndFeelException
  335. {
  336. Class lnfClass = SwingUtilities.loadSystemClass(className);
  337. setLookAndFeel((LookAndFeel)(lnfClass.newInstance()));
  338. }
  339. /**
  340. * Returns the name of the LookAndFeel class that implements
  341. * the native systems look and feel if there is one,
  342. * otherwise the name of the default cross platform LookAndFeel
  343. * class.
  344. *
  345. * @see #setLookAndFeel
  346. * @see #getCrossPlatformLookAndFeelClassName
  347. */
  348. public static String getSystemLookAndFeelClassName() {
  349. final String osName[] = new String[]{""};
  350. SwingUtilities.doPrivileged(new Runnable() {
  351. public void run() {
  352. osName[0] = System.getProperty("os.name");
  353. }
  354. });
  355. if (osName[0] != null) {
  356. if (osName[0].indexOf("Windows") != -1) {
  357. return "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
  358. }
  359. else if ((osName[0].indexOf("Solaris") != -1) ||
  360. (osName[0].indexOf("SunOS") != -1)) {
  361. return "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
  362. }
  363. else if (osName[0].indexOf("Mac") != -1 ) {
  364. return "com.sun.java.swing.plaf.mac.MacLookAndFeel";
  365. }
  366. }
  367. return getCrossPlatformLookAndFeelClassName();
  368. }
  369. /**
  370. * Returns the name of the LookAndFeel class that implements
  371. * the default cross platform look and feel -- the Java
  372. * Look and Feel (JLF).
  373. *
  374. * @return a string with the JLF implementation-class
  375. * @see #setLookAndFeel
  376. * @see #getSystemLookAndFeelClassName
  377. */
  378. public static String getCrossPlatformLookAndFeelClassName() {
  379. return "javax.swing.plaf.metal.MetalLookAndFeel";
  380. }
  381. /**
  382. * Returns the default values for this look and feel.
  383. *
  384. * @return an UIDefaults object containing the default values
  385. */
  386. public static UIDefaults getDefaults() {
  387. maybeInitialize();
  388. return getLAFState().multiUIDefaults;
  389. }
  390. /**
  391. * Returns a drawing font from the defaults table.
  392. *
  393. * @param key an Object specifying the font
  394. * @return the Font object
  395. */
  396. public static Font getFont(Object key) {
  397. return getDefaults().getFont(key);
  398. }
  399. /**
  400. * Returns a drawing color from the defaults table.
  401. *
  402. * @param key an Object specifying the color
  403. * @return the Color object
  404. */
  405. public static Color getColor(Object key) {
  406. return getDefaults().getColor(key);
  407. }
  408. /**
  409. * Returns an Icon from the defaults table.
  410. *
  411. * @param key an Object specifying the icon
  412. * @return the Icon object
  413. */
  414. public static Icon getIcon(Object key) {
  415. return getDefaults().getIcon(key);
  416. }
  417. /**
  418. * Returns a border from the defaults table.
  419. *
  420. * @param key an Object specifying the border
  421. * @return the Border object
  422. */
  423. public static Border getBorder(Object key) {
  424. return getDefaults().getBorder(key);
  425. }
  426. /**
  427. * Returns a string from the defaults table.
  428. *
  429. * @param key an Object specifying the string
  430. * @return the String
  431. */
  432. public static String getString(Object key) {
  433. return getDefaults().getString(key);
  434. }
  435. /**
  436. * Returns an int from the defaults table.
  437. *
  438. * @param key an Object specifying the int
  439. * @return the int
  440. */
  441. public static int getInt(Object key) {
  442. return getDefaults().getInt(key);
  443. }
  444. /**
  445. * Returns an Insets object from the defaults table.
  446. *
  447. * @param key an Object specifying the Insets object
  448. * @return the Insets object
  449. */
  450. public static Insets getInsets(Object key) {
  451. return getDefaults().getInsets(key);
  452. }
  453. /**
  454. * Returns a dimension from the defaults table.
  455. *
  456. * @param key an Object specifying the dimension object
  457. * @return the Dimension object
  458. */
  459. public static Dimension getDimension(Object key) {
  460. return getDefaults().getDimension(key);
  461. }
  462. /**
  463. * Returns an object from the defaults table.
  464. *
  465. * @param key an Object specifying the desired object
  466. * @return the Object
  467. */
  468. public static Object get(Object key) {
  469. return getDefaults().get(key);
  470. }
  471. /**
  472. * Stores an object in the defaults table.
  473. *
  474. * @param key an Object specifying the retrieval key
  475. * @param value the Object to store
  476. * @return the Object returned by {@link UIDefaults#put}
  477. */
  478. public static Object put(Object key, Object value) {
  479. return getDefaults().put(key, value);
  480. }
  481. /**
  482. * Returns the L&F object that renders the target component.
  483. *
  484. * @param target the JComponent to render
  485. * @return the ComponentUI object that renders the target component
  486. */
  487. public static ComponentUI getUI(JComponent target) {
  488. maybeInitialize();
  489. ComponentUI ui = null;
  490. LookAndFeel multiLAF = getLAFState().multiLookAndFeel;
  491. if (multiLAF != null) {
  492. // This can return null if the multiplexing look and feel
  493. // doesn't support a particular UI.
  494. ui = multiLAF.getDefaults().getUI(target);
  495. }
  496. if (ui == null) {
  497. ui = getDefaults().getUI(target);
  498. }
  499. return ui;
  500. }
  501. /**
  502. * Returns the default values for this look and feel.
  503. *
  504. * @return an UIDefaults object containing the default values
  505. */
  506. public static UIDefaults getLookAndFeelDefaults() {
  507. maybeInitialize();
  508. return getLAFState().getLookAndFeelDefaults();
  509. }
  510. /**
  511. * Find the Multiplexing LookAndFeel.
  512. */
  513. private static LookAndFeel getMultiLookAndFeel() {
  514. LookAndFeel multiLookAndFeel = getLAFState().multiLookAndFeel;
  515. if (multiLookAndFeel == null) {
  516. String defaultName = "javax.swing.plaf.multi.MultiLookAndFeel";
  517. String className = getLAFState().swingProps.getProperty(multiplexingLAFKey, defaultName);
  518. try {
  519. Class lnfClass = SwingUtilities.loadSystemClass(className);
  520. multiLookAndFeel = (LookAndFeel)lnfClass.newInstance();
  521. } catch (Exception exc) {
  522. System.err.println("UIManager: failed loading " + className);
  523. }
  524. }
  525. return multiLookAndFeel;
  526. }
  527. /**
  528. * Add a LookAndFeel to the list of auxiliary look and feels. The
  529. * auxiliary look and feels tell the multiplexing look and feel what
  530. * other LookAndFeel classes for a component instance are to be used
  531. * in addition to the default LookAndFeel class when creating a
  532. * multiplexing UI. The change will only take effect when a new
  533. * UI class is created or when the default look and feel is changed
  534. * on a component instance.
  535. * <p>Note these are not the same as the installed look and feels.
  536. *
  537. * @param laf the LookAndFeel object
  538. * @see #removeAuxiliaryLookAndFeel
  539. * @see #setLookAndFeel
  540. * @see #getAuxiliaryLookAndFeels
  541. * @see #getInstalledLookAndFeels
  542. */
  543. static public void addAuxiliaryLookAndFeel(LookAndFeel laf) {
  544. maybeInitialize();
  545. Vector v = getLAFState().auxLookAndFeels;
  546. if (v == null) {
  547. v = new Vector();
  548. }
  549. if (!v.contains(laf)) {
  550. v.addElement(laf);
  551. laf.initialize();
  552. getLAFState().auxLookAndFeels = v;
  553. if (getLAFState().multiLookAndFeel == null) {
  554. getLAFState().multiLookAndFeel = getMultiLookAndFeel();
  555. }
  556. }
  557. }
  558. /**
  559. * Remove a LookAndFeel from the list of auxiliary look and feels. The
  560. * auxiliary look and feels tell the multiplexing look and feel what
  561. * other LookAndFeel classes for a component instance are to be used
  562. * in addition to the default LookAndFeel class when creating a
  563. * multiplexing UI. The change will only take effect when a new
  564. * UI class is created or when the default look and feel is changed
  565. * on a component instance.
  566. * <p>Note these are not the same as the installed look and feels.
  567. * @return true if the LookAndFeel was removed from the list
  568. * @see #removeAuxiliaryLookAndFeel
  569. * @see #getAuxiliaryLookAndFeels
  570. * @see #setLookAndFeel
  571. * @see #getInstalledLookAndFeels
  572. */
  573. static public boolean removeAuxiliaryLookAndFeel(LookAndFeel laf) {
  574. maybeInitialize();
  575. boolean result;
  576. Vector v = getLAFState().auxLookAndFeels;
  577. if ((v == null) || (v.size() == 0)) {
  578. return false;
  579. }
  580. result = v.removeElement(laf);
  581. if (result) {
  582. if (v.size() == 0) {
  583. getLAFState().auxLookAndFeels = null;
  584. getLAFState().multiLookAndFeel = null;
  585. } else {
  586. getLAFState().auxLookAndFeels = v;
  587. }
  588. }
  589. laf.uninitialize();
  590. return result;
  591. }
  592. /**
  593. * Return the list of auxiliary look and feels (can be null). The
  594. * auxiliary look and feels tell the multiplexing look and feel what
  595. * other LookAndFeel classes for a component instance are to be used
  596. * in addition to the default LookAndFeel class when creating a
  597. * multiplexing UI.
  598. * <p>Note these are not the same as the installed look and feels.
  599. * @see #addAuxiliaryLookAndFeel
  600. * @see #removeAuxiliaryLookAndFeel
  601. * @see #setLookAndFeel
  602. * @see #getInstalledLookAndFeels
  603. */
  604. static public LookAndFeel[] getAuxiliaryLookAndFeels() {
  605. maybeInitialize();
  606. Vector v = getLAFState().auxLookAndFeels;
  607. if ((v == null) || (v.size() == 0)) {
  608. return null;
  609. }
  610. else {
  611. LookAndFeel[] rv = new LookAndFeel[v.size()];
  612. for (int i = 0; i < rv.length; i++) {
  613. rv[i] = (LookAndFeel)v.elementAt(i);
  614. }
  615. return rv;
  616. }
  617. }
  618. /**
  619. * Add a PropertyChangeListener to the listener list.
  620. * The listener is registered for all properties.
  621. *
  622. * @param listener The PropertyChangeListener to be added
  623. * @see java.beans.PropertyChangeSupport
  624. */
  625. public static void addPropertyChangeListener(PropertyChangeListener listener)
  626. {
  627. synchronized (classLock) {
  628. getLAFState().changeSupport.addPropertyChangeListener(listener);
  629. }
  630. }
  631. /**
  632. * Remove a PropertyChangeListener from the listener list.
  633. * This removes a PropertyChangeListener that was registered
  634. * for all properties.
  635. *
  636. * @param listener The PropertyChangeListener to be removed
  637. * @see java.beans.PropertyChangeSupport
  638. */
  639. public static void removePropertyChangeListener(PropertyChangeListener listener)
  640. {
  641. synchronized (classLock) {
  642. getLAFState().changeSupport.removePropertyChangeListener(listener);
  643. }
  644. }
  645. private static Properties loadSwingProperties()
  646. {
  647. /* Don't bother checking for Swing properties if untrusted, as
  648. * there's no way to look them up without triggering SecurityExceptions.
  649. */
  650. if (UIManager.class.getClassLoader() != null) {
  651. return new Properties();
  652. }
  653. else {
  654. final Properties props = new Properties();
  655. SwingUtilities.doPrivileged(new Runnable() {
  656. public void run() {
  657. try {
  658. File file = new File(makeSwingPropertiesFilename());
  659. // InputStream has been buffered in Properties class
  660. FileInputStream ins = new FileInputStream(file);
  661. props.load(ins);
  662. ins.close();
  663. }
  664. catch (Exception e) {
  665. // No such file, or file is otherwise non-readable.
  666. }
  667. // Check whether any properties were overridden at the
  668. // command line.
  669. checkProperty(props, defaultLAFKey);
  670. checkProperty(props, auxiliaryLAFsKey);
  671. checkProperty(props, multiplexingLAFKey);
  672. checkProperty(props, installedLAFsKey);
  673. }
  674. });
  675. return props;
  676. }
  677. }
  678. private static void checkProperty(Properties props, String key) {
  679. try {
  680. String value = System.getProperty(key);
  681. if (value != null) {
  682. props.put(key, value);
  683. }
  684. } catch (SecurityException e) {
  685. // If system won't give us a property, we don't want it!
  686. }
  687. }
  688. /**
  689. * If a swing.properties file exist and it has a swing.installedlafs property
  690. * then initialize the installedLAFs field.
  691. *
  692. * @see #getInstalledLookAndFeels
  693. */
  694. private static void initializeInstalledLAFs(Properties swingProps)
  695. {
  696. String ilafsString = swingProps.getProperty(installedLAFsKey);
  697. if (ilafsString == null) {
  698. return;
  699. }
  700. /* Create a vector that contains the value of the swing.installedlafs
  701. * property. For example given "swing.installedlafs=motif,windows"
  702. * lafs = {"motif", "windows"}.
  703. */
  704. Vector lafs = new Vector();
  705. StringTokenizer st = new StringTokenizer(ilafsString, ",", false);
  706. while (st.hasMoreTokens()) {
  707. lafs.addElement(st.nextToken());
  708. }
  709. /* Look up the name and class for each name in the "swing.installedlafs"
  710. * list. If they both exist then add a LookAndFeelInfo to
  711. * the installedLafs array.
  712. */
  713. Vector ilafs = new Vector(lafs.size());
  714. for(int i = 0; i < lafs.size(); i++) {
  715. String laf = (String)lafs.elementAt(i);
  716. String name = swingProps.getProperty(makeInstalledLAFKey(laf, "name"), laf);
  717. String cls = swingProps.getProperty(makeInstalledLAFKey(laf, "class"));
  718. if (cls != null) {
  719. ilafs.addElement(new LookAndFeelInfo(name, cls));
  720. }
  721. }
  722. installedLAFs = new LookAndFeelInfo[ilafs.size()];
  723. for(int i = 0; i < ilafs.size(); i++) {
  724. installedLAFs[i] = (LookAndFeelInfo)(ilafs.elementAt(i));
  725. }
  726. }
  727. /**
  728. * If the user has specified a default look and feel, use that.
  729. * Otherwise use the look and feel that's native to this platform.
  730. * If this code is called after the application has expclicitly
  731. * set it's look and feel, do nothing.
  732. *
  733. * @see #maybeInitialize
  734. */
  735. private static void initializeDefaultLAF(Properties swingProps)
  736. {
  737. if (getLAFState().lookAndFeel != null) {
  738. return;
  739. }
  740. String metalLnf = getCrossPlatformLookAndFeelClassName();
  741. String lnfDefault = metalLnf;
  742. String lnfName = "<undefined>" ;
  743. try {
  744. lnfName = swingProps.getProperty(defaultLAFKey, lnfDefault);
  745. setLookAndFeel(lnfName);
  746. } catch (Exception e) {
  747. try {
  748. lnfName = swingProps.getProperty(defaultLAFKey, metalLnf);
  749. setLookAndFeel(lnfName);
  750. } catch (Exception e2) {
  751. throw new Error("can't load " + lnfName);
  752. }
  753. }
  754. }
  755. private static void initializeAuxiliaryLAFs(Properties swingProps)
  756. {
  757. String auxLookAndFeelNames = swingProps.getProperty(auxiliaryLAFsKey);
  758. if (auxLookAndFeelNames == null) {
  759. return;
  760. }
  761. Vector auxLookAndFeels = new Vector();
  762. StringTokenizer p = new StringTokenizer(auxLookAndFeelNames,",");
  763. String factoryName;
  764. /* Try to load each LookAndFeel subclass in the list.
  765. */
  766. while (p.hasMoreTokens()) {
  767. String className = p.nextToken();
  768. try {
  769. Class lnfClass = SwingUtilities.loadSystemClass(className);
  770. LookAndFeel newLAF = (LookAndFeel)lnfClass.newInstance();
  771. newLAF.initialize();
  772. auxLookAndFeels.addElement(newLAF);
  773. }
  774. catch (Exception e) {
  775. System.err.println("UIManager: failed loading auxiliary look and feel " + className);
  776. }
  777. }
  778. /* If there were problems and no auxiliary look and feels were
  779. * loaded, make sure we reset auxLookAndFeels to null.
  780. * Otherwise, we are going to use the MultiLookAndFeel to get
  781. * all component UI's, so we need to load it now.
  782. */
  783. if (auxLookAndFeels.size() == 0) {
  784. auxLookAndFeels = null;
  785. }
  786. else {
  787. getLAFState().multiLookAndFeel = getMultiLookAndFeel();
  788. if (getLAFState().multiLookAndFeel == null) {
  789. auxLookAndFeels = null;
  790. }
  791. }
  792. getLAFState().auxLookAndFeels = auxLookAndFeels;
  793. }
  794. private static void initializeSystemDefaults(Properties swingProps) {
  795. Object defaults[] = {
  796. "FocusManagerClassName", "javax.swing.DefaultFocusManager"
  797. };
  798. getLAFState().setSystemDefaults(new UIDefaults(defaults));
  799. getLAFState().swingProps = swingProps;
  800. }
  801. /*
  802. * This method is called before any code that depends on the
  803. * AppContext specific LAFState object runs. When the AppContext
  804. * corresponds to a set of applets it's possible for this method
  805. * to be re-entered, which is why we grab a lock before calling
  806. * initialize().
  807. */
  808. private static void maybeInitialize() {
  809. synchronized (classLock) {
  810. if (!getLAFState().initialized) {
  811. getLAFState().initialized = true;
  812. initialize();
  813. }
  814. }
  815. }
  816. /*
  817. * Only called by maybeInitialize().
  818. */
  819. private static void initialize() {
  820. Properties swingProps = loadSwingProperties();
  821. try {
  822. // We discourage the JIT during UI initialization.
  823. // JITing here tends to be counter-productive.
  824. java.lang.Compiler.disable();
  825. initializeSystemDefaults(swingProps);
  826. initializeDefaultLAF(swingProps);
  827. initializeAuxiliaryLAFs(swingProps);
  828. initializeInstalledLAFs(swingProps);
  829. }
  830. finally {
  831. // Make sure to always re-enable the JIT.
  832. java.lang.Compiler.enable();
  833. }
  834. }
  835. }