src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/XPStyle.java
branchhttp-client-branch
changeset 55982 b6ff245c0db6
parent 55981 907bddce488c
parent 48332 651a95f30dfb
child 55983 e4a1f0c9d4c6
equal deleted inserted replaced
55981:907bddce488c 55982:b6ff245c0db6
     1 /*
       
     2  * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 /*
       
    27  * <p>These classes are designed to be used while the
       
    28  * corresponding <code>LookAndFeel</code> class has been installed
       
    29  * (<code>UIManager.setLookAndFeel(new <i>XXX</i>LookAndFeel())</code>).
       
    30  * Using them while a different <code>LookAndFeel</code> is installed
       
    31  * may produce unexpected results, including exceptions.
       
    32  * Additionally, changing the <code>LookAndFeel</code>
       
    33  * maintained by the <code>UIManager</code> without updating the
       
    34  * corresponding <code>ComponentUI</code> of any
       
    35  * <code>JComponent</code>s may also produce unexpected results,
       
    36  * such as the wrong colors showing up, and is generally not
       
    37  * encouraged.
       
    38  *
       
    39  */
       
    40 
       
    41 package com.sun.java.swing.plaf.windows;
       
    42 
       
    43 import java.awt.*;
       
    44 import java.awt.image.*;
       
    45 import java.security.AccessController;
       
    46 import java.util.*;
       
    47 
       
    48 import javax.swing.*;
       
    49 import javax.swing.border.*;
       
    50 import javax.swing.plaf.*;
       
    51 import javax.swing.text.JTextComponent;
       
    52 
       
    53 import sun.awt.image.SunWritableRaster;
       
    54 import sun.awt.windows.ThemeReader;
       
    55 import sun.security.action.GetPropertyAction;
       
    56 import sun.swing.CachedPainter;
       
    57 
       
    58 import static com.sun.java.swing.plaf.windows.TMSchema.*;
       
    59 
       
    60 
       
    61 /**
       
    62  * Implements Windows XP Styles for the Windows Look and Feel.
       
    63  *
       
    64  * @author Leif Samuelsson
       
    65  */
       
    66 class XPStyle {
       
    67     // Singleton instance of this class
       
    68     private static XPStyle xp;
       
    69 
       
    70     // Singleton instance of SkinPainter
       
    71     private static SkinPainter skinPainter = new SkinPainter();
       
    72 
       
    73     private static Boolean themeActive = null;
       
    74 
       
    75     private HashMap<String, Border> borderMap;
       
    76     private HashMap<String, Color>  colorMap;
       
    77 
       
    78     private boolean flatMenus;
       
    79 
       
    80     static {
       
    81         invalidateStyle();
       
    82     }
       
    83 
       
    84     /** Static method for clearing the hashmap and loading the
       
    85      * current XP style and theme
       
    86      */
       
    87     static synchronized void invalidateStyle() {
       
    88         xp = null;
       
    89         themeActive = null;
       
    90         skinPainter.flush();
       
    91     }
       
    92 
       
    93     /** Get the singleton instance of this class
       
    94      *
       
    95      * @return the singleton instance of this class or null if XP styles
       
    96      * are not active or if this is not Windows XP
       
    97      */
       
    98     static synchronized XPStyle getXP() {
       
    99         if (themeActive == null) {
       
   100             Toolkit toolkit = Toolkit.getDefaultToolkit();
       
   101             themeActive =
       
   102                 (Boolean)toolkit.getDesktopProperty("win.xpstyle.themeActive");
       
   103             if (themeActive == null) {
       
   104                 themeActive = Boolean.FALSE;
       
   105             }
       
   106             if (themeActive.booleanValue()) {
       
   107                 GetPropertyAction propertyAction =
       
   108                     new GetPropertyAction("swing.noxp");
       
   109                 if (AccessController.doPrivileged(propertyAction) == null &&
       
   110                     ThemeReader.isThemed() &&
       
   111                     !(UIManager.getLookAndFeel()
       
   112                       instanceof WindowsClassicLookAndFeel)) {
       
   113 
       
   114                     xp = new XPStyle();
       
   115                 }
       
   116             }
       
   117         }
       
   118         return ThemeReader.isXPStyleEnabled() ? xp : null;
       
   119     }
       
   120 
       
   121     static boolean isVista() {
       
   122         XPStyle xp = XPStyle.getXP();
       
   123         return (xp != null && xp.isSkinDefined(null, Part.CP_DROPDOWNBUTTONRIGHT));
       
   124     }
       
   125 
       
   126     /** Get a named <code>String</code> value from the current style
       
   127      *
       
   128      * @param part a <code>Part</code>
       
   129      * @param state a <code>String</code>
       
   130      * @param prop a <code>String</code>
       
   131      * @return a <code>String</code> or null if key is not found
       
   132      *    in the current style
       
   133      *
       
   134      * This is currently only used by WindowsInternalFrameTitlePane for painting
       
   135      * title foregound and can be removed when no longer needed
       
   136      */
       
   137     String getString(Component c, Part part, State state, Prop prop) {
       
   138         return getTypeEnumName(c, part, state, prop);
       
   139     }
       
   140 
       
   141     TypeEnum getTypeEnum(Component c, Part part, State state, Prop prop) {
       
   142         int enumValue = ThemeReader.getEnum(part.getControlName(c), part.getValue(),
       
   143                                             State.getValue(part, state),
       
   144                                             prop.getValue());
       
   145         return TypeEnum.getTypeEnum(prop, enumValue);
       
   146     }
       
   147 
       
   148     private static String getTypeEnumName(Component c, Part part, State state, Prop prop) {
       
   149         int enumValue = ThemeReader.getEnum(part.getControlName(c), part.getValue(),
       
   150                                             State.getValue(part, state),
       
   151                                             prop.getValue());
       
   152         if (enumValue == -1) {
       
   153             return null;
       
   154         }
       
   155         return TypeEnum.getTypeEnum(prop, enumValue).getName();
       
   156     }
       
   157 
       
   158 
       
   159 
       
   160 
       
   161     /** Get a named <code>int</code> value from the current style
       
   162      *
       
   163      * @param part a <code>Part</code>
       
   164      * @return an <code>int</code> or null if key is not found
       
   165      *    in the current style
       
   166      */
       
   167     int getInt(Component c, Part part, State state, Prop prop, int fallback) {
       
   168         return ThemeReader.getInt(part.getControlName(c), part.getValue(),
       
   169                                   State.getValue(part, state),
       
   170                                   prop.getValue());
       
   171     }
       
   172 
       
   173     /** Get a named <code>Dimension</code> value from the current style
       
   174      *
       
   175      * @return a <code>Dimension</code> or null if key is not found
       
   176      *    in the current style
       
   177      *
       
   178      * This is currently only used by WindowsProgressBarUI and the value
       
   179      * should probably be cached there instead of here.
       
   180      */
       
   181     Dimension getDimension(Component c, Part part, State state, Prop prop) {
       
   182         Dimension d = ThemeReader.getPosition(part.getControlName(c), part.getValue(),
       
   183                                               State.getValue(part, state),
       
   184                                               prop.getValue());
       
   185         return (d != null) ? d : new Dimension();
       
   186     }
       
   187 
       
   188     /** Get a named <code>Point</code> (e.g. a location or an offset) value
       
   189      *  from the current style
       
   190      *
       
   191      * @return a <code>Point</code> or null if key is not found
       
   192      *    in the current style
       
   193      *
       
   194      * This is currently only used by WindowsInternalFrameTitlePane for painting
       
   195      * title foregound and can be removed when no longer needed
       
   196      */
       
   197     Point getPoint(Component c, Part part, State state, Prop prop) {
       
   198         Dimension d = ThemeReader.getPosition(part.getControlName(c), part.getValue(),
       
   199                                               State.getValue(part, state),
       
   200                                               prop.getValue());
       
   201         return (d != null) ? new Point(d.width, d.height) : new Point();
       
   202     }
       
   203 
       
   204     /** Get a named <code>Insets</code> value from the current style
       
   205      *
       
   206      * @return an <code>Insets</code> object or null if key is not found
       
   207      *    in the current style
       
   208      *
       
   209      * This is currently only used to create borders and by
       
   210      * WindowsInternalFrameTitlePane for painting title foregound.
       
   211      * The return value is already cached in those places.
       
   212      */
       
   213     Insets getMargin(Component c, Part part, State state, Prop prop) {
       
   214         Insets insets = ThemeReader.getThemeMargins(part.getControlName(c), part.getValue(),
       
   215                                                     State.getValue(part, state),
       
   216                                                     prop.getValue());
       
   217         return (insets != null) ? insets : new Insets(0, 0, 0, 0);
       
   218     }
       
   219 
       
   220 
       
   221     /** Get a named <code>Color</code> value from the current style
       
   222      *
       
   223      * @return a <code>Color</code> or null if key is not found
       
   224      *    in the current style
       
   225      */
       
   226     synchronized Color getColor(Skin skin, Prop prop, Color fallback) {
       
   227         String key = skin.toString() + "." + prop.name();
       
   228         Part part = skin.part;
       
   229         Color color = colorMap.get(key);
       
   230         if (color == null) {
       
   231             color = ThemeReader.getColor(part.getControlName(null), part.getValue(),
       
   232                                          State.getValue(part, skin.state),
       
   233                                          prop.getValue());
       
   234             if (color != null) {
       
   235                 color = new ColorUIResource(color);
       
   236                 colorMap.put(key, color);
       
   237             }
       
   238         }
       
   239         return (color != null) ? color : fallback;
       
   240     }
       
   241 
       
   242     Color getColor(Component c, Part part, State state, Prop prop, Color fallback) {
       
   243         return getColor(new Skin(c, part, state), prop, fallback);
       
   244     }
       
   245 
       
   246 
       
   247 
       
   248     /** Get a named <code>Border</code> value from the current style
       
   249      *
       
   250      * @param part a <code>Part</code>
       
   251      * @return a <code>Border</code> or null if key is not found
       
   252      *    in the current style or if the style for the particular
       
   253      *    part is not defined as "borderfill".
       
   254      */
       
   255     synchronized Border getBorder(Component c, Part part) {
       
   256         if (part == Part.MENU) {
       
   257             // Special case because XP has no skin for menus
       
   258             if (flatMenus) {
       
   259                 // TODO: The classic border uses this color, but we should
       
   260                 // create a new UI property called "PopupMenu.borderColor"
       
   261                 // instead.
       
   262                 return new XPFillBorder(UIManager.getColor("InternalFrame.borderShadow"),
       
   263                                         1);
       
   264             } else {
       
   265                 return null;    // Will cause L&F to use classic border
       
   266             }
       
   267         }
       
   268         Skin skin = new Skin(c, part, null);
       
   269         Border border = borderMap.get(skin.string);
       
   270         if (border == null) {
       
   271             String bgType = getTypeEnumName(c, part, null, Prop.BGTYPE);
       
   272             if ("borderfill".equalsIgnoreCase(bgType)) {
       
   273                 int thickness = getInt(c, part, null, Prop.BORDERSIZE, 1);
       
   274                 Color color = getColor(skin, Prop.BORDERCOLOR, Color.black);
       
   275                 border = new XPFillBorder(color, thickness);
       
   276                 if (part == Part.CP_COMBOBOX) {
       
   277                     border = new XPStatefulFillBorder(color, thickness, part, Prop.BORDERCOLOR);
       
   278                 }
       
   279             } else if ("imagefile".equalsIgnoreCase(bgType)) {
       
   280                 Insets m = getMargin(c, part, null, Prop.SIZINGMARGINS);
       
   281                 if (m != null) {
       
   282                     if (getBoolean(c, part, null, Prop.BORDERONLY)) {
       
   283                         border = new XPImageBorder(c, part);
       
   284                     } else if (part == Part.CP_COMBOBOX) {
       
   285                         border = new EmptyBorder(1, 1, 1, 1);
       
   286                     } else {
       
   287                         if(part == Part.TP_BUTTON) {
       
   288                             border = new XPEmptyBorder(new Insets(3,3,3,3));
       
   289                         } else {
       
   290                             border = new XPEmptyBorder(m);
       
   291                         }
       
   292                     }
       
   293                 }
       
   294             }
       
   295             if (border != null) {
       
   296                 borderMap.put(skin.string, border);
       
   297             }
       
   298         }
       
   299         return border;
       
   300     }
       
   301 
       
   302     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   303     private class XPFillBorder extends LineBorder implements UIResource {
       
   304         XPFillBorder(Color color, int thickness) {
       
   305             super(color, thickness);
       
   306         }
       
   307 
       
   308         public Insets getBorderInsets(Component c, Insets insets)       {
       
   309             Insets margin = null;
       
   310             //
       
   311             // Ideally we'd have an interface defined for classes which
       
   312             // support margins (to avoid this hackery), but we've
       
   313             // decided against it for simplicity
       
   314             //
       
   315            if (c instanceof AbstractButton) {
       
   316                margin = ((AbstractButton)c).getMargin();
       
   317            } else if (c instanceof JToolBar) {
       
   318                margin = ((JToolBar)c).getMargin();
       
   319            } else if (c instanceof JTextComponent) {
       
   320                margin = ((JTextComponent)c).getMargin();
       
   321            }
       
   322            insets.top    = (margin != null? margin.top : 0)    + thickness;
       
   323            insets.left   = (margin != null? margin.left : 0)   + thickness;
       
   324            insets.bottom = (margin != null? margin.bottom : 0) + thickness;
       
   325            insets.right =  (margin != null? margin.right : 0)  + thickness;
       
   326 
       
   327            return insets;
       
   328         }
       
   329     }
       
   330 
       
   331     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   332     private class XPStatefulFillBorder extends XPFillBorder {
       
   333         private final Part part;
       
   334         private final Prop prop;
       
   335         XPStatefulFillBorder(Color color, int thickness, Part part, Prop prop) {
       
   336             super(color, thickness);
       
   337             this.part = part;
       
   338             this.prop = prop;
       
   339         }
       
   340 
       
   341         public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
       
   342             State state = State.NORMAL;
       
   343             // special casing for comboboxes.
       
   344             // there may be more special cases in the future
       
   345             if(c instanceof JComboBox) {
       
   346                 JComboBox<?> cb = (JComboBox)c;
       
   347                 // note. in the future this should be replaced with a call
       
   348                 // to BasicLookAndFeel.getUIOfType()
       
   349                 if(cb.getUI() instanceof WindowsComboBoxUI) {
       
   350                     WindowsComboBoxUI wcb = (WindowsComboBoxUI)cb.getUI();
       
   351                     state = wcb.getXPComboBoxState(cb);
       
   352                 }
       
   353             }
       
   354             lineColor = getColor(c, part, state, prop, Color.black);
       
   355             super.paintBorder(c, g, x, y, width, height);
       
   356         }
       
   357     }
       
   358 
       
   359     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   360     private class XPImageBorder extends AbstractBorder implements UIResource {
       
   361         Skin skin;
       
   362 
       
   363         XPImageBorder(Component c, Part part) {
       
   364             this.skin = getSkin(c, part);
       
   365         }
       
   366 
       
   367         public void paintBorder(Component c, Graphics g,
       
   368                                 int x, int y, int width, int height) {
       
   369             skin.paintSkin(g, x, y, width, height, null);
       
   370         }
       
   371 
       
   372         public Insets getBorderInsets(Component c, Insets insets)       {
       
   373             Insets margin = null;
       
   374             Insets borderInsets = skin.getContentMargin();
       
   375             if(borderInsets == null) {
       
   376                 borderInsets = new Insets(0, 0, 0, 0);
       
   377             }
       
   378             //
       
   379             // Ideally we'd have an interface defined for classes which
       
   380             // support margins (to avoid this hackery), but we've
       
   381             // decided against it for simplicity
       
   382             //
       
   383            if (c instanceof AbstractButton) {
       
   384                margin = ((AbstractButton)c).getMargin();
       
   385            } else if (c instanceof JToolBar) {
       
   386                margin = ((JToolBar)c).getMargin();
       
   387            } else if (c instanceof JTextComponent) {
       
   388                margin = ((JTextComponent)c).getMargin();
       
   389            }
       
   390            insets.top    = (margin != null? margin.top : 0)    + borderInsets.top;
       
   391            insets.left   = (margin != null? margin.left : 0)   + borderInsets.left;
       
   392            insets.bottom = (margin != null? margin.bottom : 0) + borderInsets.bottom;
       
   393            insets.right  = (margin != null? margin.right : 0)  + borderInsets.right;
       
   394 
       
   395            return insets;
       
   396         }
       
   397     }
       
   398 
       
   399     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   400     private class XPEmptyBorder extends EmptyBorder implements UIResource {
       
   401         XPEmptyBorder(Insets m) {
       
   402             super(m.top+2, m.left+2, m.bottom+2, m.right+2);
       
   403         }
       
   404 
       
   405         public Insets getBorderInsets(Component c, Insets insets)       {
       
   406             insets = super.getBorderInsets(c, insets);
       
   407 
       
   408             Insets margin = null;
       
   409             if (c instanceof AbstractButton) {
       
   410                 Insets m = ((AbstractButton)c).getMargin();
       
   411                 // if this is a toolbar button then ignore getMargin()
       
   412                 // and subtract the padding added by the constructor
       
   413                 if(c.getParent() instanceof JToolBar
       
   414                    && ! (c instanceof JRadioButton)
       
   415                    && ! (c instanceof JCheckBox)
       
   416                    && m instanceof InsetsUIResource) {
       
   417                     insets.top -= 2;
       
   418                     insets.left -= 2;
       
   419                     insets.bottom -= 2;
       
   420                     insets.right -= 2;
       
   421                 } else {
       
   422                     margin = m;
       
   423                 }
       
   424             } else if (c instanceof JToolBar) {
       
   425                 margin = ((JToolBar)c).getMargin();
       
   426             } else if (c instanceof JTextComponent) {
       
   427                 margin = ((JTextComponent)c).getMargin();
       
   428             }
       
   429             if (margin != null) {
       
   430                 insets.top    = margin.top + 2;
       
   431                 insets.left   = margin.left + 2;
       
   432                 insets.bottom = margin.bottom + 2;
       
   433                 insets.right  = margin.right + 2;
       
   434             }
       
   435             return insets;
       
   436         }
       
   437     }
       
   438     boolean isSkinDefined(Component c, Part part) {
       
   439         return (part.getValue() == 0)
       
   440             || ThemeReader.isThemePartDefined(
       
   441                    part.getControlName(c), part.getValue(), 0);
       
   442     }
       
   443 
       
   444 
       
   445     /** Get a <code>Skin</code> object from the current style
       
   446      * for a named part (component type)
       
   447      *
       
   448      * @param part a <code>Part</code>
       
   449      * @return a <code>Skin</code> object
       
   450      */
       
   451     synchronized Skin getSkin(Component c, Part part) {
       
   452         assert isSkinDefined(c, part) : "part " + part + " is not defined";
       
   453         return new Skin(c, part, null);
       
   454     }
       
   455 
       
   456 
       
   457     long getThemeTransitionDuration(Component c, Part part, State stateFrom,
       
   458                                     State stateTo, Prop prop) {
       
   459          return ThemeReader.getThemeTransitionDuration(part.getControlName(c),
       
   460                                           part.getValue(),
       
   461                                           State.getValue(part, stateFrom),
       
   462                                           State.getValue(part, stateTo),
       
   463                                           (prop != null) ? prop.getValue() : 0);
       
   464     }
       
   465 
       
   466 
       
   467     /** A class which encapsulates attributes for a given part
       
   468      * (component type) and which provides methods for painting backgrounds
       
   469      * and glyphs
       
   470      */
       
   471     static class Skin {
       
   472         final Component component;
       
   473         final Part part;
       
   474         final State state;
       
   475 
       
   476         private final String string;
       
   477         private Dimension size = null;
       
   478         private boolean switchStates = false;
       
   479 
       
   480         Skin(Component component, Part part) {
       
   481             this(component, part, null);
       
   482         }
       
   483 
       
   484         Skin(Part part, State state) {
       
   485             this(null, part, state);
       
   486         }
       
   487 
       
   488         Skin(Component component, Part part, State state) {
       
   489             this.component = component;
       
   490             this.part  = part;
       
   491             this.state = state;
       
   492 
       
   493             String str = part.getControlName(component) +"." + part.name();
       
   494             if (state != null) {
       
   495                 str += "("+state.name()+")";
       
   496             }
       
   497             string = str;
       
   498         }
       
   499 
       
   500         Insets getContentMargin() {
       
   501             /* idk: it seems margins are the same for all 'big enough'
       
   502              * bounding rectangles.
       
   503              */
       
   504             int boundingWidth = 100;
       
   505             int boundingHeight = 100;
       
   506 
       
   507             Insets insets = ThemeReader.getThemeBackgroundContentMargins(
       
   508                 part.getControlName(null), part.getValue(),
       
   509                 0, boundingWidth, boundingHeight);
       
   510             return (insets != null) ? insets : new Insets(0, 0, 0, 0);
       
   511         }
       
   512 
       
   513         boolean haveToSwitchStates() {
       
   514             return switchStates;
       
   515         }
       
   516 
       
   517         void switchStates(boolean b) {
       
   518             switchStates = b;
       
   519         }
       
   520 
       
   521         private int getWidth(State state) {
       
   522             if (size == null) {
       
   523                 size = getPartSize(part, state);
       
   524             }
       
   525             return (size != null) ? size.width : 0;
       
   526         }
       
   527 
       
   528         int getWidth() {
       
   529             return getWidth((state != null) ? state : State.NORMAL);
       
   530         }
       
   531 
       
   532         private int getHeight(State state) {
       
   533             if (size == null) {
       
   534                 size = getPartSize(part, state);
       
   535             }
       
   536             return (size != null) ? size.height : 0;
       
   537         }
       
   538 
       
   539         int getHeight() {
       
   540             return getHeight((state != null) ? state : State.NORMAL);
       
   541         }
       
   542 
       
   543         public String toString() {
       
   544             return string;
       
   545         }
       
   546 
       
   547         public boolean equals(Object obj) {
       
   548             return (obj instanceof Skin && ((Skin)obj).string.equals(string));
       
   549         }
       
   550 
       
   551         public int hashCode() {
       
   552             return string.hashCode();
       
   553         }
       
   554 
       
   555         /** Paint a skin at x, y.
       
   556          *
       
   557          * @param g   the graphics context to use for painting
       
   558          * @param dx  the destination <i>x</i> coordinate
       
   559          * @param dy  the destination <i>y</i> coordinate
       
   560          * @param state which state to paint
       
   561          */
       
   562         void paintSkin(Graphics g, int dx, int dy, State state) {
       
   563             if (state == null) {
       
   564                 state = this.state;
       
   565             }
       
   566             paintSkin(g, dx, dy, getWidth(state), getHeight(state), state);
       
   567         }
       
   568 
       
   569         /** Paint a skin in an area defined by a rectangle.
       
   570          *
       
   571          * @param g the graphics context to use for painting
       
   572          * @param r     a <code>Rectangle</code> defining the area to fill,
       
   573          *                     may cause the image to be stretched or tiled
       
   574          * @param state which state to paint
       
   575          */
       
   576         void paintSkin(Graphics g, Rectangle r, State state) {
       
   577             paintSkin(g, r.x, r.y, r.width, r.height, state);
       
   578         }
       
   579 
       
   580         /** Paint a skin at a defined position and size
       
   581          *  This method supports animation.
       
   582          *
       
   583          * @param g   the graphics context to use for painting
       
   584          * @param dx  the destination <i>x</i> coordinate
       
   585          * @param dy  the destination <i>y</i> coordinate
       
   586          * @param dw  the width of the area to fill, may cause
       
   587          *                  the image to be stretched or tiled
       
   588          * @param dh  the height of the area to fill, may cause
       
   589          *                  the image to be stretched or tiled
       
   590          * @param state which state to paint
       
   591          */
       
   592         void paintSkin(Graphics g, int dx, int dy, int dw, int dh, State state) {
       
   593             if (XPStyle.getXP() == null) {
       
   594                 return;
       
   595             }
       
   596             if (ThemeReader.isGetThemeTransitionDurationDefined()
       
   597                   && component instanceof JComponent
       
   598                   && SwingUtilities.getAncestorOfClass(CellRendererPane.class,
       
   599                                                        component) == null) {
       
   600                 AnimationController.paintSkin((JComponent) component, this,
       
   601                                               g, dx, dy, dw, dh, state);
       
   602             } else {
       
   603                 paintSkinRaw(g, dx, dy, dw, dh, state);
       
   604             }
       
   605         }
       
   606 
       
   607         /** Paint a skin at a defined position and size. This method
       
   608          *  does not trigger animation. It is needed for the animation
       
   609          *  support.
       
   610          *
       
   611          * @param g   the graphics context to use for painting
       
   612          * @param dx  the destination <i>x</i> coordinate.
       
   613          * @param dy  the destination <i>y</i> coordinate.
       
   614          * @param dw  the width of the area to fill, may cause
       
   615          *                  the image to be stretched or tiled
       
   616          * @param dh  the height of the area to fill, may cause
       
   617          *                  the image to be stretched or tiled
       
   618          * @param state which state to paint
       
   619          */
       
   620         void paintSkinRaw(Graphics g, int dx, int dy, int dw, int dh, State state) {
       
   621             if (XPStyle.getXP() == null) {
       
   622                 return;
       
   623             }
       
   624             skinPainter.paint(null, g, dx, dy, dw, dh, this, state);
       
   625         }
       
   626 
       
   627         /** Paint a skin at a defined position and size
       
   628          *
       
   629          * @param g   the graphics context to use for painting
       
   630          * @param dx  the destination <i>x</i> coordinate
       
   631          * @param dy  the destination <i>y</i> coordinate
       
   632          * @param dw  the width of the area to fill, may cause
       
   633          *                  the image to be stretched or tiled
       
   634          * @param dh  the height of the area to fill, may cause
       
   635          *                  the image to be stretched or tiled
       
   636          * @param state which state to paint
       
   637          * @param borderFill should test if the component uses a border fill
       
   638                             and skip painting if it is
       
   639          */
       
   640         void paintSkin(Graphics g, int dx, int dy, int dw, int dh, State state,
       
   641                 boolean borderFill) {
       
   642             if (XPStyle.getXP() == null) {
       
   643                 return;
       
   644             }
       
   645             if(borderFill && "borderfill".equals(getTypeEnumName(component, part,
       
   646                     state, Prop.BGTYPE))) {
       
   647                 return;
       
   648             }
       
   649             skinPainter.paint(null, g, dx, dy, dw, dh, this, state);
       
   650         }
       
   651     }
       
   652 
       
   653     private static class SkinPainter extends CachedPainter {
       
   654         SkinPainter() {
       
   655             super(30);
       
   656             flush();
       
   657         }
       
   658 
       
   659         public void flush() {
       
   660             super.flush();
       
   661         }
       
   662 
       
   663         protected void paintToImage(Component c, Image image, Graphics g,
       
   664                                     int w, int h, Object[] args) {
       
   665             Skin skin = (Skin)args[0];
       
   666             Part part = skin.part;
       
   667             State state = (State)args[1];
       
   668             if (state == null) {
       
   669                 state = skin.state;
       
   670             }
       
   671             if (c == null) {
       
   672                 c = skin.component;
       
   673             }
       
   674             BufferedImage bi = (BufferedImage)image;
       
   675             w = bi.getWidth();
       
   676             h = bi.getHeight();
       
   677 
       
   678             WritableRaster raster = bi.getRaster();
       
   679             DataBufferInt dbi = (DataBufferInt)raster.getDataBuffer();
       
   680             // Note that stealData() requires a markDirty() afterwards
       
   681             // since we modify the data in it.
       
   682             ThemeReader.paintBackground(SunWritableRaster.stealData(dbi, 0),
       
   683                                         part.getControlName(c), part.getValue(),
       
   684                                         State.getValue(part, state),
       
   685                                         0, 0, w, h, w);
       
   686             SunWritableRaster.markDirty(dbi);
       
   687         }
       
   688 
       
   689         protected Image createImage(Component c, int w, int h,
       
   690                                     GraphicsConfiguration config, Object[] args) {
       
   691             return new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
       
   692         }
       
   693     }
       
   694 
       
   695     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   696     static class GlyphButton extends JButton {
       
   697         protected Skin skin;
       
   698 
       
   699         public GlyphButton(Component parent, Part part) {
       
   700             XPStyle xp = getXP();
       
   701             skin = xp != null ? xp.getSkin(parent, part) : null;
       
   702             setBorder(null);
       
   703             setContentAreaFilled(false);
       
   704             setMinimumSize(new Dimension(5, 5));
       
   705             setPreferredSize(new Dimension(16, 16));
       
   706             setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
       
   707         }
       
   708 
       
   709         @SuppressWarnings("deprecation")
       
   710         public boolean isFocusTraversable() {
       
   711             return false;
       
   712         }
       
   713 
       
   714         protected State getState() {
       
   715             State state = State.NORMAL;
       
   716             if (!isEnabled()) {
       
   717                 state = State.DISABLED;
       
   718             } else if (getModel().isPressed()) {
       
   719                 state = State.PRESSED;
       
   720             } else if (getModel().isRollover()) {
       
   721                 state = State.HOT;
       
   722             }
       
   723             return state;
       
   724         }
       
   725 
       
   726         public void paintComponent(Graphics g) {
       
   727             if (XPStyle.getXP() == null || skin == null) {
       
   728                 return;
       
   729             }
       
   730             Dimension d = getSize();
       
   731             skin.paintSkin(g, 0, 0, d.width, d.height, getState());
       
   732         }
       
   733 
       
   734         public void setPart(Component parent, Part part) {
       
   735             XPStyle xp = getXP();
       
   736             skin = xp != null ? xp.getSkin(parent, part) : null;
       
   737             revalidate();
       
   738             repaint();
       
   739         }
       
   740 
       
   741         protected void paintBorder(Graphics g) {
       
   742         }
       
   743 
       
   744 
       
   745     }
       
   746 
       
   747     // Private constructor
       
   748     private XPStyle() {
       
   749         flatMenus = getSysBoolean(Prop.FLATMENUS);
       
   750 
       
   751         colorMap  = new HashMap<String, Color>();
       
   752         borderMap = new HashMap<String, Border>();
       
   753         // Note: All further access to the maps must be synchronized
       
   754     }
       
   755 
       
   756 
       
   757     private boolean getBoolean(Component c, Part part, State state, Prop prop) {
       
   758         return ThemeReader.getBoolean(part.getControlName(c), part.getValue(),
       
   759                                       State.getValue(part, state),
       
   760                                       prop.getValue());
       
   761     }
       
   762 
       
   763 
       
   764 
       
   765     static Dimension getPartSize(Part part, State state) {
       
   766         return ThemeReader.getPartSize(part.getControlName(null), part.getValue(),
       
   767                                        State.getValue(part, state));
       
   768     }
       
   769 
       
   770     private static boolean getSysBoolean(Prop prop) {
       
   771         // We can use any widget name here, I guess.
       
   772         return ThemeReader.getSysBoolean("window", prop.getValue());
       
   773     }
       
   774 }