org.jdesktop.swingx.plaf.UIManagerExt Maven / Gradle / Ivy
Show all versions of swingx-all Show documentation
/*
* $Id$
*
* Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jdesktop.swingx.plaf;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Insets;
import java.awt.Shape;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.Icon;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.BorderUIResource;
import javax.swing.plaf.ColorUIResource;
import javax.swing.plaf.DimensionUIResource;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.IconUIResource;
import javax.swing.plaf.InsetsUIResource;
import javax.swing.plaf.UIResource;
import org.jdesktop.swingx.painter.Painter;
import org.jdesktop.swingx.util.Contract;
/**
* A utility class for obtaining configuration properties from the
* {@code UIDefaults}. This class handles SwingX-specific L&F needs, such as
* the installation of painters and shapes. There are several categories of
* utility methods:
*
* - Support for the safe creation of {@code UIResource}s.
* - Support for new {@code UIResource} types, such as
* {@code PainterUIResource}.
* - Support for the dynamic localization of {@code UIDefaults}.
* - Support for returning non-{@code String} localizations from
* {@code ResourceBundle}s.
*
* Safe Methods
*
* The {@code getSafeXXX} methods are designed for use with
* {@code LookAndFeelAddon}s. Any addon that attempts to obtain a property
* defined in the defaults (available from {@code UIManager.get}) to set a
* property that will be added to the defaults for the addon should use the
* "safe" methods. The methods ensure that a valid value is always returned and
* that value is a {@code UIResource}.
*
* Support for New Types
*
* {@code UIManagerExt} supports the retrieval of new {@code UIResource} types.
* There is a {@code getXXX} method for every {@code UIResource} subtype in the
* {@code org.jdesktop.swingx.plaf} package.
*
* Support for Dynamic Localization
*
* {@code UIManagerExt} enables dynamic localization by supporting
* {@code ResourceBundle}s. The
* {@linkplain UIDefaults#addResourceBundle(String)} allows resource bundles to
* be added to the {@code UIDefaults}. While there is support for this feature
* in core, there is a bug with the class loader that prevents user added
* bundles from working correctly when used via Web Start. Therefore,
* {@code UIManagerExt} defines methods to add and remove resource bundles.
* These are the only methods that SwingX classes should use when adding
* resource bundles to the defaults. Since {@code UIManagerExt} is maintaining
* the bundles, any localized {@code String}s must be retrieved from
* the {@code getString} methods in this class.
*
* Support for Non-{@code String} Localization Values
*
* All methods work by first determining if the value is present
* {@code UIDefaults}. If the value is not present, then the installed
* {@code ResourceBundle}s are queried. {@code UIManagerExt} will attempt to
* convert any returned value to the appropriate type. For instance,
* {@code getInt} uses {@code Integer.decode} to convert {@code String}s
* returned from the bundle into {@code int}s.
*
*
* @author Karl George Schaefer
*
* @see UIManager
* @see UIDefaults
*/
@SuppressWarnings("nls")
public class UIManagerExt {
/**
* Used to replicate the resource bundle behavior from the
* {@code UIDefaults}.
*/
private static class UIDefaultsExt {
//use vector; we want synchronization
private Vector resourceBundles;
/**
* Maps from a Locale to a cached Map of the ResourceBundle. This is done
* so as to avoid an exception being thrown when a value is asked for.
* Access to this should be done while holding a lock on the
* UIDefaults, eg synchronized(this).
*/
private Map> resourceCache;
UIDefaultsExt() {
resourceCache = new HashMap>();
}
//should this just return String?
private Object getFromResourceBundle(Object key, Locale l) {
if( resourceBundles == null ||
resourceBundles.isEmpty() ||
!(key instanceof String) ) {
return null;
}
// A null locale means use the default locale.
if( l == null ) {
l = Locale.getDefault();
}
synchronized(this) {
return getResourceCache(l).get(key);
}
}
/**
* Returns a Map of the known resources for the given locale.
*/
private Map getResourceCache(Locale l) {
Map values = resourceCache.get(l);
if (values == null) {
values = new HashMap();
for (int i=resourceBundles.size()-1; i >= 0; i--) {
String bundleName = resourceBundles.get(i);
try {
ResourceBundle b = ResourceBundle.
getBundle(bundleName, l, UIManagerExt.class.getClassLoader());
Enumeration keys = b.getKeys();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
if (values.get(key) == null) {
Object value = b.getObject(key);
values.put(key, (String) value);
}
}
} catch( MissingResourceException mre ) {
// Keep looking
}
}
resourceCache.put(l, values);
}
return values;
}
public synchronized void addResourceBundle(String bundleName) {
if( bundleName == null ) {
return;
}
if( resourceBundles == null ) {
resourceBundles = new Vector(5);
}
if (!resourceBundles.contains(bundleName)) {
resourceBundles.add( bundleName );
resourceCache.clear();
}
}
public synchronized void removeResourceBundle( String bundleName ) {
if( resourceBundles != null ) {
resourceBundles.remove( bundleName );
}
resourceCache.clear();
}
}
private static UIDefaultsExt uiDefaultsExt = new UIDefaultsExt();
private UIManagerExt() {
//does nothing
}
/**
* Adds a resource bundle to the list of resource bundles that are searched
* for localized values. Resource bundles are searched in the reverse order
* they were added. In other words, the most recently added bundle is
* searched first.
*
* @param bundleName
* the base name of the resource bundle to be added
* @see java.util.ResourceBundle
* @see #removeResourceBundle
*/
public static void addResourceBundle(String bundleName) {
uiDefaultsExt.addResourceBundle(bundleName);
}
/**
* Removes a resource bundle from the list of resource bundles that are
* searched for localized defaults.
*
* @param bundleName
* the base name of the resource bundle to be removed
* @see java.util.ResourceBundle
* @see #addResourceBundle
*/
public static void removeResourceBundle(String bundleName) {
uiDefaultsExt.removeResourceBundle(bundleName);
}
/**
* Returns a string from the defaults. If the value for {@code key} is not a
* {@code String}, {@code null} is returned.
*
* @param key
* an {@code Object} specifying the string
* @return the {@code String} object
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static String getString(Object key) {
return getString(key, null);
}
/**
* Returns a string from the defaults. If the value for {@code key} is not a
* {@code String}, {@code null} is returned.
*
* @param key
* an {@code Object} specifying the string
* @param l
* the {@code Locale} for which the string is desired; refer
* to {@code UIDefaults} for details on how a {@code null}
* {@code Locale} is handled
* @return the {@code String} object
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static String getString(Object key, Locale l) {
Object value = UIManager.get(key, l);
if (value instanceof String) {
return (String) value;
}
//only return resource bundle if not in UIDefaults
if (value == null) {
value = uiDefaultsExt.getFromResourceBundle(key, l);
if (value instanceof String) {
return (String) value;
}
}
return null;
}
/**
* Returns an integer from the defaults. If the value for {@code key} is not
* an {@code int}, {@code 0} is returned.
*
* @param key
* an {@code Object} specifying the integer
* @return the {@code int}
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static int getInt(Object key) {
return getInt(key, null);
}
/**
* Returns an integer from the defaults. If the value for {@code key} is not
* an {@code int}, {@code 0} is returned.
*
* @param key
* an {@code Object} specifying the integer
* @param l
* the {@code Locale} for which the integer is desired; refer
* to {@code UIDefaults} for details on how a {@code null}
* {@code Locale} is handled
* @return the {@code int}
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static int getInt(Object key, Locale l) {
Object value = UIManager.get(key, l);
if (value instanceof Integer) {
return (Integer) value;
}
if (value == null) {
value = uiDefaultsExt.getFromResourceBundle(key, l);
if (value instanceof Integer) {
return (Integer) value;
}
if (value instanceof String) {
try {
return Integer.decode((String) value);
} catch (NumberFormatException e) {
// ignore - the entry was not parseable, can't do anything
// JW: should we log it?
}
}
}
return 0;
}
/**
* Returns an Boolean from the defaults. If the value for {@code key} is not
* a {@code boolean}, {@code false} is returned.
*
* @param key
* an {@code Object} specifying the Boolean
* @return the {@code boolean}
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static boolean getBoolean(Object key) {
return getBoolean(key, null);
}
/**
* Returns an Boolean from the defaults. If the value for {@code key} is not
* a {@code boolean}, {@code false} is returned.
*
* @param key
* an {@code Object} specifying the Boolean
* @param l
* the {@code Locale} for which the Boolean is desired; refer
* to {@code UIDefaults} for details on how a {@code null}
* {@code Locale} is handled
* @return the {@code boolean}
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static boolean getBoolean(Object key, Locale l) {
Object value = UIManager.get(key, l);
if (value instanceof Boolean) {
return (Boolean) value;
}
//only return resource bundle if not in UIDefaults
if (value == null) {
value = uiDefaultsExt.getFromResourceBundle(key, l);
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof String) {
return Boolean.valueOf((String) value);
}
}
return false;
}
/**
* Returns a color from the defaults. If the value for {@code key} is not
* a {@code Color}, {@code null} is returned.
*
* @param key
* an {@code Object} specifying the color
* @return the {@code Color} object
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static Color getColor(Object key) {
return getColor(key, null);
}
/**
* Returns a color from the defaults. If the value for {@code key} is not
* a {@code Color}, {@code null} is returned.
*
* @param key
* an {@code Object} specifying the color
* @param l
* the {@code Locale} for which the color is desired; refer
* to {@code UIDefaults} for details on how a {@code null}
* {@code Locale} is handled
* @return the {@code Color} object
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static Color getColor(Object key, Locale l) {
Object value = UIManager.get(key, l);
if (value instanceof Color) {
return (Color) value;
}
//only return resource bundle if not in UIDefaults
if (value == null) {
value = uiDefaultsExt.getFromResourceBundle(key, l);
if (value instanceof Color) {
return (Color) value;
}
if (value instanceof String) {
try {
return Color.decode((String) value);
} catch (NumberFormatException e) {
// incorrect format; does nothing
}
}
}
return null;
}
//TODO: Font.decode always returns a valid font. This is not acceptable for UIManager
// /**
// * Returns a font from the defaults. If the value for {@code key} is not
// * a {@code Font}, {@code null} is returned.
// *
// * @param key
// * an {@code Object} specifying the font
// * @return the {@code Font} object
// * @throws NullPointerException
// * if {@code key} is {@code null}
// */
// public static Font getFont(Object key) {
// return getFont(key, null);
// }
//
// /**
// * Returns a font from the defaults. If the value for {@code key} is not
// * a {@code Font}, {@code null} is returned.
// *
// * @param key
// * an {@code Object} specifying the font
// * @param l
// * the {@code Locale} for which the font is desired; refer
// * to {@code UIDefaults} for details on how a {@code null}
// * {@code Locale} is handled
// * @return the {@code Font} object
// * @throws NullPointerException
// * if {@code key} is {@code null}
// */
// public static Font getFont(Object key, Locale l) {
// Object value = UIManager.get(key, l);
//
// if (value instanceof Font) {
// return (Font) value;
// }
//
// //only return resource bundle if not in UIDefaults
// if (value == null) {
// value = uiDefaultsExt.getFromResourceBundle(key, l);
//
// if (value instanceof Font) {
// return (Font) value;
// }
//
// if (value instanceof String) {
// return Font.decode((String) value);
// }
// }
//
// return null;
// }
/**
* Returns a shape from the defaults. If the value for {@code key} is not a
* {@code Shape}, {@code null} is returned.
*
* @param key an {@code Object} specifying the shape
* @return the {@code Shape} object
* @throws NullPointerException if {@code key} is {@code null}
*/
public static Shape getShape(Object key) {
Object value = UIManager.getDefaults().get(key);
return (value instanceof Shape) ? (Shape) value : null;
}
/**
* Returns a shape from the defaults that is appropriate for the given
* locale. If the value for {@code key} is not a {@code Shape},
* {@code null} is returned.
*
* @param key
* an {@code Object} specifying the shape
* @param l
* the {@code Locale} for which the shape is desired; refer
* to {@code UIDefaults} for details on how a {@code null}
* {@code Locale} is handled
* @return the {@code Shape} object
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static Shape getShape(Object key, Locale l) {
Object value = UIManager.getDefaults().get(key, l);
return (value instanceof Shape) ? (Shape) value : null;
}
/**
* Returns a painter from the defaults. If the value for {@code key} is not
* a {@code Painter}, {@code null} is returned.
*
* @param key
* an {@code Object} specifying the painter
* @return the {@code Painter} object
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static Painter> getPainter(Object key) {
Object value = UIManager.getDefaults().get(key);
return (value instanceof Painter>) ? (Painter>) value : null;
}
/**
* Returns a painter from the defaults that is appropriate for the given
* locale. If the value for {@code key} is not a {@code Painter},
* {@code null} is returned.
*
* @param key
* an {@code Object} specifying the painter
* @param l
* the {@code Locale} for which the painter is desired; refer
* to {@code UIDefaults} for details on how a {@code null}
* {@code Locale} is handled
* @return the {@code Painter} object
* @throws NullPointerException
* if {@code key} is {@code null}
*/
public static Painter> getPainter(Object key, Locale l) {
Object value = UIManager.getDefaults().get(key, l);
return (value instanceof Painter>) ? (Painter>) value : null;
}
/**
* Returns a border from the defaults. If the value for {@code key} is not a
* {@code Border}, {@code defaultBorder} is returned.
*
* @param key
* an {@code Object} specifying the border
* @param defaultBorder
* the border to return if the border specified by
* {@code key} does not exist
* @return the {@code Border} object
* @throws NullPointerException
* if {@code key} or {@code defaultBorder} is {@code null}
*/
public static Border getSafeBorder(Object key, Border defaultBorder) {
Contract.asNotNull(defaultBorder, "defaultBorder cannot be null");
Border safeBorder = UIManager.getBorder(key);
if (safeBorder == null) {
safeBorder = defaultBorder;
}
if (!(safeBorder instanceof UIResource)) {
safeBorder = new BorderUIResource(safeBorder);
}
return safeBorder;
}
/**
* Returns a color from the defaults. If the value for {@code key} is not a
* {@code Color}, {@code defaultColor} is returned.
*
* @param key
* an {@code Object} specifying the color
* @param defaultColor
* the color to return if the color specified by {@code key}
* does not exist
* @return the {@code Color} object
* @throws NullPointerException
* if {@code key} or {@code defaultColor} is {@code null}
*/
public static Color getSafeColor(Object key, Color defaultColor) {
Contract.asNotNull(defaultColor, "defaultColor cannot be null");
Color safeColor = UIManager.getColor(key);
if (safeColor == null) {
safeColor = defaultColor;
}
if (!(safeColor instanceof UIResource)) {
safeColor = new ColorUIResource(safeColor);
}
return safeColor;
}
/**
* Returns a dimension from the defaults. If the value for {@code key} is
* not a {@code Dimension}, {@code defaultDimension} is returned.
*
* @param key
* an {@code Object} specifying the dimension
* @param defaultDimension
* the dimension to return if the dimension specified by
* {@code key} does not exist
* @return the {@code Dimension} object
* @throws NullPointerException
* if {@code key} or {@code defaultColor} is {@code null}
*/
public static Dimension getSafeDimension(Object key, Dimension defaultDimension) {
Contract.asNotNull(defaultDimension, "defaultDimension cannot be null");
Dimension safeDimension = UIManager.getDimension(key);
if (safeDimension == null) {
safeDimension = defaultDimension;
}
if (!(safeDimension instanceof UIResource)) {
safeDimension = new DimensionUIResource(safeDimension.width, safeDimension.height);
}
return safeDimension;
}
/**
* Returns a font from the defaults. If the value for {@code key} is not a
* {@code Font}, {@code defaultFont} is returned.
*
* @param key
* an {@code Object} specifying the font
* @param defaultFont
* the font to return if the font specified by {@code key}
* does not exist
* @return the {@code Font} object
* @throws NullPointerException
* if {@code key} or {@code defaultFont} is {@code null}
*/
public static Font getSafeFont(Object key, Font defaultFont) {
Contract.asNotNull(defaultFont, "defaultFont cannot be null");
Font safeFont = UIManager.getFont(key);
if (safeFont == null) {
safeFont = defaultFont;
}
if (!(safeFont instanceof UIResource)) {
safeFont = new FontUIResource(safeFont);
}
return safeFont;
}
/**
* Returns an icon from the defaults. If the value for {@code key} is not a
* {@code Icon}, {@code defaultIcon} is returned.
*
* @param key
* an {@code Object} specifying the icon
* @param defaultIcon
* the icon to return if the icon specified by {@code key}
* does not exist
* @return the {@code Icon} object
* @throws NullPointerException
* if {@code key} or {@code defaultIcon} is {@code null}
*/
public static Icon getSafeIcon(Object key, Icon defaultIcon) {
Contract.asNotNull(defaultIcon, "defaultIcon cannot be null");
Icon safeIcon = UIManager.getIcon(key);
if (safeIcon == null) {
safeIcon = defaultIcon;
}
if (!(safeIcon instanceof UIResource)) {
safeIcon = new IconUIResource(safeIcon);
}
return safeIcon;
}
/**
* Returns an insets from the defaults. If the value for {@code key} is not
* a {@code Insets}, {@code defaultInsets} is returned.
*
* @param key
* an {@code Object} specifying the insets
* @param defaultInsets
* the insets to return if the insets specified by
* {@code key} does not exist
* @return the {@code Insets} object
* @throws NullPointerException
* if {@code key} or {@code defaultInsets} is {@code null}
*/
public static Insets getSafeInsets(Object key, Insets defaultInsets) {
Contract.asNotNull(defaultInsets, "defaultInsets cannot be null");
Insets safeInsets = UIManager.getInsets(key);
if (safeInsets == null) {
safeInsets = defaultInsets;
}
if (!(safeInsets instanceof UIResource)) {
safeInsets = new InsetsUIResource(safeInsets.top, safeInsets.left,
safeInsets.bottom, safeInsets.right);
}
return safeInsets;
}
}