1 /* 2 * Copyright (c) 2001, 2014, 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 invalidateStyle(); 104 Frame appFrames[] = Frame.getFrames(); 105 for (Frame appFrame : appFrames) { 106 updateWindowUI(appFrame); 107 } 108 } 109 110 /** 111 * Invalidates the current style in WindowsLookAndFeel. 112 */ 113 private static void invalidateStyle() { 114 // Check if the current UI is WindowsLookAndfeel and flush the XP style map. 115 // Note: Change the package test if this class is moved to a different package. 116 Class uiClass = UIManager.getLookAndFeel().getClass(); 117 if (uiClass.getPackage().equals(DesktopProperty.class.getPackage())) { 118 XPStyle.invalidateStyle(); 119 } 120 } 121 122 /** 123 * Updates the UI of the passed in window and all its children. 124 */ 125 private static void updateWindowUI(Window window) { 126 SwingUtilities.updateComponentTreeUI(window); 127 Window ownedWins[] = window.getOwnedWindows(); 128 for (Window ownedWin : ownedWins) { 129 updateWindowUI(ownedWin); 130 } 131 } 132 133 134 /** 135 * Creates a DesktopProperty. 136 * 137 * @param key Key used in looking up desktop value. 138 * @param fallback Value used if desktop property is null. 139 */ 140 public DesktopProperty(String key, Object fallback) { 141 this.key = key; 142 this.fallback = fallback; 143 // The only sure fire way to clear our references is to create a 144 // Thread and wait for a reference to be added to the queue. 145 // Because it is so rare that you will actually change the look 146 // and feel, this stepped is forgoed and a middle ground of 147 // flushing references from the constructor is instead done. 148 // The implication is that once one DesktopProperty is created 149 // there will most likely be n (number of DesktopProperties created 150 // by the LookAndFeel) WeakPCLs around, but this number will not 151 // grow past n. 152 flushUnreferencedProperties(); 153 } 154 155 /** 156 * UIManager.LazyValue method, returns the value from the desktop 157 * or the fallback value if the desktop value is null. 158 */ 159 public Object createValue(UIDefaults table) { 160 if (value == null) { 161 value = configureValue(getValueFromDesktop()); 162 if (value == null) { 163 value = configureValue(getDefaultValue()); 164 } 165 } 166 return value; 167 } 168 169 /** 170 * Returns the value from the desktop. 171 */ 172 protected Object getValueFromDesktop() { 173 Toolkit toolkit = Toolkit.getDefaultToolkit(); 174 175 if (pcl == null) { 176 pcl = new WeakPCL(this, getKey(), UIManager.getLookAndFeel()); 177 toolkit.addPropertyChangeListener(getKey(), pcl); 178 } 179 180 return toolkit.getDesktopProperty(getKey()); 181 } 182 183 /** 184 * Returns the value to use if the desktop property is null. 185 */ 186 protected Object getDefaultValue() { 187 return fallback; 188 } 189 190 /** 191 * Invalidates the current value. 192 * 193 * @param laf the LookAndFeel this DesktopProperty was created with 194 */ 195 public void invalidate(LookAndFeel laf) { 196 invalidate(); 197 } 198 199 /** 200 * Invalides the current value so that the next invocation of 201 * <code>createValue</code> will ask for the property again. 202 */ 203 public void invalidate() { 204 value = null; 205 } 206 207 /** 208 * Requests that all components in the GUI hierarchy be updated 209 * to reflect dynamic changes in this look&feel. This update occurs 210 * by uninstalling and re-installing the UI objects. Requests are 211 * batched and collapsed into a single update pass because often 212 * many desktop properties will change at once. 213 */ 214 protected void updateUI() { 215 if (!isUpdatePending()) { 216 setUpdatePending(true); 217 // JDK-8039383: invalidate the cached style as soon as possible 218 invalidateStyle(); 219 Runnable uiUpdater = new Runnable() { 220 public void run() { 221 updateAllUIs(); 222 setUpdatePending(false); 223 } 224 }; 225 SwingUtilities.invokeLater(uiUpdater); 226 } 227 } 228 229 /** 230 * Configures the value as appropriate for a defaults property in 231 * the UIDefaults table. 232 */ 233 protected Object configureValue(Object value) { 234 if (value != null) { 235 if (value instanceof Color) { 236 return new ColorUIResource((Color)value); 237 } 238 else if (value instanceof Font) { 239 return new FontUIResource((Font)value); 240 } 241 else if (value instanceof UIDefaults.LazyValue) { 242 value = ((UIDefaults.LazyValue)value).createValue(null); 243 } 244 else if (value instanceof UIDefaults.ActiveValue) { 245 value = ((UIDefaults.ActiveValue)value).createValue(null); 246 } 247 } 248 return value; 249 } 250 251 /** 252 * Returns the key used to lookup the desktop properties value. 253 */ 254 protected String getKey() { 255 return key; 256 } 257 258 259 260 /** 261 * As there is typically only one Toolkit, the PropertyChangeListener 262 * is handled via a WeakReference so as not to pin down the 263 * DesktopProperty. 264 */ 265 private static class WeakPCL extends WeakReference<DesktopProperty> 266 implements PropertyChangeListener { 267 private String key; 268 private LookAndFeel laf; 269 270 WeakPCL(DesktopProperty target, String key, LookAndFeel laf) { 271 super(target, queue); 272 this.key = key; 273 this.laf = laf; 274 } 275 276 public void propertyChange(PropertyChangeEvent pce) { 277 DesktopProperty property = get(); 278 279 if (property == null || laf != UIManager.getLookAndFeel()) { 280 // The property was GC'ed, we're no longer interested in 281 // PropertyChanges, remove the listener. 282 dispose(); 283 } 284 else { 285 property.invalidate(laf); 286 property.updateUI(); 287 } 288 } 289 290 void dispose() { 291 Toolkit.getDefaultToolkit().removePropertyChangeListener(key, this); 292 } 293 } 294 }