/* * This file is part of WebLookAndFeel library. * * WebLookAndFeel library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * WebLookAndFeel 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with WebLookAndFeel library. If not, see . */ package com.alee.managers.style; import com.alee.extended.painter.Painter; import com.alee.managers.style.data.ComponentStyle; import com.alee.managers.style.data.SkinInfo; import com.alee.managers.style.skin.WebLafSkin; import com.alee.managers.style.skin.web.WebSkin; import com.alee.utils.LafUtils; import com.alee.utils.ReflectUtils; import com.alee.utils.XmlUtils; import com.alee.utils.laf.Styleable; import com.alee.utils.ninepatch.NinePatchIcon; import javax.swing.*; import javax.swing.plaf.ComponentUI; import java.awt.*; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; /** * This class manages WebLaF component styles. * It also manages available WebLaF skins and can install/change them in runtime. * * @author Mikle Garin * @see How to use StyleManager * @see com.alee.managers.style.skin.WebLafSkin * @see com.alee.managers.style.data.SkinInfo */ public final class StyleManager { /** * todo 1. Probably add "setCustomUIStyle" to override settings provided by skin */ /** * Default painter ID. */ public static final String DEFAULT_PAINTER_ID = "painter"; /** * Static class aliases processing. * This allows StyleManager usage without its initialization. */ static { // Class aliases XmlUtils.processAnnotations ( SkinInfo.class ); XmlUtils.processAnnotations ( ComponentStyle.class ); XmlUtils.processAnnotations ( NinePatchIcon.class ); } /** * Skins applied for each specific skinnable component. * Used to determine skinnable components, update them properly and detect their current skin. * Map structure: JComponent -> WebLafSkin */ private static final Map appliedSkins = new WeakHashMap (); /** * Custom painter properties. * These properties may be specified for each separate component. * Map structure: JComponent -> painterId -> propertyName -> propertyValue */ private static final Map>> customPainterProperties = new WeakHashMap>> (); /** * Custom component painters. * These are the painters set from the code and they replace default painters provided by skin. * Map structure: JComponent -> Painter ID -> Painter */ private static final Map> customPainters = new WeakHashMap> (); /** * Default WebLaF skin. * Skin used by default when no other skins provided. * This skin can be set before WebLaF initialization to avoid unnecessary UI updates afterwards. */ private static WebLafSkin defaultSkin = null; /** * Currently used skin. * This skin is applied to all newly created components styled by WebLaF except customized ones. */ private static WebLafSkin currentSkin = null; /** * Whether strict style checks are enabled or not. *

* In case strick checks are enabled any incorrect properties or painters getter and setter calls will cause exceptions. * These exceptions will not cause UI to halt but they will properly inform about missing styles, incorrect settings etc. *

* It is highly recommended to keep this property enabled to see and fix all problems right away. */ private static boolean strictStyleChecks = true; /** * Manager initialization mark. */ private static boolean initialized = false; /** * Initializes StyleManager settings. */ public static void initialize () { if ( !initialized ) { initialized = true; // Applying default skin as current skin applyDefaultSkin (); } } /** * Returns whether strict style checks are enabled or not. * * @return true if strict style checks are enabled, false otherwise */ public static boolean isStrictStyleChecks () { return strictStyleChecks; } /** * Sets whether strict style checks are enabled or not. * * @param strict whether strict style checks are enabled or not */ public static void setStrictStyleChecks ( final boolean strict ) { StyleManager.strictStyleChecks = strict; } /** * Performs skin support check and throws an exception if skin is not supported. * * @param skin skin to check */ private static void checkSupport ( final WebLafSkin skin ) { if ( skin == null ) { throw new StyleException ( "Empty skin provided" ); } if ( !skin.isSupported () ) { throw new StyleException ( "Skin \"" + skin.getName () + "\" is not supported in this system" ); } } /** * Returns default skin. * * @return default skin */ public static WebLafSkin getDefaultSkin () { if ( defaultSkin == null ) { setDefaultSkin ( new WebSkin () ); } return defaultSkin; } /** * Sets default skin. * * @param skinClassName default skin class name * @return previous default skin */ public static WebLafSkin setDefaultSkin ( final String skinClassName ) { return setDefaultSkin ( ReflectUtils.getClassSafely ( skinClassName ) ); } /** * Sets default skin. * * @param skinClass default skin class * @return previous default skin */ public static WebLafSkin setDefaultSkin ( final Class skinClass ) { return setDefaultSkin ( createSkin ( skinClass ) ); } /** * Returns newly created skin class instance. * * @param skinClass skin class * @return newly created skin class instance */ public static WebLafSkin createSkin ( final Class skinClass ) { return ( WebLafSkin ) ReflectUtils.createInstanceSafely ( skinClass ); } /** * Sets default skin. * * @param skin default skin * @return previous default skin */ public static WebLafSkin setDefaultSkin ( final WebLafSkin skin ) { // Checking skin support checkSupport ( skin ); // Saving new default skin final WebLafSkin oldSkin = StyleManager.defaultSkin; StyleManager.defaultSkin = skin; return oldSkin; } /** * Applies default skin to all existing skinnable components. * This skin will also be applied to all skinnable components created afterwards. * * @return previously applied skin */ public static WebLafSkin applyDefaultSkin () { return applySkin ( getDefaultSkin () ); } /** * Returns currently applied skin. * * @return currently applied skin */ public static WebLafSkin getCurrentSkin () { return currentSkin != null ? currentSkin : getDefaultSkin (); } /** * Applies specified skin to all existing skinnable components. * This skin will also be applied to all skinnable components created afterwards. * * @param skinClassName class name of the skin to be applied * @return previously applied skin */ public static WebLafSkin applySkin ( final String skinClassName ) { return applySkin ( ReflectUtils.getClassSafely ( skinClassName ) ); } /** * Applies specified skin to all existing skinnable components. * This skin will also be applied to all skinnable components created afterwards. * * @param skinClass class of the skin to be applied * @return previously applied skin */ public static WebLafSkin applySkin ( final Class skinClass ) { return applySkin ( createSkin ( skinClass ) ); } /** * Applies specified skin to all existing skinnable components. * This skin will also be applied to all skinnable components created afterwards. * * @param skin skin to be applied * @return previously applied skin */ public static WebLafSkin applySkin ( final WebLafSkin skin ) { // Checking skin support checkSupport ( skin ); // Saving previously applied skin final WebLafSkin previousSkin = currentSkin; // Updating currently applied skin currentSkin = skin; // Applying new skin to all existing skinnable components for ( final Map.Entry entry : appliedSkins.entrySet () ) { final JComponent component = entry.getKey (); final WebLafSkin oldSkin = entry.getValue (); if ( oldSkin != null ) { oldSkin.removeSkin ( component ); } if ( skin != null ) { skin.applySkin ( component ); } entry.setValue ( skin ); } return previousSkin; } /** * Applies current skin to the skinnable component. * * @param component component to apply skin to * @return previously applied skin */ public static WebLafSkin applySkin ( final JComponent component ) { return applySkin ( component, getCurrentSkin () ); } /** * Applies specified skin to the skinnable component. * todo Do not replace custom applied skins when current skin changed? * * @param component component to apply skin to * @param skin skin to be applied * @return previously applied skin */ public static WebLafSkin applySkin ( final JComponent component, final WebLafSkin skin ) { // Checking skin support checkSupport ( skin ); // Removing old skin from the component final WebLafSkin previousSkin = removeSkin ( component ); // Applying new skin skin.applySkin ( component ); appliedSkins.put ( component, skin ); return previousSkin; } /** * Removes skin applied to the specified component and returns it. * * @param component component to remove skin from * @return previously applied skin */ public static WebLafSkin removeSkin ( final JComponent component ) { final WebLafSkin skin = appliedSkins.get ( component ); if ( skin != null ) { skin.removeSkin ( component ); appliedSkins.remove ( component ); } return skin; } /** * Returns painter property value from the specified component. * Specified property is searched only inside the base painter so far. * * @param component component to retrieve style property from * @param key style property key * @param style property value type * @return style property value */ public static T getPainterPropertyValue ( final JComponent component, final String key ) { return getPainterPropertyValue ( component, null, key ); } /** * Returns painter property value from the specified component. * Specified property is searched only inside the base painter so far. * * @param component component to retrieve style property from * @param painterId painter ID * @param key style property key * @param style property value type * @return style property value */ public static T getPainterPropertyValue ( final JComponent component, final String painterId, final String key ) { final WebLafSkin skin = appliedSkins.get ( component ); return skin != null ? ( T ) skin.getPainterPropertyValue ( component, painterId, key ) : null; } /** * Sets custom value for painter property for the specified component. * This tricky method uses component skin to set the specified property into component painter. * * @param component component to apply custom style property to * @param key custom style property key * @param value custom style property value * @param custom style property value type * @return old custom style property value */ public static T setCustomPainterProperty ( final JComponent component, final String key, final T value ) { return setCustomPainterProperty ( component, null, key, value ); } /** * Sets custom value for painter property for the specified component. * This tricky method uses component skin to set the specified property into component painter. * * @param component component to apply custom style property to * @param painterId painter ID * @param key custom style property key * @param value custom style property value * @param custom style property value type * @return old custom style property value */ public static T setCustomPainterProperty ( final JComponent component, final String painterId, final String key, final T value ) { // Retrieving custom properties map Map> allProperties = customPainterProperties.get ( component ); if ( allProperties == null ) { allProperties = new HashMap> ( 1 ); customPainterProperties.put ( component, allProperties ); } // Retrieving painter properties map Map properties = allProperties.get ( painterId ); if ( properties == null ) { properties = new HashMap ( 1 ); allProperties.put ( painterId, properties ); } // Saving custom property final T oldValue = ( T ) properties.put ( key, value ); // Applying custom style property if there is a skin applied to this component final WebLafSkin componentSkin = appliedSkins.get ( component ); if ( componentSkin != null ) { componentSkin.setCustomPainterProperty ( component, key, value ); } return oldValue; } /** * Returns all custom painter properties. * Map structure: JComponent -> painterId -> propertyName -> propertyValue * * @return all custom painter properties */ public static Map>> getCustomPainterProperties () { return customPainterProperties; } /** * Returns all custom painter properties for the specified component. * Map structure: painterId -> propertyName -> propertyValue * * @param component component to retrieve custom properties for * @return all custom painter properties for the specified component */ public static Map> getCustomPainterProperties ( final JComponent component ) { return customPainterProperties.get ( component ); } /** * Clears all custom painter properties for the specified component. * This is required when painter changes to avoid setting unexisting variables into painter. * * @param component component to clear custom painter properties for */ public static void clearCustomPainterProperties ( final JComponent component ) { final Map> properties = customPainterProperties.get ( component ); if ( properties != null ) { // Removing all custom painter properties properties.clear (); // Forcing component skin update applySkin ( component ); } } /** * Returns painter for the specified component. * * @param component component to retrieve painter from * @param painter type * @return current component painter */ public static T getPainter ( final JComponent component ) { // todo Change ID to null return getPainter ( component, DEFAULT_PAINTER_ID ); } /** * Returns painter for the specified component and painter ID. * * @param component component to retrieve painter from * @param painterId painter ID * @param painter type * @return current component painter for the specified painter ID */ public static T getPainter ( final JComponent component, final String painterId ) { final Map painters = customPainters.get ( component ); if ( painters != null && painters.containsKey ( painterId ) ) { // Return custom installed painter return ( T ) painters.get ( painterId ); } else { // Return painter applied by skin return getCurrentSkin ().getPainter ( component, painterId ); } } /** * Sets custom default painter for the specified component. * You should call this method when setting painter outside of the UI to avoid * * @param component component to set painter for * @param painter painter * @param painter type * @return old custom painter */ public static T setCustomPainter ( final JComponent component, final T painter ) { // todo Change ID to null return setCustomPainter ( component, DEFAULT_PAINTER_ID, painter ); } /** * Sets custom painter for the specified component. * You should call this method when setting painter outside of the UI to avoid * * @param component component to set painter for * @param id painter ID * @param painter painter * @param painter type * @return old custom painter */ public static T setCustomPainter ( final JComponent component, final String id, final T painter ) { // Clearing custom properties first clearCustomPainterProperties ( component ); // Saving custom painter Map painters = customPainters.get ( component ); if ( painters == null ) { painters = new HashMap ( 1 ); customPainters.put ( component, painters ); } final T oldValue = ( T ) painters.put ( id, painter ); // Forcing component update applySkin ( component ); return oldValue; } /** * Returns all custom painters. * * @return all custom painters */ public static Map> getCustomPainters () { return customPainters; } /** * Returns all custom painters for the specified component. * * @param component component to retrieve custom painters for * @return all custom painters for the specified component */ public static Map getCustomPainters ( final JComponent component ) { return customPainters.get ( component ); } /** * Removes all custom painters from the specified component. * * @param component component to remove custom painters from */ public static void removeCustomPainters ( final JComponent component ) { final Map painters = customPainters.get ( component ); if ( painters != null ) { // Removing all custom painters painters.clear (); // Forcing component skin update applySkin ( component ); } } /** * Sets component style ID if component or its UI is instance of Styleable interface. * This might be useful in cases when you cannot be sure about component type but want to provide style if possible. * * @param component component to apply style ID to * @param styleId style ID * @return true if style ID was successfully applied, false otherwise */ public static boolean setStyleId ( final Component component, final String styleId ) { if ( component instanceof Styleable ) { ( ( Styleable ) component ).setStyleId ( styleId ); return true; } else { final ComponentUI ui = LafUtils.getUI ( component ); if ( ui != null && ui instanceof Styleable ) { ( ( Styleable ) ui ).setStyleId ( styleId ); return true; } } return false; } }