001: /*
002: * @(#)DesktopProperty.java
003: *
004: * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
005: * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
006: */
007: package com.jidesoft.plaf;
008:
009: import javax.swing.*;
010: import javax.swing.plaf.ColorUIResource;
011: import javax.swing.plaf.FontUIResource;
012: import java.awt.*;
013: import java.beans.PropertyChangeEvent;
014: import java.beans.PropertyChangeListener;
015: import java.lang.ref.WeakReference;
016:
017: /**
018: * Wrapper for a value from the desktop. The value is lazily looked up, and
019: * can be accessed using the <code>UIManager.ActiveValue</code> method
020: * <code>createValue</code>. If the underlying desktop property changes this
021: * will force the UIs to update all known Frames. You can invoke
022: * <code>invalidate</code> to force the value to be fetched again.
023: * <p/>
024: * Note: This class is the same as DesktopProperty under com.sun.java.swing.plaf.windows.
025: * We keep a copy of it here is mainly for compatibility reason in case Sun
026: * decides to change the location of this class.
027: */
028:
029: public class WindowsDesktopProperty implements UIDefaults.ActiveValue {
030: /**
031: * Indicates if an updateUI call is pending.
032: */
033: private static boolean updatePending;
034:
035: /**
036: * PropertyChangeListener attached to the Toolkit.
037: */
038: private WeakPCL pcl;
039: /**
040: * Key used to lookup value from desktop.
041: */
042: private String key;
043: /**
044: * Value to return.
045: */
046: private Object value;
047: /**
048: * Fallback value in case we get null from desktop.
049: */
050: private Object fallback;
051:
052: /**
053: * Toolkit.
054: */
055: private Toolkit toolkit;
056:
057: private float fontSize = -1f;
058:
059: private int fontStyle = -1;
060:
061: /**
062: * Sets whether or not an updateUI call is pending.
063: */
064: private static synchronized void setUpdatePending(boolean update) {
065: updatePending = update;
066: }
067:
068: /**
069: * Returns true if a UI update is pending.
070: */
071: private static synchronized boolean isUpdatePending() {
072: return updatePending;
073: }
074:
075: /**
076: * Updates the UIs of all the known Frames.
077: */
078: private static void updateAllUIs() {
079: Frame appFrames[] = Frame.getFrames();
080: for (int j = 0; j < appFrames.length; j++) {
081: updateWindowUI(appFrames[j]);
082: }
083: }
084:
085: /**
086: * Updates the UI of the passed in window and all its children.
087: */
088: private static void updateWindowUI(Window window) {
089: SwingUtilities.updateComponentTreeUI(window);
090: Window ownedWins[] = window.getOwnedWindows();
091: for (int i = 0; i < ownedWins.length; i++) {
092: updateWindowUI(ownedWins[i]);
093: }
094: }
095:
096: /**
097: * Creates a DesktopProperty.
098: *
099: * @param key Key used in looking up desktop value.
100: * @param fallback Value used if desktop property is null.
101: * @param toolkit Toolkit used to fetch property from, can be null
102: * in which default will be used.
103: */
104: public WindowsDesktopProperty(String key, Object fallback,
105: Toolkit toolkit) {
106: this .key = key;
107: this .fallback = fallback;
108: this .toolkit = toolkit;
109: }
110:
111: public WindowsDesktopProperty(String key, Object fallback,
112: Toolkit toolkit, float fontSize) {
113: this .key = key;
114: this .fallback = fallback;
115: this .toolkit = toolkit;
116: this .fontSize = fontSize;
117: }
118:
119: public WindowsDesktopProperty(String key, Object fallback,
120: Toolkit toolkit, float fontSize, int fontStyle) {
121: this .key = key;
122: this .fallback = fallback;
123: this .toolkit = toolkit;
124: this .fontSize = fontSize;
125: this .fontStyle = fontStyle;
126: }
127:
128: /**
129: * UIManager.LazyValue method, returns the value from the desktop
130: * or the fallback value if the desktop value is null.
131: */
132: public Object createValue(UIDefaults table) {
133: if (value == null) {
134: value = configureValue(getValueFromDesktop());
135: if (value == null) {
136: value = configureValue(getDefaultValue());
137: }
138: }
139: return value;
140: }
141:
142: /**
143: * Returns the value from the desktop.
144: */
145: protected Object getValueFromDesktop() {
146: if (this .toolkit == null) {
147: this .toolkit = Toolkit.getDefaultToolkit();
148: }
149: Object value = toolkit.getDesktopProperty(getKey());
150: pcl = new WeakPCL(this , toolkit);
151: toolkit.addPropertyChangeListener(getKey(), pcl);
152: return value;
153: }
154:
155: /**
156: * Returns the value to use if the desktop property is null.
157: */
158: protected Object getDefaultValue() {
159: if (fallback instanceof String) {
160: return UIDefaultsLookup.get(fallback);
161: } else {
162: return fallback;
163: }
164: }
165:
166: /**
167: * Invalides the current value so that the next invocation of
168: * <code>createValue</code> will ask for the property again.
169: */
170: public void invalidate() {
171: if (pcl != null) {
172: toolkit.removePropertyChangeListener(getKey(), pcl);
173: toolkit = null;
174: pcl = null;
175: value = null;
176: }
177: }
178:
179: /**
180: * Requests that all components in the GUI hierarchy be updated
181: * to reflect dynamic changes in this look&feel. This update occurs
182: * by uninstalling and re-installing the UI objects. Requests are
183: * batched and collapsed into a single update pass because often
184: * many desktop properties will change at once.
185: */
186: protected void updateUI() {
187: if (!isUpdatePending()) {
188: setUpdatePending(true);
189: Runnable uiUpdater = new Runnable() {
190: public void run() {
191: updateAllUIs();
192: setUpdatePending(false);
193: }
194: };
195: SwingUtilities.invokeLater(uiUpdater);
196: }
197: }
198:
199: /**
200: * Configures the value as appropriate for a defaults property in
201: * the UIDefaults table.
202: */
203: protected Object configureValue(Object value) {
204: if (value != null) {
205: if (value instanceof Color) {
206: return new ColorUIResource((Color) value);
207: } else if (value instanceof Font) {
208: if (fontSize != -1 && fontStyle != -1f) {
209: return new FontUIResource(((Font) value)
210: .deriveFont(fontStyle, fontSize));
211: } else if (fontSize != -1) {
212: return new FontUIResource(((Font) value)
213: .deriveFont(fontSize));
214: } else if (fontStyle != -1f) {
215: return new FontUIResource(((Font) value)
216: .deriveFont(fontStyle));
217: } else {
218: return new FontUIResource((Font) value);
219: }
220: } else if (value instanceof UIDefaults.ProxyLazyValue) {
221: value = ((UIDefaults.ProxyLazyValue) value)
222: .createValue(null);
223: } else if (value instanceof UIDefaults.ActiveValue) {
224: value = ((UIDefaults.ActiveValue) value)
225: .createValue(null);
226: }
227: }
228: return value;
229: }
230:
231: /**
232: * Returns the key used to lookup the desktop properties value.
233: */
234: protected String getKey() {
235: return key;
236: }
237:
238: /**
239: * As there is typically only one Toolkit, the PropertyChangeListener
240: * is handled via a WeakReference so as not to pin down the
241: * DesktopProperty.
242: */
243: private static class WeakPCL extends WeakReference implements
244: PropertyChangeListener {
245: private Toolkit kit;
246:
247: WeakPCL(Object target, Toolkit kit) {
248: super (target);
249: this .kit = kit;
250: }
251:
252: public void propertyChange(PropertyChangeEvent pce) {
253: WindowsDesktopProperty property = (WindowsDesktopProperty) get();
254:
255: if (property == null) {
256: // The property was GC'ed, we're no longer interested in
257: // PropertyChanges, remove the listener.
258: kit.removePropertyChangeListener(pce.getPropertyName(),
259: this);
260: } else {
261: property.invalidate();
262: property.updateUI();
263: }
264: }
265: }
266: }
|