Path: blob/master/src/java.desktop/share/classes/sun/swing/plaf/DesktopProperty.java
41159 views
/*1* Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.swing.plaf;2627import java.awt.Color;28import java.awt.Font;29import java.awt.Frame;30import java.awt.Toolkit;31import java.awt.Window;32import java.beans.PropertyChangeEvent;33import java.beans.PropertyChangeListener;34import java.lang.ref.ReferenceQueue;35import java.lang.ref.WeakReference;3637import javax.swing.LookAndFeel;38import javax.swing.SwingUtilities;39import javax.swing.UIDefaults;40import javax.swing.UIManager;41import javax.swing.plaf.ColorUIResource;42import javax.swing.plaf.FontUIResource;4344import sun.awt.AppContext;4546/**47* Wrapper for a value from the desktop. The value is lazily looked up, and48* can be accessed using the <code>UIManager.ActiveValue</code> method49* <code>createValue</code>. If the underlying desktop property changes this50* will force the UIs to update all known Frames. You can invoke51* <code>invalidate</code> to force the value to be fetched again.52*/53public class DesktopProperty implements UIDefaults.ActiveValue {54private static final StringBuilder DESKTOP_PROPERTY_UPDATE_PENDING_KEY =55new StringBuilder("DesktopPropertyUpdatePending");5657/**58* ReferenceQueue of unreferenced WeakPCLs.59*/60private static final ReferenceQueue<DesktopProperty> queue = new ReferenceQueue<DesktopProperty>();6162/**63* PropertyChangeListener attached to the Toolkit.64*/65private WeakPCL pcl;66/**67* Key used to lookup value from desktop.68*/69private final String key;70/**71* Value to return.72*/73private Object value;74/**75* Fallback value in case we get null from desktop.76*/77private final Object fallback;787980/**81* Cleans up any lingering state held by unrefeernced82* DesktopProperties.83*/84public static void flushUnreferencedProperties() {85WeakPCL pcl;8687while ((pcl = (WeakPCL)queue.poll()) != null) {88pcl.dispose();89}90}919293/**94* Sets whether or not an updateUI call is pending.95*/96private static synchronized void setUpdatePending(boolean update) {97AppContext.getAppContext()98.put(DESKTOP_PROPERTY_UPDATE_PENDING_KEY, update);99}100101/**102* Returns true if a UI update is pending.103*/104private static synchronized boolean isUpdatePending() {105return Boolean.TRUE.equals(AppContext.getAppContext()106.get(DESKTOP_PROPERTY_UPDATE_PENDING_KEY));107}108109/**110* Updates the UIs of all the known Frames.111*/112protected void updateAllUIs() {113Frame[] appFrames = Frame.getFrames();114for (Frame appFrame : appFrames) {115updateWindowUI(appFrame);116}117}118119/**120* Updates the UI of the passed in window and all its children.121*/122private static void updateWindowUI(Window window) {123SwingUtilities.updateComponentTreeUI(window);124Window[] ownedWins = window.getOwnedWindows();125for (Window ownedWin : ownedWins) {126updateWindowUI(ownedWin);127}128}129130131/**132* Creates a DesktopProperty.133*134* @param key Key used in looking up desktop value.135* @param fallback Value used if desktop property is null.136*/137public DesktopProperty(String key, Object fallback) {138this.key = key;139this.fallback = fallback;140// The only sure fire way to clear our references is to create a141// Thread and wait for a reference to be added to the queue.142// Because it is so rare that you will actually change the look143// and feel, this stepped is forgoed and a middle ground of144// flushing references from the constructor is instead done.145// The implication is that once one DesktopProperty is created146// there will most likely be n (number of DesktopProperties created147// by the LookAndFeel) WeakPCLs around, but this number will not148// grow past n.149flushUnreferencedProperties();150}151152/**153* UIManager.LazyValue method, returns the value from the desktop154* or the fallback value if the desktop value is null.155*/156public Object createValue(UIDefaults table) {157if (value == null) {158value = configureValue(getValueFromDesktop());159if (value == null) {160value = configureValue(getDefaultValue());161}162}163return value;164}165166/**167* Returns the value from the desktop.168*/169protected Object getValueFromDesktop() {170Toolkit toolkit = Toolkit.getDefaultToolkit();171172if (pcl == null) {173pcl = new WeakPCL(this, getKey(), UIManager.getLookAndFeel());174toolkit.addPropertyChangeListener(getKey(), pcl);175}176177return toolkit.getDesktopProperty(getKey());178}179180/**181* Returns the value to use if the desktop property is null.182*/183protected Object getDefaultValue() {184return fallback;185}186187/**188* Invalidates the current value.189*190* @param laf the LookAndFeel this DesktopProperty was created with191*/192public void invalidate(LookAndFeel laf) {193invalidate();194}195196/**197* Invalides the current value so that the next invocation of198* <code>createValue</code> will ask for the property again.199*/200public void invalidate() {201value = null;202}203204/**205* Requests that all components in the GUI hierarchy be updated206* to reflect dynamic changes in this {@literal look&feel}. This update occurs207* by uninstalling and re-installing the UI objects. Requests are208* batched and collapsed into a single update pass because often209* many desktop properties will change at once.210*/211protected void updateUI() {212if (!isUpdatePending()) {213setUpdatePending(true);214Runnable uiUpdater = new Runnable() {215public void run() {216updateAllUIs();217setUpdatePending(false);218}219};220SwingUtilities.invokeLater(uiUpdater);221}222}223224/**225* Configures the value as appropriate for a defaults property in226* the UIDefaults table.227*/228protected Object configureValue(Object value) {229if (value != null) {230if (value instanceof Color) {231return new ColorUIResource((Color)value);232}233else if (value instanceof Font) {234return new FontUIResource((Font)value);235}236else if (value instanceof UIDefaults.LazyValue) {237value = ((UIDefaults.LazyValue)value).createValue(null);238}239else if (value instanceof UIDefaults.ActiveValue) {240value = ((UIDefaults.ActiveValue)value).createValue(null);241}242}243return value;244}245246/**247* Returns the key used to lookup the desktop properties value.248*/249protected String getKey() {250return key;251}252253/**254* As there is typically only one Toolkit, the PropertyChangeListener255* is handled via a WeakReference so as not to pin down the256* DesktopProperty.257*/258private static class WeakPCL extends WeakReference<DesktopProperty>259implements PropertyChangeListener {260private String key;261private LookAndFeel laf;262263WeakPCL(DesktopProperty target, String key, LookAndFeel laf) {264super(target, queue);265this.key = key;266this.laf = laf;267}268269public void propertyChange(PropertyChangeEvent pce) {270DesktopProperty property = get();271272if (property == null || laf != UIManager.getLookAndFeel()) {273// The property was GC'ed, we're no longer interested in274// PropertyChanges, remove the listener.275dispose();276}277else {278property.invalidate(laf);279property.updateUI();280}281}282283void dispose() {284Toolkit.getDefaultToolkit().removePropertyChangeListener(key, this);285}286}287}288289290