com.jgoodies.looks.windows.WindowsLookAndFeel.java Source code

Java tutorial

Introduction

Here is the source code for com.jgoodies.looks.windows.WindowsLookAndFeel.java

Source

/*
 * Copyright (c) 2001-2013 JGoodies Software GmbH. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of JGoodies Software GmbH nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package com.jgoodies.looks.windows;

import java.awt.Color;
import java.awt.Font;
import java.awt.Insets;
import java.lang.reflect.Method;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.IconUIResource;
import javax.swing.plaf.InsetsUIResource;
import javax.swing.plaf.basic.BasicBorders;

import com.jgoodies.common.base.SystemUtils;
import com.jgoodies.looks.FontPolicies;
import com.jgoodies.looks.FontPolicy;
import com.jgoodies.looks.FontSet;
import com.jgoodies.looks.LookUtils;
import com.jgoodies.looks.MicroLayout;
import com.jgoodies.looks.MicroLayoutPolicies;
import com.jgoodies.looks.MicroLayoutPolicy;
import com.jgoodies.looks.Options;
import com.jgoodies.looks.common.MinimumSizedIcon;
import com.jgoodies.looks.common.RGBGrayFilter;
import com.jgoodies.looks.common.ShadowPopupFactory;

/**
 * The main class of the JGoodies Windows Look&Feel.
 * This look provides several corrections and extensions to Sun's Windows L&F.
 * In addition it tries to provide a unified look for the J2SE 1.4.0x, 1.4.1x,
 * 1.4.2, and 1.5 environments.
 *
 * @author Karsten Lentzsch
 * @version $Revision: 1.48 $
 */
public class WindowsLookAndFeel extends com.sun.java.swing.plaf.windows.WindowsLookAndFeel {

    /**
     * An optional client property key for JMenu and JToolBar
     * to set a border style - shadows the header style.
     */
    public static final String BORDER_STYLE_KEY = "jgoodies.windows.borderStyle";

    /**
     * Returns the String "JGoodies Windows".
     * Note that this L&f's ID ("Windows") is inherited
     * from the superclass, because we don't do any fundamental changes.
     *
     * @see LookAndFeel#getID()
     */
    @Override
    public String getName() {
        return "JGoodies Windows";
    }

    @Override
    public String getDescription() {
        return "The JGoodies Windows Look and Feel" + " - \u00a9 2001-2013 JGoodies Software GmbH";
    }

    // Optional Settings ******************************************************

    /**
     * Looks up and retrieves the FontPolicy used by
     * the JGoodies Windows Look&Feel.
     * If a FontPolicy has been set for this look, it'll be returned.
     * Otherwise, this method checks if a FontPolicy or FontSet is defined
     * in the system properties or UIDefaults. If so, it is returned.
     * If no FontPolicy has been set for this look, in the system
     * properties or UIDefaults, the default Windows font policy
     * will be returned.
     *
     * @return the FontPolicy set for this Look&feel - if any,
     *     the FontPolicy specified in the system properties or UIDefaults
     *     - if any, or the default Windows font policy.
     *
     * @see #setFontPolicy
     * @see Options#WINDOWS_FONT_POLICY_KEY
     * @see FontPolicies
     * @see FontPolicies#customSettingsPolicy(FontPolicy)
     * @see FontPolicies#getDefaultWindowsPolicy()
     */
    public static FontPolicy getFontPolicy() {
        FontPolicy policy = (FontPolicy) UIManager.get(Options.WINDOWS_FONT_POLICY_KEY);
        if (policy != null) {
            return policy;
        }

        FontPolicy defaultPolicy = FontPolicies.getDefaultWindowsPolicy();
        return FontPolicies.customSettingsPolicy(defaultPolicy);
    }

    /**
     * Sets the FontPolicy to be used with the JGoodies Windows L&F.
     * If the specified policy is {@code null}, the default will be reset.
     *
     * @param fontPolicy   the FontPolicy to be used with the JGoodies Windows
     *     L&F, or {@code null} to reset to the default
     *
     * @see #getFontPolicy()
     * @see Options#WINDOWS_FONT_POLICY_KEY
     */
    public static void setFontPolicy(FontPolicy fontPolicy) {
        UIManager.put(Options.WINDOWS_FONT_POLICY_KEY, fontPolicy);
    }

    /**
     * Looks up and retrieves the MicroLayoutPolicy used by
     * the JGoodies Windows Look&Feel.
     * If a MicroLayoutPolicy has been set for this look, it'll be returned.
     * Otherwise, the default Windows micro layout policy will be returned.
     *
     * @return the MicroLayoutPolicy set for this Look&feel - if any,
     *     or the default Windows MicroLayoutPolicy.
     *
     * @see #setMicroLayoutPolicy
     * @see Options#WINDOWS_MICRO_LAYOUT_POLICY_KEY
     * @see MicroLayoutPolicies
     * @see MicroLayoutPolicies#getDefaultWindowsPolicy()
     */
    public static MicroLayoutPolicy getMicroLayoutPolicy() {
        MicroLayoutPolicy policy = (MicroLayoutPolicy) UIManager.get(Options.WINDOWS_MICRO_LAYOUT_POLICY_KEY);
        return policy != null ? policy : MicroLayoutPolicies.getDefaultWindowsPolicy();
    }

    /**
     * Sets the MicroLayoutPolicy to be used with the JGoodies Windows L&F.
     * If the specified policy is {@code null}, the default will be reset.
     *
     * @param microLayoutPolicy   the MicroLayoutPolicy to be used with
     *     the JGoodies Windows L&F, or {@code null} to reset
     *     to the default
     *
     * @see #getMicroLayoutPolicy()
     * @see Options#WINDOWS_MICRO_LAYOUT_POLICY_KEY
     */
    public static void setMicroLayoutPolicy(MicroLayout microLayoutPolicy) {
        UIManager.put(Options.WINDOWS_MICRO_LAYOUT_POLICY_KEY, microLayoutPolicy);
    }

    // Overriding Superclass Behavior ***************************************

    /**
     * Invoked during {@code UIManager#setLookAndFeel}. In addition
     * to the superclass behavior, we install the ShadowPopupFactory.
     *
     * @see #uninitialize
     */
    @Override
    public void initialize() {
        super.initialize();
        ShadowPopupFactory.install();
    }

    /**
     * Invoked during {@code UIManager#setLookAndFeel}. In addition
     * to the superclass behavior, we uninstall the ShadowPopupFactory.
     *
     * @see #initialize
     */
    @Override
    public void uninitialize() {
        super.uninitialize();
        ShadowPopupFactory.uninstall();
    }

    /**
     * Returns an icon with a disabled appearance. This method is used
     * to generate a disabled icon when one has not been specified.<p>
     *
     * This method will be used only on JDK 5.0 and later.
     *
     * @param component the component that will display the icon, may be null.
     * @param icon the icon to generate disabled icon from.
     * @return disabled icon, or null if a suitable icon can not be generated.
     */
    @Override
    public Icon getDisabledIcon(JComponent component, Icon icon) {
        Icon disabledIcon = RGBGrayFilter.getDisabledIcon(component, icon);
        return disabledIcon != null ? new IconUIResource(disabledIcon) : null;
    }

    /**
     * Initializes the class defaults, that is, overrides some UI delegates
     * with JGoodies Windows implementations.
     */
    @Override
    protected void initClassDefaults(UIDefaults table) {
        super.initClassDefaults(table);
        final String windowsPrefix = "com.jgoodies.looks.windows.Windows";
        final String commonPrefix = "com.jgoodies.looks.common.ExtBasic";

        // Overwrite some of the uiDefaults.
        Object[] uiDefaults = {
                // Modified size
                "ComboBoxUI", windowsPrefix + "ComboBoxUI",

                // Modified preferred height: can be even or odd
                "ButtonUI", windowsPrefix + "ButtonUI",

                // Can installs an optional etched border
                "ScrollPaneUI", windowsPrefix + "ScrollPaneUI",

                // Optional style and optional special borders
                "MenuBarUI", windowsPrefix + "MenuBarUI",

                // Provides an option for a no margin border
                "PopupMenuUI", windowsPrefix + "PopupMenuUI",

                // Honors the screen resolution and uses a minimum button width
                "OptionPaneUI", windowsPrefix + "OptionPaneUI",

                // 1.4.1 has ugly one touch triangles
                "SplitPaneUI", windowsPrefix + "SplitPaneUI",

                // Work in progress: Can have a flat presentation
                "TabbedPaneUI", windowsPrefix + "TabbedPaneUI",

                // Selects text after focus gained via keyboard
                "TextFieldUI", windowsPrefix + "TextFieldUI", "FormattedTextFieldUI",
                windowsPrefix + "FormattedTextFieldUI",

                // Selects text after focus gained via keyboard
                // Renders a dot, not the star ("*") character on Java 1.4 and 5
                "PasswordFieldUI", windowsPrefix + "PasswordFieldUI",

                // Updates the disabled and inactive background
                "TextAreaUI", windowsPrefix + "TextAreaUI",

                // Corrected position of the tree button icon
                "TreeUI", windowsPrefix + "TreeUI",

                // Just to use shared UI delegate
                "SeparatorUI", windowsPrefix + "SeparatorUI",

                "SpinnerUI", windowsPrefix + "SpinnerUI" };

        // Aligned menu items
        if (!SystemUtils.IS_OS_WINDOWS_6_OR_LATER || !SystemUtils.IS_LAF_WINDOWS_XP_ENABLED) {
            uiDefaults = append(uiDefaults, "MenuItemUI", windowsPrefix + "MenuItemUI");
            uiDefaults = append(uiDefaults, "CheckBoxMenuItemUI", commonPrefix + "CheckBoxMenuItemUI");
            uiDefaults = append(uiDefaults, "RadioButtonMenuItemUI", commonPrefix + "RadioButtonMenuItemUI");
            // Has padding above and below the separator lines
            uiDefaults = append(uiDefaults, "PopupMenuSeparatorUI", commonPrefix + "PopupMenuSeparatorUI");
        }

        if (SystemUtils.IS_LAF_WINDOWS_XP_ENABLED) {
            // Aligned menu items
            if (!SystemUtils.IS_OS_WINDOWS_6_OR_LATER) {
                uiDefaults = append(uiDefaults, "MenuUI", windowsPrefix + "XPMenuUI");
            }

            // Optional style and optional special borders;
            // rollover borders for compound buttons
            uiDefaults = append(uiDefaults, "ToolBarUI", windowsPrefix + "XPToolBarUI");

            // Honors XP table header style for custom user renderers.
            uiDefaults = append(uiDefaults, "TableHeaderUI", windowsPrefix + "XPTableHeaderUI");
        } else {
            // Aligned menu items
            uiDefaults = append(uiDefaults, "MenuUI", commonPrefix + "MenuUI");

            // Optional style and optional special borders;
            // rollover borders corrected
            uiDefaults = append(uiDefaults, "ToolBarUI", windowsPrefix + "ToolBarUI");

            // Black arrows
            uiDefaults = append(uiDefaults, "ScrollBarUI", windowsPrefix + "ScrollBarUI");
        }
        table.putDefaults(uiDefaults);
    }

    /**
     * Initializes the component defaults.
     */
    @Override
    protected void initComponentDefaults(UIDefaults table) {
        super.initComponentDefaults(table);

        final boolean isXP = SystemUtils.IS_LAF_WINDOWS_XP_ENABLED;
        final boolean isClassic = !isXP;
        final boolean isVista = SystemUtils.IS_OS_WINDOWS_6_OR_LATER;

        initFontDefaults(table);

        if (isClassic) {
            initComponentDefaultsClassic(table);
        }
        MicroLayout microLayout = getMicroLayoutPolicy().getMicroLayout("Windows", table);
        if (!isVista || !SystemUtils.IS_LAF_WINDOWS_XP_ENABLED) {
            initMenuItemDefaults(table, microLayout);
        }

        Object marginBorder = new BasicBorders.MarginBorder();
        Object checkBoxMargin = microLayout.getCheckBoxMargin();

        Object etchedBorder = new UIDefaults.ProxyLazyValue("javax.swing.plaf.BorderUIResource",
                "getEtchedBorderUIResource");
        Object buttonBorder = new SimpleProxyLazyValue("com.jgoodies.looks.windows.WindowsLookAndFeel",
                "getButtonBorder");

        Object menuBorder = isXP ? WindowsBorders.getXPMenuBorder() : WindowsBorders.getMenuBorder();

        Object menuBarEmptyBorder = marginBorder;
        Object menuBarSeparatorBorder = WindowsBorders.getSeparatorBorder();
        Object menuBarEtchedBorder = WindowsBorders.getEtchedBorder();
        Object menuBarHeaderBorder = WindowsBorders.getMenuBarHeaderBorder();

        Object toolBarEmptyBorder = marginBorder;
        Object toolBarSeparatorBorder = WindowsBorders.getSeparatorBorder();
        Object toolBarEtchedBorder = WindowsBorders.getEtchedBorder();
        Object toolBarHeaderBorder = WindowsBorders.getToolBarHeaderBorder();

        Object buttonMargin = microLayout.getButtonMargin();

        Object toolBarSeparatorSize = null;

        Object textInsets = microLayout.getTextInsets();
        Object wrappedTextInsets = microLayout.getWrappedTextInsets();
        Insets comboEditorInsets = microLayout.getComboBoxEditorInsets();

        int comboBorderSize = microLayout.getComboBorderSize();
        int comboPopupBorderSize = microLayout.getComboPopupBorderSize();
        int comboRendererGap = comboEditorInsets.left + comboBorderSize - comboPopupBorderSize;
        Object comboRendererBorder = new EmptyBorder(1, comboRendererGap, 1, comboRendererGap);
        Object comboTableEditorInsets = new Insets(0, 0, 0, 0);

        Object popupMenuSeparatorMargin = microLayout.getPopupMenuSeparatorMargin();

        //    Should be active.
        int treeFontSize = table.getFont("Tree.font").getSize();
        Integer rowHeight = new Integer(treeFontSize + 6);

        Color controlColor = table.getColor("control");
        Object disabledTextBackground = table.getColor("TextField.disabledBackground");
        Object inactiveTextBackground = table.getColor("TextField.inactiveBackground");

        Object comboBoxDisabledBackground = isVista && isXP ? table.getColor("ComboBox.background")
                : disabledTextBackground;

        Object menuBarBackground = isXP ? table.get("control") : table.get("menu");
        Object menuSelectionBackground = isXP ? table.get("MenuItem.selectionBackground")
                : table.get("Menu.background");
        Object menuSelectionForeground = isXP ? table.get("MenuItem.selectionForeground")
                : table.get("Menu.foreground");

        Character passwordEchoChar = new Character(isXP ? '\u25CF' : '*');

        Object[] defaults = { "Button.border", buttonBorder, "Button.margin", buttonMargin, // Sun's 14px margin is too wide

                // 1.4.2 uses a 2 pixel non-standard border that leads to bad
                // alignment in the typical case that the border is not painted
                "CheckBox.border", marginBorder, "CheckBox.margin", checkBoxMargin,

                "ComboBox.disabledBackground", comboBoxDisabledBackground, "ComboBox.editorBorder", marginBorder,
                "ComboBox.editorColumns", new Integer(5), "ComboBox.editorInsets", comboEditorInsets, // Added by JGoodies
                "ComboBox.tableEditorInsets", comboTableEditorInsets, "ComboBox.rendererBorder",
                comboRendererBorder, // Added by JGoodies

                "EditorPane.margin", wrappedTextInsets,

                // Begin 1.3 und 1.4.0
                "Menu.border", menuBorder, // Fixed in 1.4.1
                "Menu.borderPainted", Boolean.TRUE, "Menu.background", menuBarBackground,
                "Menu.selectionForeground", menuSelectionForeground, "Menu.selectionBackground",
                menuSelectionBackground,
                // End 1.3 und 1.4.0

                "MenuBar.background", menuBarBackground, "MenuBar.border", menuBarSeparatorBorder, // 1.4.1 Separator wrong
                "MenuBar.emptyBorder", menuBarEmptyBorder, // Added by JGoodies
                "MenuBar.separatorBorder", menuBarSeparatorBorder, // Added by JGoodies
                "MenuBar.etchedBorder", menuBarEtchedBorder, // Added by JGoodies
                "MenuBar.headerBorder", menuBarHeaderBorder, // Added by JGoodies

                "FormattedTextField.disabledBackground", disabledTextBackground, // for readonly
                "FormattedTextField.inactiveBackground", inactiveTextBackground, // for readonly
                "FormattedTextField.margin", textInsets, // Poor in 1.6

                "PasswordField.margin", textInsets, // Poor in 1.6
                "PasswordField.echoChar", passwordEchoChar,

                "PopupMenu.border", WindowsBorders.getPopupMenuBorder(), "PopupMenu.noMarginBorder",
                WindowsBorders.getNoMarginPopupMenuBorder(), "PopupMenuSeparator.margin", popupMenuSeparatorMargin,

                "ScrollPane.etchedBorder", etchedBorder, // Added by JGoodies

                "Spinner.defaultEditorInsets", textInsets, // Added by JGoodies

                // 1.4.1 uses a 2 pixel non-standard border, that leads to bad
                // alignment in the typical case that the border is not painted
                "RadioButton.border", marginBorder, "RadioButton.margin", checkBoxMargin,

                "Spinner.border", table.get("TextField.border"),

                "Table.gridColor", controlColor, // 1.4.1 Bug; active
                "TextArea.margin", wrappedTextInsets, // 1.4.1 Bug
                "TextArea.disabledBackground", disabledTextBackground, "TextArea.inactiveBackground",
                inactiveTextBackground, "TextField.margin", textInsets, // 1.4.1 Bug
                "ToggleButton.margin", buttonMargin, // Sun's 14px margin is too wide

                "ToolBar.emptyBorder", toolBarEmptyBorder, // Added by JGoodies
                "ToolBar.separatorBorder", toolBarSeparatorBorder, // Added by JGoodies
                "ToolBar.etchedBorder", toolBarEtchedBorder, // Added by JGoodies
                "ToolBar.headerBorder", toolBarHeaderBorder, // Added by JGoodies
                "ToolBar.separatorSize", toolBarSeparatorSize, "ToolBar.margin", new InsetsUIResource(0, 10, 0, 0),

                "Tree.selectionBorderColor", controlColor, // 1.4.1 Bug; active
                "Tree.rowHeight", rowHeight, // 1.4.1 Bug
        };
        table.putDefaults(defaults);
    }

    /**
     * Initializes component defaults required in classic mode only.
     */
    private static void initComponentDefaultsClassic(UIDefaults table) {
        Object checkBoxIcon = new SimpleProxyLazyValue("com.jgoodies.looks.windows.WindowsLookAndFeel",
                "getCheckBoxIcon");

        Object radioButtonIcon = new SimpleProxyLazyValue("com.jgoodies.looks.windows.WindowsLookAndFeel",
                "getRadioButtonIcon");

        Border winInsetBorder = new BasicBorders.FieldBorder(table.getColor("controlShadow"),
                table.getColor("controlDkShadow"), table.getColor("controlHighlight"),
                table.getColor("controlLtHighlight"));

        Object[] defaults = { "CheckBox.checkColor", table.get("controlText"), // kind-of black
                "CheckBox.icon", checkBoxIcon, "RadioButton.checkColor", table.get("controlText"), // kind-of black
                "RadioButton.icon", radioButtonIcon, "Table.scrollPaneBorder", winInsetBorder, // 1.4.1 Bug

        };
        table.putDefaults(defaults);
    }

    /**
     * Looks up the correct control font and sets it for all controls.
     */
    private static void initFontDefaults(UIDefaults table) {
        FontPolicy fontChoicePolicy = getFontPolicy();
        FontSet fontSet = fontChoicePolicy.getFontSet("Windows", table);
        initFontDefaults(table, fontSet);
    }

    private static void initMenuItemDefaults(UIDefaults table, MicroLayout microLayout) {
        Object menuMargin = microLayout.getMenuMargin();
        Object menuItemMargin = microLayout.getMenuItemMargin();
        Icon menuItemCheckIcon = new MinimumSizedIcon();
        Object[] defaults = {

                "Menu.margin", menuMargin, // 1.4.1 Bug

                "MenuItem.borderPainted", Boolean.TRUE, "MenuItem.checkIcon", menuItemCheckIcon, // Aligns menu items
                "MenuItem.margin", menuItemMargin, // 1.4.1 Bug
                "CheckBoxMenuItem.margin", menuItemMargin, // 1.4.1 Bug
                "RadioButtonMenuItem.margin", menuItemMargin, // 1.4.1 Bug
        };
        table.putDefaults(defaults);
    }

    /**
    * Sets Fonts in the given FontSet as defaults for all known
     * component types in the given UIDefaults table.
     *
     * @param table       the UIDefaults table used to set fonts
     * @param fontSet     describes the set of Fonts to be installed
    */
    private static void initFontDefaults(UIDefaults table, FontSet fontSet) {
        Font controlFont = fontSet.getControlFont();
        Font menuFont = fontSet.getMenuFont();
        Font messageFont = fontSet.getMessageFont();
        Font toolTipFont = fontSet.getSmallFont();
        Font titleFont = fontSet.getTitleFont();
        Font windowFont = fontSet.getWindowTitleFont();

        Object[] defaults = { "Button.font", controlFont, "CheckBox.font", controlFont, "ColorChooser.font",
                controlFont, "ComboBox.font", controlFont, "EditorPane.font", controlFont,
                "FormattedTextField.font", controlFont, "Label.font", controlFont, "List.font", controlFont,
                "Panel.font", controlFont, "PasswordField.font", controlFont, "ProgressBar.font", controlFont,
                "RadioButton.font", controlFont, "ScrollPane.font", controlFont, "Spinner.font", controlFont,
                "TabbedPane.font", controlFont, "Table.font", controlFont, "TableHeader.font", controlFont,
                "TextArea.font", controlFont, "TextField.font", controlFont, "TextPane.font", controlFont,
                "ToolBar.font", controlFont, "ToggleButton.font", controlFont, "Tree.font", controlFont,
                "Viewport.font", controlFont,

                "InternalFrame.titleFont", windowFont, // controlBold
                "OptionPane.font", messageFont, "OptionPane.messageFont", messageFont, "OptionPane.buttonFont",
                messageFont, "TitledBorder.font", titleFont, "ToolTip.font", toolTipFont,

                "CheckBoxMenuItem.font", menuFont, "CheckBoxMenuItem.acceleratorFont", menuFont, // 1.3 only ?
                "Menu.font", menuFont, "Menu.acceleratorFont", menuFont, "MenuBar.font", menuFont, "MenuItem.font",
                menuFont, "MenuItem.acceleratorFont", menuFont, "PopupMenu.font", menuFont,
                "RadioButtonMenuItem.font", menuFont, "RadioButtonMenuItem.acceleratorFont", menuFont, // 1.3 only ?
        };
        table.putDefaults(defaults);
    }

    // Getters for Proxy Access (Referred classes can stay package visible) ***

    public static Border getButtonBorder() {
        return WindowsBorders.getButtonBorder();
    }

    public static Icon getCheckBoxIcon() {
        return WindowsIconFactory.getCheckBoxIcon();
    }

    public static Icon getRadioButtonIcon() {
        return WindowsIconFactory.getRadioButtonIcon();
    }

    // Helper Code ************************************************************

    /**
     * Appends the key and value to the given source array and returns
     * a copy that has the two new elements at its end.
     *
     * @return an array with the key and value appended
     */
    private static Object[] append(Object[] source, String key, Object value) {
        int length = source.length;
        Object[] destination = new Object[length + 2];
        System.arraycopy(source, 0, destination, 0, length);
        destination[length] = key;
        destination[length + 1] = value;
        return destination;
    }

    // Helper Class ***********************************************************

    /**
     * This class provides an implementation of {@code LazyValue} that
     * can be used to delay loading of the Class for the instance to be created.
     * It also avoids creation of an anonymous inner class for the
     * {@code LazyValue}
     * subclass.  Both of these improve performance at the time that a
     * a Look and Feel is loaded, at the cost of a slight performance
     * reduction the first time {@code createValue} is called
     * (since Reflection APIs are used).
     */
    private static class SimpleProxyLazyValue implements UIDefaults.LazyValue {

        private final String className;
        private final String methodName;

        /**
         * Creates a {@code LazyValue} which will construct an instance
         * when asked.
         *
         * @param c    a {@code String} specifying the classname of the class
         *                containing a static method to be called for instance creation
         * @param m    a {@code String} specifying the static
         *      method to be called on class c
         */
        public SimpleProxyLazyValue(String c, String m) {
            className = c;
            methodName = m;
        }

        /**
         * Creates the value retrieved from the {@code UIDefaults} table.
         * The object is created each time it is accessed.
         *
         * @param table  a {@code UIDefaults} table
         * @return the created {@code Object}
         */
        @Override
        public Object createValue(UIDefaults table) {
            Object instance = null;
            try {
                Class c;
                // We use a separate ClassLoader
                ClassLoader classLoader = table != null ? (ClassLoader) table.get("ClassLoader")
                        : Thread.currentThread().getContextClassLoader();
                if (classLoader == null) {
                    classLoader = getClass().getClassLoader();
                }
                c = Class.forName(className, true, classLoader);
                Method m = c.getMethod(methodName, (Class[]) null);
                instance = m.invoke(c, (Object[]) null);
            } catch (Throwable t) {
                LookUtils.log("Problem creating " + className + " with method " + methodName + t);
            }
            return instance;
        }
    }

}