6802944: Nimbus initialization is too slow

Reviewed-by: jasper
This commit is contained in:
Peter Zhelezniakov 2009-08-31 13:46:24 +04:00
parent 4e064060c0
commit d1839690e3
5 changed files with 230 additions and 371 deletions

View file

@ -24,6 +24,7 @@
*/ */
package org.jdesktop.synthdesigner.generator; package org.jdesktop.synthdesigner.generator;
import java.awt.Color;
import org.jdesktop.swingx.designer.Canvas; import org.jdesktop.swingx.designer.Canvas;
import org.jdesktop.swingx.designer.font.Typeface; import org.jdesktop.swingx.designer.font.Typeface;
import org.jdesktop.swingx.designer.paint.Matte; import org.jdesktop.swingx.designer.paint.Matte;
@ -133,11 +134,7 @@ public class DefaultsGenerator {
private static void writeColorPalette(StringBuilder uiDefaultInit, List<UIPaint> colors) { private static void writeColorPalette(StringBuilder uiDefaultInit, List<UIPaint> colors) {
for (UIPaint color : colors) { for (UIPaint color : colors) {
uiDefaultInit.append(" d.put(\"") writeMatte(color.getName(), (Matte)color.getValue(), uiDefaultInit);
.append(color.getName())
.append("\",")
.append(convertPaint(color.getValue()))
.append(");\n");
} }
} }
@ -255,12 +252,8 @@ public class DefaultsGenerator {
.append("));\n"); .append("));\n");
break; break;
case COLOR: case COLOR:
uiDefaultInit.append(" d.put(\"") writeMatte(prefix + property.getName(),
.append(prefix) (Matte) property.getValue(), uiDefaultInit);
.append(property.getName())
.append("\", ")
.append(convertPaint((Matte)property.getValue()))
.append(");\n");
break; break;
case FONT: case FONT:
writeTypeFace(prefix.replace("\"", "\\\"") + property.getName(), writeTypeFace(prefix.replace("\"", "\\\"") + property.getName(),
@ -300,7 +293,7 @@ public class DefaultsGenerator {
private static void writeMatte(String propertyName, Matte matte, StringBuilder uiDefaultInit) { private static void writeMatte(String propertyName, Matte matte, StringBuilder uiDefaultInit) {
if (matte==null) System.err.println("Error matte is NULL for ["+propertyName+"]"); if (matte==null) System.err.println("Error matte is NULL for ["+propertyName+"]");
uiDefaultInit.append(" d.put(\"") uiDefaultInit.append(" addColor(d, \"")
.append(propertyName) .append(propertyName)
.append("\", ") .append("\", ")
.append(convertPaint(matte)) .append(convertPaint(matte))
@ -609,23 +602,19 @@ public class DefaultsGenerator {
if (paint instanceof Matte) { if (paint instanceof Matte) {
Matte matte = (Matte) paint; Matte matte = (Matte) paint;
if (matte.isAbsolute()) { if (matte.isAbsolute()) {
String colorParams = convert(matte.getColor()); Color c = matte.getColor();
if (matte.isUiResource()) { return c.getRed() + ", " + c.getGreen() + ", " +
return "new ColorUIResource(" + colorParams + ")"; c.getBlue() + ", " + c.getAlpha();
} else { } else {
return colorParams; String s = "\"" + matte.getUiDefaultParentName() + "\", " +
} matte.getHueOffset() + "f, " +
} else { matte.getSaturationOffset() + "f, " +
String s = "getDerivedColor(\"" + matte.getBrightnessOffset() + "f, " +
matte.getUiDefaultParentName()+"\","+
matte.getHueOffset()+"f,"+matte.getSaturationOffset()+
"f,"+matte.getBrightnessOffset()+"f,"+
matte.getAlphaOffset(); matte.getAlphaOffset();
if (matte.isUiResource()) { if (! matte.isUiResource()) {
return s + ")"; s += ", false";
} else {
return s + ",false)";
} }
return s;
} }
} else { } else {
//TODO: What about gradients etc here? //TODO: What about gradients etc here?

View file

@ -101,14 +101,7 @@ final class ${LAF_NAME}Defaults {
*/ */
private FontUIResource defaultFont; private FontUIResource defaultFont;
/** private ColorTree colorTree = new ColorTree();
* Map of lists of derived colors keyed by the DerivedColorKeys
*/
private Map<DerivedColorKey, DerivedColor> derivedColorsMap =
new HashMap<DerivedColorKey, DerivedColor>();
/** Tempory key used for fetching from the derivedColorsMap */
private final DerivedColorKey tmpDCKey = new DerivedColorKey();
/** Listener for changes to user defaults table */ /** Listener for changes to user defaults table */
private DefaultsListener defaultsListener = new DefaultsListener(); private DefaultsListener defaultsListener = new DefaultsListener();
@ -117,14 +110,14 @@ final class ${LAF_NAME}Defaults {
void initialize() { void initialize() {
// add listener for derived colors // add listener for derived colors
UIManager.addPropertyChangeListener(defaultsListener); UIManager.addPropertyChangeListener(defaultsListener);
UIManager.getDefaults().addPropertyChangeListener(defaultsListener); UIManager.getDefaults().addPropertyChangeListener(colorTree);
} }
/** Called by UIManager when this look and feel is uninstalled. */ /** Called by UIManager when this look and feel is uninstalled. */
void uninitialize() { void uninitialize() {
// remove listener for derived colors // remove listener for derived colors
UIManager.getDefaults().removePropertyChangeListener(defaultsListener);
UIManager.removePropertyChangeListener(defaultsListener); UIManager.removePropertyChangeListener(defaultsListener);
UIManager.getDefaults().removePropertyChangeListener(colorTree);
} }
/** /**
@ -663,22 +656,23 @@ ${UI_DEFAULT_INIT}
} }
} }
/** private void addColor(UIDefaults d, String uin, int r, int g, int b, int a) {
* Get a derived color, derived colors are shared instances and will be Color color = new ColorUIResource(new Color(r, g, b, a));
* updated when its parent UIDefault color changes. colorTree.addColor(uin, color);
* d.put(uin, color);
* @param uiDefaultParentName The parent UIDefault key }
* @param hOffset The hue offset
* @param sOffset The saturation offset private void addColor(UIDefaults d, String uin, String parentUin,
* @param bOffset The brightness offset float hOffset, float sOffset, float bOffset, int aOffset) {
* @param aOffset The alpha offset addColor(d, uin, parentUin, hOffset, sOffset, bOffset, aOffset, true);
* @return The stored derived color }
*/
public DerivedColor getDerivedColor(String uiDefaultParentName, private void addColor(UIDefaults d, String uin, String parentUin,
float hOffset, float sOffset, float hOffset, float sOffset, float bOffset,
float bOffset, int aOffset){ int aOffset, boolean uiResource) {
return getDerivedColor(uiDefaultParentName, hOffset, sOffset, Color color = getDerivedColor(uin, parentUin,
bOffset, aOffset, true); hOffset, sOffset, bOffset, aOffset, uiResource);
d.put(uin, color);
} }
/** /**
@ -694,89 +688,110 @@ ${UI_DEFAULT_INIT}
* false if it should not be a UIResource * false if it should not be a UIResource
* @return The stored derived color * @return The stored derived color
*/ */
public DerivedColor getDerivedColor(String uiDefaultParentName, public DerivedColor getDerivedColor(String parentUin,
float hOffset, float sOffset, float hOffset, float sOffset,
float bOffset, int aOffset, float bOffset, int aOffset,
boolean uiResource){ boolean uiResource){
tmpDCKey.set(uiDefaultParentName, hOffset, sOffset, bOffset, aOffset, return getDerivedColor(null, parentUin,
uiResource); hOffset, sOffset, bOffset, aOffset, uiResource);
DerivedColor color = derivedColorsMap.get(tmpDCKey); }
if (color == null){
private DerivedColor getDerivedColor(String uin, String parentUin,
float hOffset, float sOffset,
float bOffset, int aOffset,
boolean uiResource) {
DerivedColor color;
if (uiResource) { if (uiResource) {
color = new DerivedColor.UIResource(uiDefaultParentName, color = new DerivedColor.UIResource(parentUin,
hOffset, sOffset, bOffset, aOffset); hOffset, sOffset, bOffset, aOffset);
} else { } else {
color = new DerivedColor(uiDefaultParentName, hOffset, sOffset, color = new DerivedColor(parentUin, hOffset, sOffset,
bOffset, aOffset); bOffset, aOffset);
} }
// calculate the initial value
color.rederiveColor(); if (derivedColors.containsKey(color)) {
// add the listener so that if the color changes we'll propogate it return derivedColors.get(color);
color.addPropertyChangeListener(defaultsListener); } else {
// add to the derived colors table derivedColors.put(color, color);
derivedColorsMap.put(new DerivedColorKey(uiDefaultParentName, color.rederiveColor(); /// move to ARP.decodeColor() ?
hOffset, sOffset, bOffset, aOffset, uiResource),color); colorTree.addColor(uin, color);
}
return color; return color;
} }
/**
* Key class for derived colors
*/
private class DerivedColorKey {
private String uiDefaultParentName;
private float hOffset, sOffset, bOffset;
private int aOffset;
private boolean uiResource;
DerivedColorKey(){}
DerivedColorKey(String uiDefaultParentName, float hOffset,
float sOffset, float bOffset, int aOffset,
boolean uiResource) {
set(uiDefaultParentName, hOffset, sOffset, bOffset, aOffset, uiResource);
} }
void set (String uiDefaultParentName, float hOffset, private Map<DerivedColor, DerivedColor> derivedColors =
float sOffset, float bOffset, int aOffset, new HashMap<DerivedColor, DerivedColor>();
boolean uiResource) {
this.uiDefaultParentName = uiDefaultParentName; private class ColorTree implements PropertyChangeListener {
this.hOffset = hOffset; private Node root = new Node(null, null);
this.sOffset = sOffset; private Map<String, Node> nodes = new HashMap<String, Node>();
this.bOffset = bOffset;
this.aOffset = aOffset; public Color getColor(String uin) {
this.uiResource = uiResource; return nodes.get(uin).color;
}
public void addColor(String uin, Color color) {
Node parent = getParentNode(color);
Node node = new Node(color, parent);
parent.children.add(node);
if (uin != null) {
nodes.put(uin, node);
}
}
private Node getParentNode(Color color) {
Node parent = root;
if (color instanceof DerivedColor) {
String parentUin = ((DerivedColor)color).getUiDefaultParentName();
Node p = nodes.get(parentUin);
if (p != null) {
parent = p;
}
}
return parent;
}
public void update() {
root.update();
} }
@Override @Override
public boolean equals(Object o) { public void propertyChange(PropertyChangeEvent ev) {
if (this == o) return true; String name = ev.getPropertyName();
if (!(o instanceof DerivedColorKey)) return false; Node node = nodes.get(name);
DerivedColorKey that = (DerivedColorKey) o; if (node != null) {
if (aOffset != that.aOffset) return false; // this is a registered color
if (Float.compare(that.bOffset, bOffset) != 0) return false; node.parent.children.remove(node);
if (Float.compare(that.hOffset, hOffset) != 0) return false; Color color = (Color) ev.getNewValue();
if (Float.compare(that.sOffset, sOffset) != 0) return false; Node parent = getParentNode(color);
if (uiDefaultParentName != null ? node.set(color, parent);
!uiDefaultParentName.equals(that.uiDefaultParentName) : parent.children.add(node);
that.uiDefaultParentName != null) return false; node.update();
if (this.uiResource != that.uiResource) return false; }
return true;
} }
@Override class Node {
public int hashCode() { Color color;
int result = super.hashCode(); Node parent;
result = 31 * result + uiDefaultParentName.hashCode(); List<Node> children = new LinkedList<Node>();
result = 31 * result + hOffset != +0.0f ?
Float.floatToIntBits(hOffset) : 0; Node(Color color, Node parent) {
result = 31 * result + sOffset != +0.0f ? set(color, parent);
Float.floatToIntBits(sOffset) : 0; }
result = 31 * result + bOffset != +0.0f ?
Float.floatToIntBits(bOffset) : 0; public void set(Color color, Node parent) {
result = 31 * result + aOffset; this.color = color;
result = 31 * result + (uiResource ? 1 : 0); this.parent = parent;
return result; }
public void update() {
if (color instanceof DerivedColor) {
((DerivedColor)color).rederiveColor();
}
for (Node child: children) {
child.update();
}
}
} }
} }
@ -786,49 +801,12 @@ ${UI_DEFAULT_INIT}
private class DefaultsListener implements PropertyChangeListener { private class DefaultsListener implements PropertyChangeListener {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
Object src = evt.getSource(); if ("lookAndFeel".equals(evt.getPropertyName())) {
String key = evt.getPropertyName();
if (key.equals("lookAndFeel")){
// LAF has been installed, this is the first point at which we // LAF has been installed, this is the first point at which we
// can access our defaults table via UIManager so before now // can access our defaults table via UIManager so before now
// all derived colors will be incorrect. // all derived colors will be incorrect.
// First we need to update // First we need to update
for (DerivedColor color : derivedColorsMap.values()) { colorTree.update();
color.rederiveColor();
}
} else if (src instanceof DerivedColor && key.equals("rgb")) {
// derived color that is in UIManager defaults has changed
// update all its dependent colors. Don't worry about doing
// this recursively since calling rederiveColor will cause
// another PCE to be fired, ending up here and essentially
// recursing
DerivedColor parentColor = (DerivedColor)src;
String parentKey = null;
Set<Map.Entry<Object,Object>> entries =
UIManager.getDefaults().entrySet();
for (Map.Entry entry : entries) {
Object value = entry.getValue();
if (value == parentColor) {
parentKey = entry.getKey().toString();
}
}
if (parentKey == null) {
//couldn't find the DerivedColor in the UIDefaults map,
//so we just bail.
return;
}
for (Map.Entry entry : entries) {
Object value = entry.getValue();
if (value instanceof DerivedColor) {
DerivedColor color = (DerivedColor)entry.getValue();
if (parentKey.equals(color.getUiDefaultParentName())) {
color.rederiveColor();
}
}
}
} }
} }
} }
@ -875,4 +853,3 @@ ${UI_DEFAULT_INIT}
} }
} }
} }

View file

@ -39,8 +39,6 @@ import java.beans.PropertyChangeListener;
* @author Jasper Potts * @author Jasper Potts
*/ */
class DerivedColor extends Color { class DerivedColor extends Color {
private final PropertyChangeSupport changeSupport =
new PropertyChangeSupport(this);
private final String uiDefaultParentName; private final String uiDefaultParentName;
private final float hOffset, sOffset, bOffset; private final float hOffset, sOffset, bOffset;
private final int aOffset; private final int aOffset;
@ -79,7 +77,6 @@ class DerivedColor extends Color {
* Recalculate the derived color from the UIManager parent color and offsets * Recalculate the derived color from the UIManager parent color and offsets
*/ */
public void rederiveColor() { public void rederiveColor() {
int old = argbValue;
Color src = UIManager.getColor(uiDefaultParentName); Color src = UIManager.getColor(uiDefaultParentName);
if (src != null) { if (src != null) {
float[] tmp = Color.RGBtoHSB(src.getRed(), src.getGreen(), src.getBlue(), null); float[] tmp = Color.RGBtoHSB(src.getRed(), src.getGreen(), src.getBlue(), null);
@ -97,7 +94,6 @@ class DerivedColor extends Color {
int alpha = clamp(aOffset); int alpha = clamp(aOffset);
argbValue = (Color.HSBtoRGB(tmp[0], tmp[1], tmp[2]) & 0xFFFFFF) | (alpha << 24); argbValue = (Color.HSBtoRGB(tmp[0], tmp[1], tmp[2]) & 0xFFFFFF) | (alpha << 24);
} }
changeSupport.firePropertyChange("rgb", old, argbValue);
} }
/** /**
@ -141,35 +137,6 @@ class DerivedColor extends Color {
return result; return result;
} }
/**
* Add a PropertyChangeListener to the listener list.
* The listener is registered for all properties.
* The same listener object may be added more than once, and will be called
* as many times as it is added.
* If <code>listener</code> is null, no exception is thrown and no action
* is taken.
*
* @param listener The PropertyChangeListener to be added
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
/**
* Remove a PropertyChangeListener from the listener list.
* This removes a PropertyChangeListener that was registered
* for all properties.
* If <code>listener</code> was added more than once to the same event
* source, it will be notified one less time after being removed.
* If <code>listener</code> is null, or was never added, no exception is
* thrown and no action is taken.
*
* @param listener The PropertyChangeListener to be removed
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
private float clamp(float value) { private float clamp(float value) {
if (value < 0) { if (value < 0) {
value = 0; value = 0;
@ -211,5 +178,15 @@ class DerivedColor extends Color {
float bOffset, int aOffset) { float bOffset, int aOffset) {
super(uiDefaultParentName, hOffset, sOffset, bOffset, aOffset); super(uiDefaultParentName, hOffset, sOffset, bOffset, aOffset);
} }
@Override
public boolean equals(Object o) {
return (o instanceof UIResource) && super.equals(o);
}
@Override
public int hashCode() {
return super.hashCode() + 7;
}
} }
} }

View file

@ -40,6 +40,9 @@ import java.awt.Container;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.LayoutManager; import java.awt.LayoutManager;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import javax.swing.GrayFilter; import javax.swing.GrayFilter;
import javax.swing.Icon; import javax.swing.Icon;
import javax.swing.JToolBar; import javax.swing.JToolBar;
@ -87,6 +90,8 @@ public class NimbusLookAndFeel extends SynthLookAndFeel {
*/ */
private UIDefaults uiDefaults; private UIDefaults uiDefaults;
private DefaultsListener defaultsListener = new DefaultsListener();
/** /**
* Create a new NimbusLookAndFeel. * Create a new NimbusLookAndFeel.
*/ */
@ -115,8 +120,7 @@ public class NimbusLookAndFeel extends SynthLookAndFeel {
defaults.uninitialize(); defaults.uninitialize();
// clear all cached images to free memory // clear all cached images to free memory
ImageCache.getInstance().flush(); ImageCache.getInstance().flush();
// remove the listeners and things installed by NimbusStyle UIManager.getDefaults().removePropertyChangeListener(defaultsListener);
NimbusStyle.uninitialize();
} }
/** /**
@ -515,4 +519,62 @@ public class NimbusLookAndFeel extends SynthLookAndFeel {
return obj; return obj;
} }
} }
private Map<String, Map<String, Object>> compiledDefaults = null;
private boolean defaultListenerAdded = false;
static String parsePrefix(String key) {
if (key == null) {
return null;
}
boolean inquotes = false;
for (int i = 0; i < key.length(); i++) {
char c = key.charAt(i);
if (c == '"') {
inquotes = !inquotes;
} else if ((c == '[' || c == '.') && !inquotes) {
return key.substring(0, i);
}
}
return null;
}
Map<String, Object> getDefaultsForPrefix(String prefix) {
if (compiledDefaults == null) {
compiledDefaults = new HashMap<String, Map<String, Object>>();
for (Map.Entry<Object, Object> entry: UIManager.getDefaults().entrySet()) {
if (entry.getKey() instanceof String) {
addDefault((String) entry.getKey(), entry.getValue());
}
}
if (! defaultListenerAdded) {
UIManager.getDefaults().addPropertyChangeListener(defaultsListener);
defaultListenerAdded = true;
}
}
return compiledDefaults.get(prefix);
}
private void addDefault(String key, Object value) {
String prefix = parsePrefix(key);
if (prefix != null) {
Map<String, Object> keys = compiledDefaults.get(prefix);
if (keys == null) {
keys = new HashMap<String, Object>();
compiledDefaults.put(prefix, keys);
}
keys.put(key, value);
}
}
private class DefaultsListener implements PropertyChangeListener {
@Override public void propertyChange(PropertyChangeEvent ev) {
String key = ev.getPropertyName();
if ("UIDefaults".equals(key)) {
compiledDefaults = null;
} else {
addDefault(key, ev.getNewValue());
}
}
}
} }

View file

@ -26,7 +26,6 @@ package javax.swing.plaf.nimbus;
import javax.swing.Painter; import javax.swing.Painter;
import java.beans.PropertyChangeEvent;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.UIDefaults; import javax.swing.UIDefaults;
import javax.swing.UIManager; import javax.swing.UIManager;
@ -39,16 +38,13 @@ import javax.swing.plaf.synth.SynthStyle;
import java.awt.Color; import java.awt.Color;
import java.awt.Font; import java.awt.Font;
import java.awt.Insets; import java.awt.Insets;
import java.beans.PropertyChangeListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
import sun.awt.AppContext;
/** /**
* <p>A SynthStyle implementation used by Nimbus. Each Region that has been * <p>A SynthStyle implementation used by Nimbus. Each Region that has been
@ -232,42 +228,6 @@ public final class NimbusStyle extends SynthStyle {
super.installDefaults(ctx); super.installDefaults(ctx);
} }
static String parsePrefix(String key) {
if (key == null) return null;
boolean inquotes = false;
for (int i=0; i<key.length(); i++) {
char c = key.charAt(i);
if (c == '"') {
inquotes = !inquotes;
} else if ((c == '[' || c == '.') && !inquotes) {
return key.substring(0, i);
}
}
return null;
}
/**
* Called by NimbusLookAndFeel when the look and feel is being uninstalled.
* Performs general cleanup of any app-context specific data.
*/
static void uninitialize() {
// get the appcontext that we've stored data in
AppContext ctx = AppContext.getAppContext();
// get the pcl stored in app context
PropertyChangeListener pcl = (PropertyChangeListener)
ctx.get("NimbusStyle.defaults.pcl");
// if the pcl exists, uninstall it from the UIDefaults tables
if (pcl != null) {
UIManager.getDefaults().removePropertyChangeListener(pcl);
UIManager.getLookAndFeelDefaults().removePropertyChangeListener(pcl);
}
// clear out the compiled defaults
ctx.put("NimbusStyle.defaults", null);
}
/** /**
* Pulls data out of UIDefaults, if it has not done so already, and sets * Pulls data out of UIDefaults, if it has not done so already, and sets
* up the internal state. * up the internal state.
@ -283,66 +243,9 @@ public final class NimbusStyle extends SynthStyle {
// any Nimbus.Overrides) // any Nimbus.Overrides)
values = new Values(); values = new Values();
// the profiler revealed that a great deal of CPU time and useless Map<String, Object> defaults =
// garbage was being produced by this method and the init method. One ((NimbusLookAndFeel) UIManager.getLookAndFeel()).
// culprit was the creation and reparsing of the entire UIDefaults getDefaultsForPrefix(prefix);
// map on each call to this method where "values" was null. It turns
// out this was happening a lot.
// To remove this bottleneck, we store the compiled TreeMaps of defaults
// in the appContext for reuse. It is nulled whenever the UIDefaults
// changes and recomputed when necessary.
final AppContext ctx = AppContext.getAppContext();
// fetch the defaults from the app context. If null, then create and
// store the compiled defaults
Map<String, TreeMap<String, Object>> compiledDefaults =
(Map<String, TreeMap<String, Object>>)
ctx.get("NimbusStyle.defaults");
if (compiledDefaults == null) {
// the entire UIDefaults tables are parsed and compiled into
// this map of maps. The key of the compiledDefaults is the
// prefix for each style, while the value is a map of
// keys->values for that prefix.
compiledDefaults = new HashMap<String, TreeMap<String, Object>>();
// get all the defaults from UIManager.getDefaults() and put them
// into the compiledDefaults
compileDefaults(compiledDefaults, UIManager.getDefaults());
// This second statement pulls defaults from the laf defaults
UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
compileDefaults(compiledDefaults, lafDefaults);
// if it has not already been done, add a listener to both
// UIManager.getDefaults() and UIManager.getLookAndFeelDefaults().
PropertyChangeListener pcl = (PropertyChangeListener)
ctx.get("NimbusStyle.defaults.pcl");
// if pcl is null, then it has not yet been registered with
// the UIManager defaults for this app context
if (pcl == null) {
// create a PCL which will simply clear out the compiled
// defaults from the app context, causing it to be recomputed
// on subsequent passes
pcl = new DefaultsListener();
// add the PCL to both defaults tables that we pay attention
// to, so that if the UIDefaults are updated, then the
// precompiled defaults will be cleared from the app context
// and recomputed on subsequent passes
UIManager.getDefaults().addPropertyChangeListener(pcl);
UIManager.getLookAndFeelDefaults().addPropertyChangeListener(pcl);
// save the PCL to the app context as a marker indicating
// that the PCL has been registered so we don't end up adding
// more than one listener to the UIDefaults tables.
ctx.put("NimbusStyle.defaults.pcl", pcl);
}
// store the defaults for reuse
ctx.put("NimbusStyle.defaults", compiledDefaults);
}
TreeMap<String, Object> defaults = compiledDefaults.get(prefix);
// inspect the client properties for the key "Nimbus.Overrides". If the // inspect the client properties for the key "Nimbus.Overrides". If the
// value is an instance of UIDefaults, then these defaults are used // value is an instance of UIDefaults, then these defaults are used
@ -371,52 +274,6 @@ public final class NimbusStyle extends SynthStyle {
} }
} }
// Now that I've accumulated all the defaults pertaining to this
// style, call init which will read these defaults and configure
// the default "values".
init(values, defaults);
}
/**
* Iterates over all the keys in the specified UIDefaults and compiles
* those keys into the comiledDefaults data structure. It relies on
* parsing the "prefix" out of the key. If the key is not a String or is
* null then it is ignored. In all other cases a prefix is parsed out
* (even if that prefix is the empty String or is a "fake" prefix. That
* is, suppose you had a key Foo~~MySpecial.KeyThing~~. In this case this
* is not a Nimbus formatted key, but we don't care, we treat it as if it
* is. This doesn't pose any harm, it will simply never be used).
*
* @param compiledDefaults
* @param d
*/
private void compileDefaults(
Map<String, TreeMap<String,Object>> compiledDefaults,
UIDefaults d) {
for (Object obj : new HashSet(d.keySet())) {
if (obj instanceof String) {
String key = (String)obj;
String kp = parsePrefix(key);
if (kp == null) continue;
TreeMap<String,Object> map = compiledDefaults.get(kp);
if (map == null) {
map = new TreeMap<String,Object>();
compiledDefaults.put(kp, map);
}
map.put(key, d.get(key));
}
}
}
/**
* Initializes the given <code>Values</code> object with the defaults
* contained in the given TreeMap.
*
* @param v The Values object to be initialized
* @param myDefaults a map of UIDefaults to use in initializing the Values.
* This map must contain only keys associated with this Style.
*/
private void init(Values v, TreeMap<String, Object> myDefaults) {
//a list of the different types of states used by this style. This //a list of the different types of states used by this style. This
//list may contain only "standard" states (those defined by Synth), //list may contain only "standard" states (those defined by Synth),
//or it may contain custom states, or it may contain only "standard" //or it may contain custom states, or it may contain only "standard"
@ -433,7 +290,7 @@ public final class NimbusStyle extends SynthStyle {
//"values" stateTypes to be a non-null array. //"values" stateTypes to be a non-null array.
//Otherwise, let the "values" stateTypes be null to indicate that //Otherwise, let the "values" stateTypes be null to indicate that
//there are no custom states or custom state ordering //there are no custom states or custom state ordering
String statesString = (String)myDefaults.get(prefix + ".States"); String statesString = (String)defaults.get(prefix + ".States");
if (statesString != null) { if (statesString != null) {
String s[] = statesString.split(","); String s[] = statesString.split(",");
for (int i=0; i<s.length; i++) { for (int i=0; i<s.length; i++) {
@ -442,7 +299,7 @@ public final class NimbusStyle extends SynthStyle {
//this is a non-standard state name, so look for the //this is a non-standard state name, so look for the
//custom state associated with it //custom state associated with it
String stateName = prefix + "." + s[i]; String stateName = prefix + "." + s[i];
State customState = (State)myDefaults.get(stateName); State customState = (State)defaults.get(stateName);
if (customState != null) { if (customState != null) {
states.add(customState); states.add(customState);
} }
@ -455,7 +312,7 @@ public final class NimbusStyle extends SynthStyle {
//to be non-null. Otherwise, leave it null (meaning, use the //to be non-null. Otherwise, leave it null (meaning, use the
//standard synth states). //standard synth states).
if (states.size() > 0) { if (states.size() > 0) {
v.stateTypes = states.toArray(new State[states.size()]); values.stateTypes = states.toArray(new State[states.size()]);
} }
//assign codes for each of the state types //assign codes for each of the state types
@ -490,7 +347,7 @@ public final class NimbusStyle extends SynthStyle {
} }
//Now iterate over all the keys in the defaults table //Now iterate over all the keys in the defaults table
for (String key : myDefaults.keySet()) { for (String key : defaults.keySet()) {
//The key is something like JButton.Enabled.backgroundPainter, //The key is something like JButton.Enabled.backgroundPainter,
//or JButton.States, or JButton.background. //or JButton.States, or JButton.background.
//Remove the "JButton." portion of the key //Remove the "JButton." portion of the key
@ -528,11 +385,11 @@ public final class NimbusStyle extends SynthStyle {
//otherwise, assume it is a property and install it on the //otherwise, assume it is a property and install it on the
//values object //values object
if ("contentMargins".equals(property)) { if ("contentMargins".equals(property)) {
v.contentMargins = (Insets)myDefaults.get(key); values.contentMargins = (Insets)defaults.get(key);
} else if ("States".equals(property)) { } else if ("States".equals(property)) {
//ignore //ignore
} else { } else {
v.defaults.put(property, myDefaults.get(key)); values.defaults.put(property, defaults.get(key));
} }
} else { } else {
//it is possible that the developer has a malformed UIDefaults //it is possible that the developer has a malformed UIDefaults
@ -582,13 +439,13 @@ public final class NimbusStyle extends SynthStyle {
//so put it in the UIDefaults associated with that runtime //so put it in the UIDefaults associated with that runtime
//state //state
if ("backgroundPainter".equals(property)) { if ("backgroundPainter".equals(property)) {
rs.backgroundPainter = (Painter)myDefaults.get(key); rs.backgroundPainter = getPainter(defaults, key);
} else if ("foregroundPainter".equals(property)) { } else if ("foregroundPainter".equals(property)) {
rs.foregroundPainter = (Painter) myDefaults.get(key); rs.foregroundPainter = getPainter(defaults, key);
} else if ("borderPainter".equals(property)) { } else if ("borderPainter".equals(property)) {
rs.borderPainter = (Painter) myDefaults.get(key); rs.borderPainter = getPainter(defaults, key);
} else { } else {
rs.defaults.put(property, myDefaults.get(key)); rs.defaults.put(property, defaults.get(key));
} }
} }
} }
@ -598,7 +455,15 @@ public final class NimbusStyle extends SynthStyle {
Collections.sort(runtimeStates, STATE_COMPARATOR); Collections.sort(runtimeStates, STATE_COMPARATOR);
//finally, set the array of runtime states on the values object //finally, set the array of runtime states on the values object
v.states = runtimeStates.toArray(new RuntimeState[runtimeStates.size()]); values.states = runtimeStates.toArray(new RuntimeState[runtimeStates.size()]);
}
private Painter getPainter(Map<String, Object> defaults, String key) {
Object p = defaults.get(key);
if (p instanceof UIDefaults.LazyValue) {
p = ((UIDefaults.LazyValue)p).createValue(UIManager.getDefaults());
}
return (p instanceof Painter ? (Painter)p : null);
} }
/** /**
@ -1245,15 +1110,4 @@ public final class NimbusStyle extends SynthStyle {
return hash; return hash;
} }
} }
/**
* This listener is used to listen to the UIDefaults tables and clear out
* the cached-precompiled map of defaults in that case.
*/
private static final class DefaultsListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
AppContext.getAppContext().put("NimbusStyle.defaults", null);
}
}
} }