blob: d7d5c0205d7f6ee2bb07ca42904af69d7bb0baea [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25package com.sun.java.swing.plaf.windows;
26
27import java.awt.*;
28import java.beans.*;
29import java.lang.ref.*;
30import javax.swing.*;
31import 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.
43public 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 ReferenceQueue queue;
53
54
55 /**
56 * PropertyChangeListener attached to the Toolkit.
57 */
58 private WeakPCL pcl;
59 /**
60 * Key used to lookup value from desktop.
61 */
62 private String key;
63 /**
64 * Value to return.
65 */
66 private Object value;
67 /**
68 * Fallback value in case we get null from desktop.
69 */
70 private Object fallback;
71
72 /**
73 * Toolkit.
74 */
75 private Toolkit toolkit;
76
77
78 static {
79 queue = new ReferenceQueue();
80 }
81
82 /**
83 * Cleans up any lingering state held by unrefeernced
84 * DesktopProperties.
85 */
86 static void flushUnreferencedProperties() {
87 WeakPCL pcl;
88
89 while ((pcl = (WeakPCL)queue.poll()) != null) {
90 pcl.dispose();
91 }
92 }
93
94
95 /**
96 * Sets whether or not an updateUI call is pending.
97 */
98 private static synchronized void setUpdatePending(boolean update) {
99 updatePending = update;
100 }
101
102 /**
103 * Returns true if a UI update is pending.
104 */
105 private static synchronized boolean isUpdatePending() {
106 return updatePending;
107 }
108
109 /**
110 * Updates the UIs of all the known Frames.
111 */
112 private static void updateAllUIs() {
113 // Check if the current UI is WindowsLookAndfeel and flush the XP style map.
114 // Note: Change the package test if this class is moved to a different package.
115 Class uiClass = UIManager.getLookAndFeel().getClass();
116 if (uiClass.getPackage().equals(DesktopProperty.class.getPackage())) {
117 XPStyle.invalidateStyle();
118 }
119 Frame appFrames[] = Frame.getFrames();
120 for (int j=0; j < appFrames.length; j++) {
121 updateWindowUI(appFrames[j]);
122 }
123 }
124
125 /**
126 * Updates the UI of the passed in window and all its children.
127 */
128 private static void updateWindowUI(Window window) {
129 SwingUtilities.updateComponentTreeUI(window);
130 Window ownedWins[] = window.getOwnedWindows();
131 for (int i=0; i < ownedWins.length; i++) {
132 updateWindowUI(ownedWins[i]);
133 }
134 }
135
136
137 /**
138 * Creates a DesktopProperty.
139 *
140 * @param key Key used in looking up desktop value.
141 * @param fallback Value used if desktop property is null.
142 * @param toolkit Toolkit used to fetch property from, can be null
143 * in which default will be used.
144 */
145 public DesktopProperty(String key, Object fallback, Toolkit toolkit) {
146 this.key = key;
147 this.fallback = fallback;
148 this.toolkit = toolkit;
149 // The only sure fire way to clear our references is to create a
150 // Thread and wait for a reference to be added to the queue.
151 // Because it is so rare that you will actually change the look
152 // and feel, this stepped is forgoed and a middle ground of
153 // flushing references from the constructor is instead done.
154 // The implication is that once one DesktopProperty is created
155 // there will most likely be n (number of DesktopProperties created
156 // by the LookAndFeel) WeakPCLs around, but this number will not
157 // grow past n.
158 flushUnreferencedProperties();
159 }
160
161 /**
162 * UIManager.LazyValue method, returns the value from the desktop
163 * or the fallback value if the desktop value is null.
164 */
165 public Object createValue(UIDefaults table) {
166 if (value == null) {
167 value = configureValue(getValueFromDesktop());
168 if (value == null) {
169 value = configureValue(getDefaultValue());
170 }
171 }
172 return value;
173 }
174
175 /**
176 * Returns the value from the desktop.
177 */
178 protected Object getValueFromDesktop() {
179 if (this.toolkit == null) {
180 this.toolkit = Toolkit.getDefaultToolkit();
181 }
182 Object value = toolkit.getDesktopProperty(getKey());
183 pcl = new WeakPCL(this, toolkit, getKey(), UIManager.getLookAndFeel());
184 toolkit.addPropertyChangeListener(getKey(), pcl);
185 return value;
186 }
187
188 /**
189 * Returns the value to use if the desktop property is null.
190 */
191 protected Object getDefaultValue() {
192 return fallback;
193 }
194
195 /**
196 * Invalidates the current value.
197 *
198 * @param laf the LookAndFeel this DesktopProperty was created with
199 */
200 public void invalidate(LookAndFeel laf) {
201 invalidate();
202 }
203
204 /**
205 * Invalides the current value so that the next invocation of
206 * <code>createValue</code> will ask for the property again.
207 */
208 public void invalidate() {
209 if (pcl != null) {
210 toolkit.removePropertyChangeListener(getKey(), pcl);
211 toolkit = null;
212 pcl = null;
213 value = null;
214 }
215 }
216
217 /**
218 * Requests that all components in the GUI hierarchy be updated
219 * to reflect dynamic changes in this look&feel. This update occurs
220 * by uninstalling and re-installing the UI objects. Requests are
221 * batched and collapsed into a single update pass because often
222 * many desktop properties will change at once.
223 */
224 protected void updateUI() {
225 if (!isUpdatePending()) {
226 setUpdatePending(true);
227 Runnable uiUpdater = new Runnable() {
228 public void run() {
229 updateAllUIs();
230 setUpdatePending(false);
231 }
232 };
233 SwingUtilities.invokeLater(uiUpdater);
234 }
235 }
236
237 /**
238 * Configures the value as appropriate for a defaults property in
239 * the UIDefaults table.
240 */
241 protected Object configureValue(Object value) {
242 if (value != null) {
243 if (value instanceof Color) {
244 return new ColorUIResource((Color)value);
245 }
246 else if (value instanceof Font) {
247 return new FontUIResource((Font)value);
248 }
249 else if (value instanceof UIDefaults.LazyValue) {
250 value = ((UIDefaults.LazyValue)value).createValue(null);
251 }
252 else if (value instanceof UIDefaults.ActiveValue) {
253 value = ((UIDefaults.ActiveValue)value).createValue(null);
254 }
255 }
256 return value;
257 }
258
259 /**
260 * Returns the key used to lookup the desktop properties value.
261 */
262 protected String getKey() {
263 return key;
264 }
265
266
267
268 /**
269 * As there is typically only one Toolkit, the PropertyChangeListener
270 * is handled via a WeakReference so as not to pin down the
271 * DesktopProperty.
272 */
273 private static class WeakPCL extends WeakReference
274 implements PropertyChangeListener {
275 private Toolkit kit;
276 private String key;
277 private LookAndFeel laf;
278
279 WeakPCL(Object target, Toolkit kit, String key, LookAndFeel laf) {
280 super(target, queue);
281 this.kit = kit;
282 this.key = key;
283 this.laf = laf;
284 }
285
286 public void propertyChange(PropertyChangeEvent pce) {
287 DesktopProperty property = (DesktopProperty)get();
288
289 if (property == null || laf != UIManager.getLookAndFeel()) {
290 // The property was GC'ed, we're no longer interested in
291 // PropertyChanges, remove the listener.
292 dispose();
293 }
294 else {
295 property.invalidate(laf);
296 property.updateUI();
297 }
298 }
299
300 void dispose() {
301 kit.removePropertyChangeListener(key, this);
302 }
303 }
304}