1. /*
  2. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package com.sun.java.swing.plaf.windows;
  6. import java.awt.*;
  7. import java.beans.*;
  8. import java.lang.ref.*;
  9. import javax.swing.*;
  10. import javax.swing.plaf.*;
  11. /**
  12. * Wrapper for a value from the desktop. The value is lazily looked up, and
  13. * can be accessed using the <code>UIManager.ActiveValue</code> method
  14. * <code>createValue</code>. If the underlying desktop property changes this
  15. * will force the UIs to update all known Frames. You can invoke
  16. * <code>invalidate</code> to force the value to be fetched again.
  17. *
  18. * @version @(#)DesktopProperty.java 1.7 03/12/19
  19. */
  20. // NOTE: Don't rely on this class staying in this location. It is likely
  21. // to move to a different package in the future.
  22. public class DesktopProperty implements UIDefaults.ActiveValue {
  23. /**
  24. * Indicates if an updateUI call is pending.
  25. */
  26. private static boolean updatePending;
  27. /**
  28. * ReferenceQueue of unreferenced WeakPCLs.
  29. */
  30. private static ReferenceQueue queue;
  31. /**
  32. * PropertyChangeListener attached to the Toolkit.
  33. */
  34. private WeakPCL pcl;
  35. /**
  36. * Key used to lookup value from desktop.
  37. */
  38. private String key;
  39. /**
  40. * Value to return.
  41. */
  42. private Object value;
  43. /**
  44. * Fallback value in case we get null from desktop.
  45. */
  46. private Object fallback;
  47. /**
  48. * Toolkit.
  49. */
  50. private Toolkit toolkit;
  51. static {
  52. queue = new ReferenceQueue();
  53. }
  54. /**
  55. * Cleans up any lingering state held by unrefeernced
  56. * DesktopProperties.
  57. */
  58. static void flushUnreferencedProperties() {
  59. WeakPCL pcl;
  60. while ((pcl = (WeakPCL)queue.poll()) != null) {
  61. pcl.dispose();
  62. }
  63. }
  64. /**
  65. * Sets whether or not an updateUI call is pending.
  66. */
  67. private static synchronized void setUpdatePending(boolean update) {
  68. updatePending = update;
  69. }
  70. /**
  71. * Returns true if a UI update is pending.
  72. */
  73. private static synchronized boolean isUpdatePending() {
  74. return updatePending;
  75. }
  76. /**
  77. * Updates the UIs of all the known Frames.
  78. */
  79. private static void updateAllUIs() {
  80. // Check if the current UI is WindowsLookAndfeel and flush the XP style map.
  81. // Note: Change the package test if this class is moved to a different package.
  82. Class uiClass = UIManager.getLookAndFeel().getClass();
  83. if (uiClass.getPackage().equals(DesktopProperty.class.getPackage())) {
  84. XPStyle.invalidateStyle();
  85. }
  86. Frame appFrames[] = Frame.getFrames();
  87. for (int j=0; j < appFrames.length; j++) {
  88. updateWindowUI(appFrames[j]);
  89. }
  90. }
  91. /**
  92. * Updates the UI of the passed in window and all its children.
  93. */
  94. private static void updateWindowUI(Window window) {
  95. SwingUtilities.updateComponentTreeUI(window);
  96. Window ownedWins[] = window.getOwnedWindows();
  97. for (int i=0; i < ownedWins.length; i++) {
  98. updateWindowUI(ownedWins[i]);
  99. }
  100. }
  101. /**
  102. * Creates a DesktopProperty.
  103. *
  104. * @param key Key used in looking up desktop value.
  105. * @param fallback Value used if desktop property is null.
  106. * @param toolkit Toolkit used to fetch property from, can be null
  107. * in which default will be used.
  108. */
  109. public DesktopProperty(String key, Object fallback, Toolkit toolkit) {
  110. this.key = key;
  111. this.fallback = fallback;
  112. this.toolkit = toolkit;
  113. // The only sure fire way to clear our references is to create a
  114. // Thread and wait for a reference to be added to the queue.
  115. // Because it is so rare that you will actually change the look
  116. // and feel, this stepped is forgoed and a middle ground of
  117. // flushing references from the constructor is instead done.
  118. // The implication is that once one DesktopProperty is created
  119. // there will most likely be n (number of DesktopProperties created
  120. // by the LookAndFeel) WeakPCLs around, but this number will not
  121. // grow past n.
  122. flushUnreferencedProperties();
  123. }
  124. /**
  125. * UIManager.LazyValue method, returns the value from the desktop
  126. * or the fallback value if the desktop value is null.
  127. */
  128. public Object createValue(UIDefaults table) {
  129. if (value == null) {
  130. value = configureValue(getValueFromDesktop());
  131. if (value == null) {
  132. value = configureValue(getDefaultValue());
  133. }
  134. }
  135. return value;
  136. }
  137. /**
  138. * Returns the value from the desktop.
  139. */
  140. protected Object getValueFromDesktop() {
  141. if (this.toolkit == null) {
  142. this.toolkit = Toolkit.getDefaultToolkit();
  143. }
  144. Object value = toolkit.getDesktopProperty(getKey());
  145. pcl = new WeakPCL(this, toolkit, getKey());
  146. toolkit.addPropertyChangeListener(getKey(), pcl);
  147. return value;
  148. }
  149. /**
  150. * Returns the value to use if the desktop property is null.
  151. */
  152. protected Object getDefaultValue() {
  153. return fallback;
  154. }
  155. /**
  156. * Invalides the current value so that the next invocation of
  157. * <code>createValue</code> will ask for the property again.
  158. */
  159. public void invalidate() {
  160. if (pcl != null) {
  161. toolkit.removePropertyChangeListener(getKey(), pcl);
  162. toolkit = null;
  163. pcl = null;
  164. value = null;
  165. }
  166. }
  167. /**
  168. * Requests that all components in the GUI hierarchy be updated
  169. * to reflect dynamic changes in this look&feel. This update occurs
  170. * by uninstalling and re-installing the UI objects. Requests are
  171. * batched and collapsed into a single update pass because often
  172. * many desktop properties will change at once.
  173. */
  174. protected void updateUI() {
  175. if (!isUpdatePending()) {
  176. setUpdatePending(true);
  177. Runnable uiUpdater = new Runnable() {
  178. public void run() {
  179. updateAllUIs();
  180. setUpdatePending(false);
  181. }
  182. };
  183. SwingUtilities.invokeLater(uiUpdater);
  184. }
  185. }
  186. /**
  187. * Configures the value as appropriate for a defaults property in
  188. * the UIDefaults table.
  189. */
  190. protected Object configureValue(Object value) {
  191. if (value != null) {
  192. if (value instanceof Color) {
  193. return new ColorUIResource((Color)value);
  194. }
  195. else if (value instanceof Font) {
  196. return new FontUIResource((Font)value);
  197. }
  198. else if (value instanceof UIDefaults.LazyValue) {
  199. value = ((UIDefaults.LazyValue)value).createValue(null);
  200. }
  201. else if (value instanceof UIDefaults.ActiveValue) {
  202. value = ((UIDefaults.ActiveValue)value).createValue(null);
  203. }
  204. }
  205. return value;
  206. }
  207. /**
  208. * Returns the key used to lookup the desktop properties value.
  209. */
  210. protected String getKey() {
  211. return key;
  212. }
  213. /**
  214. * As there is typically only one Toolkit, the PropertyChangeListener
  215. * is handled via a WeakReference so as not to pin down the
  216. * DesktopProperty.
  217. */
  218. private static class WeakPCL extends WeakReference
  219. implements PropertyChangeListener {
  220. private Toolkit kit;
  221. private String key;
  222. WeakPCL(Object target, Toolkit kit, String key) {
  223. super(target, queue);
  224. this.kit = kit;
  225. this.key = key;
  226. }
  227. public void propertyChange(PropertyChangeEvent pce) {
  228. DesktopProperty property = (DesktopProperty)get();
  229. if (property == null) {
  230. // The property was GC'ed, we're no longer interested in
  231. // PropertyChanges, remove the listener.
  232. dispose();
  233. }
  234. else {
  235. property.invalidate();
  236. property.updateUI();
  237. }
  238. }
  239. void dispose() {
  240. kit.removePropertyChangeListener(key, this);
  241. }
  242. }
  243. }