1 /* 2 * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.java.swing.plaf.windows; 26 27 import java.awt.*; 28 import java.beans.*; 29 import java.lang.ref.*; 30 import javax.swing.*; 31 import javax.swing.plaf.*; 32 33 /** 34 * Wrapper for a value from the desktop. The value is lazily looked up, and 35 * can be accessed using the <code>UIManager.ActiveValue</code> method 36 * <code>createValue</code>. If the underlying desktop property changes this 37 * will force the UIs to update all known Frames. You can invoke 38 * <code>invalidate</code> to force the value to be fetched again. 39 * 40 */ 41 // NOTE: Don't rely on this class staying in this location. It is likely 42 // to move to a different package in the future. 43 public class DesktopProperty implements UIDefaults.ActiveValue { 44 /** 45 * Indicates if an updateUI call is pending. 46 */ 47 private static boolean updatePending; 48 49 /** 50 * ReferenceQueue of unreferenced WeakPCLs. 51 */ 52 private static final ReferenceQueue<DesktopProperty> queue = new ReferenceQueue<DesktopProperty>(); 53 54 /** 55 * PropertyChangeListener attached to the Toolkit. 56 */ 57 private WeakPCL pcl; 58 /** 59 * Key used to lookup value from desktop. 60 */ 61 private final String key; 62 /** 63 * Value to return. 64 */ 65 private Object value; 66 /** 67 * Fallback value in case we get null from desktop. 68 */ 69 private final Object fallback; 70 71 72 /** 73 * Cleans up any lingering state held by unrefeernced 74 * DesktopProperties. 75 */ 76 static void flushUnreferencedProperties() { 77 WeakPCL pcl; 78 79 while ((pcl = (WeakPCL)queue.poll()) != null) { 80 pcl.dispose(); 81 } 82 } 83 84 85 /** 86 * Sets whether or not an updateUI call is pending. 87 */ 88 private static synchronized void setUpdatePending(boolean update) { 89 updatePending = update; 90 } 91 92 /** 93 * Returns true if a UI update is pending. 94 */ 95 private static synchronized boolean isUpdatePending() { 96 return updatePending; 97 } 98 99 /** 100 * Updates the UIs of all the known Frames. 101 */ 102 private static void updateAllUIs() { 103 // Check if the current UI is WindowsLookAndfeel and flush the XP style map. 104 // Note: Change the package test if this class is moved to a different package. 105 Class uiClass = UIManager.getLookAndFeel().getClass(); 106 if (uiClass.getPackage().equals(DesktopProperty.class.getPackage())) { 107 XPStyle.invalidateStyle(); 108 } 109 Frame appFrames[] = Frame.getFrames(); 110 for (Frame appFrame : appFrames) { 111 updateWindowUI(appFrame); 112 } 113 } 114 115 /** 116 * Updates the UI of the passed in window and all its children. 117 */ 118 private static void updateWindowUI(Window window) { 119 SwingUtilities.updateComponentTreeUI(window); 120 Window ownedWins[] = window.getOwnedWindows(); 121 for (Window ownedWin : ownedWins) { 122 updateWindowUI(ownedWin); 123 } 124 } 125 126 127 /** 128 * Creates a DesktopProperty. 129 * 130 * @param key Key used in looking up desktop value. 131 * @param fallback Value used if desktop property is null. 132 */ 133 public DesktopProperty(String key, Object fallback) { 134 this.key = key; 135 this.fallback = fallback; 136 // The only sure fire way to clear our references is to create a 137 // Thread and wait for a reference to be added to the queue. 138 // Because it is so rare that you will actually change the look 139 // and feel, this stepped is forgoed and a middle ground of 140 // flushing references from the constructor is instead done. 141 // The implication is that once one DesktopProperty is created 142 // there will most likely be n (number of DesktopProperties created 143 // by the LookAndFeel) WeakPCLs around, but this number will not 144 // grow past n. 145 flushUnreferencedProperties(); 146 } 147 148 /** 149 * UIManager.LazyValue method, returns the value from the desktop 150 * or the fallback value if the desktop value is null. 151 */ 152 public Object createValue(UIDefaults table) { 153 if (value == null) { 154 value = configureValue(getValueFromDesktop()); 155 if (value == null) { 156 value = configureValue(getDefaultValue()); 157 } 158 } 159 return value; 160 } 161 162 /** 163 * Returns the value from the desktop. 164 */ 165 protected Object getValueFromDesktop() { 166 Toolkit toolkit = Toolkit.getDefaultToolkit(); 167 168 if (pcl == null) { 169 pcl = new WeakPCL(this, getKey(), UIManager.getLookAndFeel()); 170 toolkit.addPropertyChangeListener(getKey(), pcl); 171 } 172 173 return toolkit.getDesktopProperty(getKey()); 174 } 175 176 /** 177 * Returns the value to use if the desktop property is null. 178 */ 179 protected Object getDefaultValue() { 180 return fallback; 181 } 182 183 /** 184 * Invalidates the current value. 185 * 186 * @param laf the LookAndFeel this DesktopProperty was created with 187 */ 188 public void invalidate(LookAndFeel laf) { 189 invalidate(); 190 } 191 192 /** 193 * Invalides the current value so that the next invocation of 194 * <code>createValue</code> will ask for the property again. 195 */ 196 public void invalidate() { 197 value = null; 198 } 199 200 /** 201 * Requests that all components in the GUI hierarchy be updated 202 * to reflect dynamic changes in this look&feel. This update occurs 203 * by uninstalling and re-installing the UI objects. Requests are 204 * batched and collapsed into a single update pass because often 205 * many desktop properties will change at once. 206 */ 207 protected void updateUI() { 208 if (!isUpdatePending()) { 209 setUpdatePending(true); 210 Runnable uiUpdater = new Runnable() { 211 public void run() { 212 updateAllUIs(); 213 setUpdatePending(false); 214 } 215 }; 216 SwingUtilities.invokeLater(uiUpdater); 217 } 218 } 219 220 /** 221 * Configures the value as appropriate for a defaults property in 222 * the UIDefaults table. 223 */ 224 protected Object configureValue(Object value) { 225 if (value != null) { 226 if (value instanceof Color) { 227 return new ColorUIResource((Color)value); 228 } 229 else if (value instanceof Font) { 230 return new FontUIResource((Font)value); 231 } 232 else if (value instanceof UIDefaults.LazyValue) { 233 value = ((UIDefaults.LazyValue)value).createValue(null); 234 } 235 else if (value instanceof UIDefaults.ActiveValue) { 236 value = ((UIDefaults.ActiveValue)value).createValue(null); 237 } 238 } 239 return value; 240 } 241 242 /** 243 * Returns the key used to lookup the desktop properties value. 244 */ 245 protected String getKey() { 246 return key; 247 } 248 249 250 251 /** 252 * As there is typically only one Toolkit, the PropertyChangeListener 253 * is handled via a WeakReference so as not to pin down the 254 * DesktopProperty. 255 */ 256 private static class WeakPCL extends WeakReference<DesktopProperty> 257 implements PropertyChangeListener { 258 private String key; 259 private LookAndFeel laf; 260 261 WeakPCL(DesktopProperty target, String key, LookAndFeel laf) { 262 super(target, queue); 263 this.key = key; 264 this.laf = laf; 265 } 266 267 public void propertyChange(PropertyChangeEvent pce) { 268 DesktopProperty property = get(); 269 270 if (property == null || laf != UIManager.getLookAndFeel()) { 271 // The property was GC'ed, we're no longer interested in 272 // PropertyChanges, remove the listener. 273 dispose(); 274 } 275 else { 276 property.invalidate(laf); 277 property.updateUI(); 278 } 279 } 280 281 void dispose() { 282 Toolkit.getDefaultToolkit().removePropertyChangeListener(key, this); 283 } 284 } 285 }