FileDocCategorySizeDatePackage
LookAndFeel.javaAPI DocJava SE 5 API17376Fri Aug 26 14:57:58 BST 2005javax.swing

LookAndFeel.java

/*
 * @(#)LookAndFeel.java	1.38 05/01/04
 *
 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package javax.swing;

import java.awt.Font;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.Color;
import java.awt.Component;
import java.awt.SystemColor;
import java.awt.Toolkit;

import javax.swing.text.*;
import javax.swing.border.*;
import javax.swing.plaf.*;

import java.net.URL;
import com.sun.java.swing.SwingUtilities2;

import java.util.StringTokenizer;


/**
 * Completely characterizes a look and feel from the point of view
 * of the pluggable look and feel components.  
 * 
 * @version 1.38 01/04/05
 * @author Tom Ball
 * @author Hans Muller
 */
public abstract class LookAndFeel 
{

    /**
     * Convenience method for initializing a component's foreground
     * and background color properties with values from the current
     * defaults table.  The properties are only set if the current
     * value is either null or a UIResource.
     * 
     * @param c the target component for installing default color/font properties
     * @param defaultBgName the key for the default background
     * @param defaultFgName the key for the default foreground
     * 
     * @see #installColorsAndFont
     * @see UIManager#getColor
     */
    public static void installColors(JComponent c,
				     String defaultBgName,
                                     String defaultFgName)
    {
        Color bg = c.getBackground();
	if (bg == null || bg instanceof UIResource) {
	    c.setBackground(UIManager.getColor(defaultBgName));
	}

        Color fg = c.getForeground();
	if (fg == null || fg instanceof UIResource) {
	    c.setForeground(UIManager.getColor(defaultFgName));
	} 
    }


    /**
     * Convenience method for initializing a components foreground
     * background and font properties with values from the current
     * defaults table.  The properties are only set if the current
     * value is either null or a UIResource.
     * 
     * @param c the target component for installing default color/font properties
     * @param defaultBgName the key for the default background
     * @param defaultFgName the key for the default foreground
     * @param defaultFontName the key for the default font
     * 
     * @see #installColors
     * @see UIManager#getColor
     * @see UIManager#getFont
     */
    public static void installColorsAndFont(JComponent c,
                                         String defaultBgName,
                                         String defaultFgName,
                                         String defaultFontName) {
        Font f = c.getFont();
	if (f == null || f instanceof UIResource) {
	    c.setFont(UIManager.getFont(defaultFontName));
	}

	installColors(c, defaultBgName, defaultFgName);
    }


    /**
     * Convenience method for installing a component's default Border 
     * object on the specified component if either the border is 
     * currently null or already an instance of UIResource.
     * @param c the target component for installing default border
     * @param defaultBorderName the key specifying the default border
     */
    public static void installBorder(JComponent c, String defaultBorderName) {
        Border b = c.getBorder();
        if (b == null || b instanceof UIResource) {
            c.setBorder(UIManager.getBorder(defaultBorderName));
        }
    }


    /**
     * Convenience method for un-installing a component's default 
     * border on the specified component if the border is 
     * currently an instance of UIResource.
     * @param c the target component for uninstalling default border
     */
    public static void uninstallBorder(JComponent c) {
        if (c.getBorder() instanceof UIResource) {
            c.setBorder(null);
        }
    }

    /**
     * Convenience method for installing a property with the specified name
     * and value on a component if that property has not already been set
     * by the client program.  This method is intended to be used by
     * UI delegate instances that need to specify a default value for a
     * property of primitive type (boolean, int, ..), but do not wish
     * to override a value set by the client.  Since primitive property
     * values cannot be wrapped with the UIResource marker, this method
     * uses private state to determine whether the property has been set
     * by the client.
     * @throws IllegalArgumentException if the specified property is not
     *         one which can be set using this method
     * @throws ClassCastException may be thrown if the property value
     *         specified does not match the property's type
     * @throws NullPointerException may be thrown if c or propertyValue is null
     * @param c the target component for installing the property
     * @param propertyName String containing the name of the property to be set
     * @param propertyValue Object containing the value of the property
     */
    public static void installProperty(JComponent c,
				       String propertyName, Object propertyValue) {
        c.setUIProperty(propertyName, propertyValue);
    }

    /**
     * Convenience method for building lists of KeyBindings.
     * <p>
     * Return an array of KeyBindings, one for each KeyStroke,Action pair
     * in <b>keyBindingList</b>.  A KeyStroke can either be a string in
     * the format specified by the <code>KeyStroke.getKeyStroke</code> 
     * method or a KeyStroke object.
     * <p>
     * Actions are strings.  Here's an example:
     * <pre>
     * 	JTextComponent.KeyBinding[] multilineBindings = makeKeyBindings( new Object[] {
     *          "UP", DefaultEditorKit.upAction,
     *        "DOWN", DefaultEditorKit.downAction,
     *     "PAGE_UP", DefaultEditorKit.pageUpAction,
     *   "PAGE_DOWN", DefaultEditorKit.pageDownAction,
     *       "ENTER", DefaultEditorKit.insertBreakAction,
     *         "TAB", DefaultEditorKit.insertTabAction
     *  });
     * </pre>
     *
     * @param keyBindingList an array of KeyStroke,Action pairs
     * @return an array of KeyBindings
     */
    public static JTextComponent.KeyBinding[] makeKeyBindings(Object[] keyBindingList) 
    {
	JTextComponent.KeyBinding[] rv = new JTextComponent.KeyBinding[keyBindingList.length / 2];

	for(int i = 0; i < keyBindingList.length; i += 2) {
	    KeyStroke keystroke = (keyBindingList[i] instanceof KeyStroke)
		? (KeyStroke)keyBindingList[i]
		: KeyStroke.getKeyStroke((String)keyBindingList[i]);
	    String action = (String)keyBindingList[i+1];
	    rv[i / 2] = new JTextComponent.KeyBinding(keystroke, action);
	}

	return rv;
    }

    /**
     * Creates a InputMap from <code>keys</code>. <code>keys</code>
     * describes the InputMap, with every even number item being a String
     * giving the KeyStroke as speced in
     * <code>KeyStroke.getKeyStroke(String)</code>
     * (or a KeyStroke), and every odd number item the Object
     * used to determine the associated Action in an ActionMap.
     *
     * @since 1.3
     */
    public static InputMap makeInputMap(Object[] keys) {
	InputMap retMap = new InputMapUIResource();
	loadKeyBindings(retMap, keys);
	return retMap;
    }

    /**
     * Creates a ComponentInputMap from <code>keys</code>. <code>keys</code>
     * describes the InputMap, with every even number item being a String
     * giving
     * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
     * (or a KeyStroke), and every odd number item the Object
     * used to determine the associated Action in an ActionMap.
     *
     * @since 1.3
     */
    public static ComponentInputMap makeComponentInputMap(JComponent c,
							  Object[] keys) {
	ComponentInputMap retMap = new ComponentInputMapUIResource(c);
	loadKeyBindings(retMap, keys);
	return retMap;
    }


    /**
     * Loads the bindings in <code>keys</code> into <code>retMap</code>.
     * This does not remove any existing bindings in <code>retMap</code>.
     * <code>keys</code>
     * describes the InputMap, with every even number item being a String
     * giving
     * the KeyStroke as speced in <code>KeyStroke.getKeyStroke(String)</code>
     * (or a KeyStroke), and every odd number item the Object
     * used to determine the associated Action in an ActionMap.
     *
     * @since 1.3
     */
    public static void loadKeyBindings(InputMap retMap, Object[] keys) {
	if (keys != null) {
	    for (int counter = 0, maxCounter = keys.length;
		 counter < maxCounter; counter++) {
		Object keyStrokeO = keys[counter++];
		KeyStroke ks = (keyStrokeO instanceof KeyStroke) ?
		                (KeyStroke)keyStrokeO :
		                KeyStroke.getKeyStroke((String)keyStrokeO);
		retMap.put(ks, keys[counter]);
	    }
	}
    }

    /**
     * Utility method that creates a UIDefaults.LazyValue that creates
     * an ImageIcon UIResource for the specified <code>gifFile</code>
     * filename.
     */
    public static Object makeIcon(final Class<?> baseClass, final String gifFile) {
        return SwingUtilities2.makeIcon(baseClass, baseClass, gifFile);
    }

    /**
     * Invoked when the user attempts an invalid operation, 
     * such as pasting into an uneditable <code>JTextField</code> 
     * that has focus. The default implementation beeps. Subclasses 
     * that wish different behavior should override this and provide 
     * the additional feedback.
     *
     * @param component the <code>Component</code> the error occurred in,
     *                  may be <code>null</code>
     *			indicating the error condition is not directly 
     *			associated with a <code>Component</code>
     * @since 1.4
     */
    public void provideErrorFeedback(Component component) {
	Toolkit toolkit = null;
	if (component != null) {
	    toolkit = component.getToolkit();
	} else {
	    toolkit = Toolkit.getDefaultToolkit();
	}
	toolkit.beep();
    } // provideErrorFeedback()

    /**
     * Returns the value of the specified system desktop property by
     * invoking <code>Toolkit.getDefaultToolkit().getDesktopProperty()</code>.
     * If the current value of the specified property is null, the 
     * fallbackValue is returned.
     * @param systemPropertyName the name of the system desktop property being queried
     * @param fallbackValue the object to be returned as the value if the system value is null
     * @return the current value of the desktop property
     *
     * @see java.awt.Toolkit#getDesktopProperty
     *
     */
    public static Object getDesktopPropertyValue(String systemPropertyName, Object fallbackValue) {
	Object value = Toolkit.getDefaultToolkit().getDesktopProperty(systemPropertyName);
	if (value == null) {
	    return fallbackValue;
	} else if (value instanceof Color) {
	    return new ColorUIResource((Color)value);
	} else if (value instanceof Font) {
	    return new FontUIResource((Font)value);
	}
	return value;
    }

    /**
     * Returns an <code>Icon</code> with a disabled appearance.
     * This method is used to generate a disabled <code>Icon</code> when
     * one has not been specified.  For example, if you create a
     * <code>JButton</code> and only specify an <code>Icon</code> via
     * <code>setIcon</code> this method will be called to generate the
     * disabled <code>Icon</code>. If null is passed as <code>icon</code>
     * this method returns null. 
     * <p>
     * Some look and feels might not render the disabled Icon, in which
     * case they will ignore this.
     *
     * @param component JComponent that will display the Icon, may be null
     * @param icon Icon to generate disable icon from.
     * @return Disabled icon, or null if a suitable Icon can not be
     *         generated.
     * @since 1.5
     */
    public Icon getDisabledIcon(JComponent component, Icon icon) {
        if (icon instanceof ImageIcon) {
            return new IconUIResource(new ImageIcon(GrayFilter.
                   createDisabledImage(((ImageIcon)icon).getImage())));
        }
        return null;
    }                       

    /**
     * Returns an <code>Icon</code> for use by disabled
     * components that are also selected. This method is used to generate an
     * <code>Icon</code> for components that are in both the disabled and
     * selected states but do not have a specific <code>Icon</code> for this
     * state.  For example, if you create a <code>JButton</code> and only
     * specify an <code>Icon</code> via <code>setIcon</code> this method
     * will be called to generate the disabled and selected
     * <code>Icon</code>. If null is passed as <code>icon</code> this method
     * returns null. 
     * <p>
     * Some look and feels might not render the disabled and selected Icon,
     * in which case they will ignore this.
     *
     * @param component JComponent that will display the Icon, may be null
     * @param icon Icon to generate disabled and selected icon from.
     * @return Disabled and Selected icon, or null if a suitable Icon can not
     *         be generated.
     * @since 1.5
     */
    public Icon getDisabledSelectedIcon(JComponent component, Icon icon) {
        return getDisabledIcon(component, icon);
    }

    /**
     * Return a short string that identifies this look and feel, e.g.
     * "CDE/Motif".  This string should be appropriate for a menu item.
     * Distinct look and feels should have different names, e.g. 
     * a subclass of MotifLookAndFeel that changes the way a few components
     * are rendered should be called "CDE/Motif My Way"; something
     * that would be useful to a user trying to select a L&F from a list
     * of names.
     */
    public abstract String getName();


    /**
     * Return a string that identifies this look and feel.  This string 
     * will be used by applications/services that want to recognize
     * well known look and feel implementations.  Presently
     * the well known names are "Motif", "Windows", "Mac", "Metal".  Note 
     * that a LookAndFeel derived from a well known superclass 
     * that doesn't make any fundamental changes to the look or feel 
     * shouldn't override this method.
     */
    public abstract String getID();


    /** 
     * Return a one line description of this look and feel implementation, 
     * e.g. "The CDE/Motif Look and Feel".   This string is intended for 
     * the user, e.g. in the title of a window or in a ToolTip message.
     */
    public abstract String getDescription();


    /**
     * Returns true if the <code>LookAndFeel</code> returned
     * <code>RootPaneUI</code> instances support providing Window decorations
     * in a <code>JRootPane</code>.
     * <p>
     * The default implementation returns false, subclasses that support
     * Window decorations should override this and return true.
     *
     * @return True if the RootPaneUI instances created support client side
     *              decorations
     * @see JDialog#setDefaultLookAndFeelDecorated
     * @see JFrame#setDefaultLookAndFeelDecorated
     * @see JRootPane#setWindowDecorationStyle
     * @since 1.4
     */
    public boolean getSupportsWindowDecorations() {
        return false;
    }

    /**
     * If the underlying platform has a "native" look and feel, and this
     * is an implementation of it, return true.  For example a CDE/Motif
     * look and implementation would return true when the underlying 
     * platform was Solaris.
     */
    public abstract boolean isNativeLookAndFeel();


    /**
     * Return true if the underlying platform supports and or permits
     * this look and feel.  This method returns false if the look 
     * and feel depends on special resources or legal agreements that
     * aren't defined for the current platform.  
     * 
     * @see UIManager#setLookAndFeel
     */
    public abstract boolean isSupportedLookAndFeel();


    /**
     * UIManager.setLookAndFeel calls this method before the first
     * call (and typically the only call) to getDefaults().  Subclasses
     * should do any one-time setup they need here, rather than 
     * in a static initializer, because look and feel class objects
     * may be loaded just to discover that isSupportedLookAndFeel()
     * returns false.
     *
     * @see #uninitialize
     * @see UIManager#setLookAndFeel
     */
    public void initialize() {
    }


    /**
     * UIManager.setLookAndFeel calls this method just before we're
     * replaced by a new default look and feel.   Subclasses may 
     * choose to free up some resources here.
     *
     * @see #initialize
     */
    public void uninitialize() {
    }

    /**
     * This method is called once by UIManager.setLookAndFeel to create
     * the look and feel specific defaults table.  Other applications,
     * for example an application builder, may also call this method.
     *
     * @see #initialize
     * @see #uninitialize
     * @see UIManager#setLookAndFeel
     */
    public UIDefaults getDefaults() {
        return null;
    }

    /**
     * Returns a string that displays and identifies this
     * object's properties.
     *
     * @return a String representation of this object
     */
    public String toString() {
	return "[" + getDescription() + " - " + getClass().getName() + "]";
    }
}