While playing with some custom L&Fs I noticed some flaws in our
UIManager impl. It seems like the UIManager should not only manage 1
UIDefaults instance (from the L&F), but another one, to which
applications can make modifications. The latter one overloads the L&F
defaults. The lookup has to first check the 'user' defaults, if the key
isn't there, it falls back to the L&F defaults. So, calling
UIManager.put("blah", null) doesn't necessarily lead to
UIManager.get("blah") == null, when that key is still in the L&F
defaults, then the value from there is returned.
I implemented this with a special subclass of UIDefaults, which does
exactly this kind of multiplexing. This brings us a little forward with
custom L&Fs. Unfortunately some of them are still not usable due to
missing Font.create().
2006-06-21 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/UIManager.java
(MultiplexUIDefaults): New inner class.
(currentUIDefaults): Changed type to be MultiplexUIDefaults.
(userUIDefaults): Changed name to be lookAndFeelDefaults.
(<cinit>): Call setLookAndFeel(String) instead of trying to load
directly. Print stacktrace if something goes wrong.
(get): Delegate call to currentUIDefaults.
(getDefaults): If currentUIDefaults is null, then lazily
instantiate
it.
(getUI): Delegate call to currentUIDefaults.
(put): Delegate call to currentUIDefaults.
(setLookAndFeel): Initialize currentUIDefaults with
MultiplexUIDefaults. Set lookAndFeelDefaults.
(setLookAndFeel): Use current thread's context classloader for
loading the L&F.
/Roman
--
“Improvement makes straight roads, but the crooked roads, without
Improvement, are roads of Genius.” - William Blake
Index: javax/swing/UIManager.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/UIManager.java,v
retrieving revision 1.35
diff -u -1 -0 -r1.35 UIManager.java
--- javax/swing/UIManager.java 1 Jun 2006 04:38:49 -0000 1.35
+++ javax/swing/UIManager.java 21 Jun 2006 09:56:39 -0000
@@ -38,20 +38,21 @@
package javax.swing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.Serializable;
+import java.util.Enumeration;
import java.util.Locale;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalLookAndFeel;
/**
* Manages the current [EMAIL PROTECTED] LookAndFeel} and any auxiliary [EMAIL PROTECTED] LookAndFeel}
* instances.
*/
@@ -110,65 +111,148 @@
s.append(getClass().getName());
s.append('[');
s.append(getName());
s.append(' ');
s.append(getClassName());
s.append(']');
return s.toString();
}
}
+ /**
+ * A UIDefaults subclass that multiplexes between itself and a 'fallback'
+ * UIDefaults instance. This is used to protect the L&F UIDefaults from beeing
+ * overwritten by applications.
+ */
+ private static class MultiplexUIDefaults
+ extends UIDefaults
+ {
+ private class MultiplexEnumeration
+ implements Enumeration
+ {
+ Enumeration[] enums;
+ int i;
+ MultiplexEnumeration(Enumeration e1, Enumeration e2)
+ {
+ enums = new Enumeration[]{ e1, e2 };
+ i = 0;
+ }
+
+ public boolean hasMoreElements()
+ {
+ return enums[i].hasMoreElements() || i < enums.length - 1;
+ }
+
+ public Object nextElement()
+ {
+ Object val = enums[i].nextElement();
+ if (! enums[i].hasMoreElements() && i < enums.length - 1)
+ i++;
+ return val;
+ }
+
+ }
+
+ UIDefaults fallback;
+
+ MultiplexUIDefaults(UIDefaults d)
+ {
+ fallback = d;
+ }
+
+ public Object get(Object key)
+ {
+ Object val = super.get(key);
+ if (val == null)
+ val = fallback.get(key);
+ return val;
+ }
+
+ public Object get(Object key, Locale l)
+ {
+ Object val = super.get(key, l);
+ if (val == null)
+ val = fallback.get(key, l);
+ return val;
+ }
+
+ public Object remove(Object key)
+ {
+ Object val = super.remove(key);
+ if (val == null)
+ val = fallback.remove(key);
+ return val;
+ }
+
+ public void clear()
+ {
+ super.clear();
+ fallback.clear();
+ }
+
+ public int size()
+ {
+ return super.size() + fallback.size();
+ }
+
+ public Enumeration keys()
+ {
+ return new MultiplexEnumeration(super.keys(), fallback.keys());
+ }
+
+ public Enumeration elements()
+ {
+ return new MultiplexEnumeration(super.elements(), fallback.elements());
+ }
+ }
+
private static final long serialVersionUID = -5547433830339189365L;
/** The installed look and feel(s). */
static LookAndFeelInfo [] installed = {
new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"),
new LookAndFeelInfo("GNU", "gnu.javax.swing.plaf.gnu.GNULookAndFeel")
};
/** The installed auxiliary look and feels. */
static LookAndFeel[] auxLookAndFeels;
/** The current look and feel. */
static LookAndFeel currentLookAndFeel;
- static UIDefaults currentUIDefaults;
+ static MultiplexUIDefaults currentUIDefaults;
- /**
- * UIDefaults set by the user.
- */
- static UIDefaults userUIDefaults;
+ static UIDefaults lookAndFeelDefaults;
/** Property change listener mechanism. */
static PropertyChangeSupport listeners
= new PropertyChangeSupport(UIManager.class);
static
{
String defaultlaf = System.getProperty("swing.defaultlaf");
try
{
if (defaultlaf != null)
{
- Class lafClass = Class.forName(defaultlaf);
- LookAndFeel laf = (LookAndFeel) lafClass.newInstance();
- setLookAndFeel(laf);
+ setLookAndFeel(defaultlaf);
}
else
{
setLookAndFeel(new MetalLookAndFeel());
}
}
catch (Exception ex)
{
System.err.println("cannot initialize Look and Feel: " + defaultlaf);
System.err.println("error: " + ex.toString());
+ ex.printStackTrace();
System.err.println("falling back to Metal Look and Feel");
try
{
setLookAndFeel(new MetalLookAndFeel());
}
catch (Exception ex2)
{
throw (Error) new AssertionError("There must be no problem installing"
+ " the MetalLookAndFeel.")
.initCause(ex2);
@@ -305,44 +389,34 @@
/**
* Returns an object from the [EMAIL PROTECTED] UIDefaults} table for the current
* [EMAIL PROTECTED] LookAndFeel}.
*
* @param key the key.
*
* @return The object.
*/
public static Object get(Object key)
{
- Object val = null;
- if (userUIDefaults != null)
- val = userUIDefaults.get(key);
- if (val == null)
- val = getLookAndFeelDefaults().get(key);
- return val;
+ return getDefaults().get(key);
}
/**
* Returns an object from the [EMAIL PROTECTED] UIDefaults} table for the current
* [EMAIL PROTECTED] LookAndFeel}.
*
* @param key the key.
*
* @return The object.
*/
public static Object get(Object key, Locale locale)
{
- Object val = null;
- if (userUIDefaults != null)
- val = userUIDefaults.get(key, locale);
- if (val == null)
- val = getLookAndFeelDefaults().get(key, locale);
- return val;
+ return getDefaults().get(key, locale);
}
/**
* Returns a boolean value from the defaults table,
* <code>false</code> if key is not present.
*
* @since 1.4
*/
public static boolean getBoolean(Object key)
{
@@ -407,20 +481,22 @@
return "javax.swing.plaf.metal.MetalLookAndFeel";
}
/**
* Returns the default values for this look and feel.
*
* @return The [EMAIL PROTECTED] UIDefaults} for the current [EMAIL PROTECTED] LookAndFeel}.
*/
public static UIDefaults getDefaults()
{
+ if (currentUIDefaults == null)
+ currentUIDefaults = new MultiplexUIDefaults(null);
return currentUIDefaults;
}
/**
* Returns a dimension from the defaults table.
*/
public static Dimension getDimension(Object key)
{
return (Dimension) get(key);
}
@@ -580,27 +656,21 @@
}
/**
* Returns UI delegate from the current [EMAIL PROTECTED] LookAndFeel} that renders the
* target component.
*
* @param target the target component.
*/
public static ComponentUI getUI(JComponent target)
{
- ComponentUI ui = null;
- if (userUIDefaults != null
- && userUIDefaults.get(target.getUIClassID()) != null)
- ui = userUIDefaults.getUI(target);
- if (ui == null)
- ui = currentUIDefaults.getUI(target);
- return ui;
+ return getDefaults().getUI(target);
}
/**
* Creates a new look and feel and adds it to the current array.
*
* @param name the look and feel name.
* @param className the fully qualified name of the class that implements the
* look and feel.
*/
public static void installLookAndFeel(String name, String className)
@@ -618,25 +688,21 @@
System.arraycopy(installed, 0, newInstalled, 0, installed.length);
newInstalled[newInstalled.length - 1] = info;
setInstalledLookAndFeels(newInstalled);
}
/**
* Stores an object in the defaults table.
*/
public static Object put(Object key, Object value)
{
- Object old = get(key);
- if (userUIDefaults == null)
- userUIDefaults = new UIDefaults();
- userUIDefaults.put(key, value);
- return old;
+ return getDefaults().put(key, value);
}
/**
* Replaces the current array of installed LookAndFeelInfos.
*/
public static void setInstalledLookAndFeels(UIManager.LookAndFeelInfo[] infos)
{
installed = infos;
}
@@ -657,21 +723,26 @@
throw new UnsupportedLookAndFeelException(newLookAndFeel.getName());
LookAndFeel oldLookAndFeel = currentLookAndFeel;
if (oldLookAndFeel != null)
oldLookAndFeel.uninitialize();
// Set the current default look and feel using a LookAndFeel object.
currentLookAndFeel = newLookAndFeel;
if (newLookAndFeel != null)
{
newLookAndFeel.initialize();
- currentUIDefaults = newLookAndFeel.getDefaults();
+ lookAndFeelDefaults = newLookAndFeel.getDefaults();
+ if (currentUIDefaults == null)
+ currentUIDefaults =
+ new MultiplexUIDefaults(lookAndFeelDefaults);
+ else
+ currentUIDefaults.fallback = lookAndFeelDefaults;
}
else
{
currentUIDefaults = null;
}
listeners.firePropertyChange("lookAndFeel", oldLookAndFeel, newLookAndFeel);
//revalidate();
//repaint();
}
@@ -682,15 +753,16 @@
*
* @throws UnsupportedLookAndFeelException if the look and feel is not
* supported on the current platform.
*
* @see LookAndFeel#isSupportedLookAndFeel()
*/
public static void setLookAndFeel(String className)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
UnsupportedLookAndFeelException
{
- Class c = Class.forName(className);
+ Class c = Class.forName(className, true,
+ Thread.currentThread().getContextClassLoader());
LookAndFeel a = (LookAndFeel) c.newInstance(); // throws class-cast-exception
setLookAndFeel(a);
}
}
signature.asc
Description: Dies ist ein digital signierter Nachrichtenteil
