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