1 /*
   2  * Copyright (c) 2005, 2014, 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 package javax.swing.plaf.nimbus;
  26 
  27 import javax.swing.Painter;
  28 
  29 import javax.swing.JComponent;
  30 import javax.swing.UIDefaults;
  31 import javax.swing.UIManager;
  32 import javax.swing.plaf.ColorUIResource;
  33 import javax.swing.plaf.synth.ColorType;
  34 import static javax.swing.plaf.synth.SynthConstants.*;
  35 import javax.swing.plaf.synth.SynthContext;
  36 import javax.swing.plaf.synth.SynthPainter;
  37 import javax.swing.plaf.synth.SynthStyle;
  38 import java.awt.Color;
  39 import java.awt.Font;
  40 import java.awt.Insets;
  41 import java.lang.ref.WeakReference;
  42 import java.util.ArrayList;
  43 import java.util.Collections;
  44 import java.util.Comparator;
  45 import java.util.HashMap;
  46 import java.util.List;
  47 import java.util.Map;
  48 import java.util.TreeMap;
  49 
  50 /**
  51  * <p>A SynthStyle implementation used by Nimbus. Each Region that has been
  52  * registered with the NimbusLookAndFeel will have an associated NimbusStyle.
  53  * Third party components that are registered with the NimbusLookAndFeel will
  54  * therefore be handed a NimbusStyle from the look and feel from the
  55  * #getStyle(JComponent, Region) method.</p>
  56  *
  57  * <p>This class properly reads and retrieves values placed in the UIDefaults
  58  * according to the standard Nimbus naming conventions. It will create and
  59  * retrieve painters, fonts, colors, and other data stored there.</p>
  60  *
  61  * <p>NimbusStyle also supports the ability to override settings on a per
  62  * component basis. NimbusStyle checks the component's client property map for
  63  * "Nimbus.Overrides". If the value associated with this key is an instance of
  64  * UIDefaults, then the values in that defaults table will override the standard
  65  * Nimbus defaults in UIManager, but for that component instance only.</p>
  66  *
  67  * <p>Optionally, you may specify the client property
  68  * "Nimbus.Overrides.InheritDefaults". If true, this client property indicates
  69  * that the defaults located in UIManager should first be read, and then
  70  * replaced with defaults located in the component client properties. If false,
  71  * then only the defaults located in the component client property map will
  72  * be used. If not specified, it is assumed to be true.</p>
  73  *
  74  * <p>You must specify "Nimbus.Overrides" for "Nimbus.Overrides.InheritDefaults"
  75  * to have any effect. "Nimbus.Overrides" indicates whether there are any
  76  * overrides, while "Nimbus.Overrides.InheritDefaults" indicates whether those
  77  * overrides should first be initialized with the defaults from UIManager.</p>
  78  *
  79  * <p>The NimbusStyle is reloaded whenever a property change event is fired
  80  * for a component for "Nimbus.Overrides" or "Nimbus.Overrides.InheritDefaults".
  81  * So for example, setting a new UIDefaults on a component would cause the
  82  * style to be reloaded.</p>
  83  *
  84  * <p>The values are only read out of UIManager once, and then cached. If
  85  * you need to read the values again (for example, if the UI is being reloaded),
  86  * then discard this NimbusStyle and read a new one from NimbusLookAndFeel
  87  * using NimbusLookAndFeel.getStyle.</p>
  88  *
  89  * <p>The primary API of interest in this class for 3rd party component authors
  90  * are the three methods which retrieve painters: #getBackgroundPainter,
  91  * #getForegroundPainter, and #getBorderPainter.</p>
  92  *
  93  * <p>NimbusStyle allows you to specify custom states, or modify the order of
  94  * states. Synth (and thus Nimbus) has the concept of a "state". For example,
  95  * a JButton might be in the "MOUSE_OVER" state, or the "ENABLED" state, or the
  96  * "DISABLED" state. These are all "standard" states which are defined in synth,
  97  * and which apply to all synth Regions.</p>
  98  *
  99  * <p>Sometimes, however, you need to have a custom state. For example, you
 100  * want JButton to render differently if it's parent is a JToolbar. In Nimbus,
 101  * you specify these custom states by including a special key in UIDefaults.
 102  * The following UIDefaults entries define three states for this button:</p>
 103  *
 104  * <pre><code>
 105  *     JButton.States = Enabled, Disabled, Toolbar
 106  *     JButton[Enabled].backgroundPainter = somePainter
 107  *     JButton[Disabled].background = BLUE
 108  *     JButton[Toolbar].backgroundPainter = someOtherPaint
 109  * </code></pre>
 110  *
 111  * <p>As you can see, the <code>JButton.States</code> entry lists the states
 112  * that the JButton style will support. You then specify the settings for
 113  * each state. If you do not specify the <code>JButton.States</code> entry,
 114  * then the standard Synth states will be assumed. If you specify the entry
 115  * but the list of states is empty or null, then the standard synth states
 116  * will be assumed.</p>
 117  *
 118  * @author Richard Bair
 119  * @author Jasper Potts
 120  */
 121 public final class NimbusStyle extends SynthStyle {
 122     /* Keys and scales for large/small/mini components, based on Apples sizes */
 123     /** Large key */
 124     public static final String LARGE_KEY = "large";
 125     /** Small key */
 126     public static final String SMALL_KEY = "small";
 127     /** Mini key */
 128     public static final String MINI_KEY = "mini";
 129     /** Large scale */
 130     public static final double LARGE_SCALE = 1.15;
 131     /** Small scale */
 132     public static final double SMALL_SCALE = 0.857;
 133     /** Mini scale */
 134     public static final double MINI_SCALE = 0.714;
 135 
 136     /**
 137      * Special constant used for performance reasons during the get() method.
 138      * If get() runs through all of the search locations and determines that
 139      * there is no value, then NULL will be placed into the values map. This way
 140      * on subsequent lookups it will simply extract NULL, see it, and return
 141      * null rather than continuing the lookup procedure.
 142      */
 143     private static final Object NULL = '\0';
 144     /**
 145      * <p>The Color to return from getColorForState if it would otherwise have
 146      * returned null.</p>
 147      *
 148      * <p>Returning null from getColorForState is a very bad thing, as it causes
 149      * the AWT peer for the component to install a SystemColor, which is not a
 150      * UIResource. As a result, if <code>null</code> is returned from
 151      * getColorForState, then thereafter the color is not updated for other
 152      * states or on LAF changes or updates. This DEFAULT_COLOR is used to
 153      * ensure that a ColorUIResource is always returned from
 154      * getColorForState.</p>
 155      */
 156     private static final Color DEFAULT_COLOR = new ColorUIResource(Color.BLACK);
 157     /**
 158      * Simple Comparator for ordering the RuntimeStates according to their
 159      * rank.
 160      */
 161     private static final Comparator<RuntimeState> STATE_COMPARATOR =
 162         new Comparator<RuntimeState>() {
 163             @Override
 164             public int compare(RuntimeState a, RuntimeState b) {
 165                 return a.state - b.state;
 166             }
 167         };
 168     /**
 169      * The prefix for the component or region that this NimbusStyle
 170      * represents. This prefix is used to lookup state in the UIManager.
 171      * It should be something like Button or Slider.Thumb or "MyButton" or
 172      * ComboBox."ComboBox.arrowButton" or "MyComboBox"."ComboBox.arrowButton"
 173      */
 174     private String prefix;
 175     /**
 176      * The SynthPainter that will be returned from this NimbusStyle. The
 177      * SynthPainter returned will be a SynthPainterImpl, which will in turn
 178      * delegate back to this NimbusStyle for the proper Painter (not
 179      * SynthPainter) to use for painting the foreground, background, or border.
 180      */
 181     private SynthPainter painter;
 182     /**
 183      * Data structure containing all of the defaults, insets, states, and other
 184      * values associated with this style. This instance refers to default
 185      * values, and are used when no overrides are discovered in the client
 186      * properties of a component. These values are lazily created on first
 187      * access.
 188      */
 189     private Values values;
 190 
 191     /**
 192      * A temporary CacheKey used to perform lookups. This pattern avoids
 193      * creating useless garbage keys, or concatenating strings, etc.
 194      */
 195     private CacheKey tmpKey = new CacheKey("", 0);
 196 
 197     /**
 198      * Some NimbusStyles are created for a specific component only. In Nimbus,
 199      * this happens whenever the component has as a client property a
 200      * UIDefaults which overrides (or supplements) those defaults found in
 201      * UIManager.
 202      */
 203     private WeakReference<JComponent> component;
 204 
 205     /**
 206      * Create a new NimbusStyle. Only the prefix must be supplied. At the
 207      * appropriate time, installDefaults will be called. At that point, all of
 208      * the state information will be pulled from UIManager and stored locally
 209      * within this style.
 210      *
 211      * @param prefix Something like Button or Slider.Thumb or
 212      *        org.jdesktop.swingx.JXStatusBar or ComboBox."ComboBox.arrowButton"
 213      * @param c an optional reference to a component that this NimbusStyle
 214      *        should be associated with. This is only used when the component
 215      *        has Nimbus overrides registered in its client properties and
 216      *        should be null otherwise.
 217      */
 218     NimbusStyle(String prefix, JComponent c) {
 219         if (c != null) {
 220             this.component = new WeakReference<JComponent>(c);
 221         }
 222         this.prefix = prefix;
 223         this.painter = new SynthPainterImpl(this);
 224     }
 225 
 226     /**
 227      * {@inheritDoc}
 228      *
 229      * Overridden to cause this style to populate itself with data from
 230      * UIDefaults, if necessary.
 231      */
 232     @Override public void installDefaults(SynthContext ctx) {
 233         validate();
 234 
 235         //delegate to the superclass to install defaults such as background,
 236         //foreground, font, and opaque onto the swing component.
 237         super.installDefaults(ctx);
 238     }
 239 
 240     /**
 241      * Pulls data out of UIDefaults, if it has not done so already, and sets
 242      * up the internal state.
 243      */
 244     private void validate() {
 245         // a non-null values object is the flag we use to determine whether
 246         // to reparse from UIManager.
 247         if (values != null) return;
 248 
 249         // reconstruct this NimbusStyle based on the entries in the UIManager
 250         // and possibly based on any overrides within the component's
 251         // client properties (assuming such a component exists and contains
 252         // any Nimbus.Overrides)
 253         values = new Values();
 254 
 255         Map<String, Object> defaults =
 256                 ((NimbusLookAndFeel) UIManager.getLookAndFeel()).
 257                         getDefaultsForPrefix(prefix);
 258 
 259         // inspect the client properties for the key "Nimbus.Overrides". If the
 260         // value is an instance of UIDefaults, then these defaults are used
 261         // in place of, or in addition to, the defaults in UIManager.
 262         if (component != null) {
 263             // We know component.get() is non-null here, as if the component
 264             // were GC'ed, we wouldn't be processing its style.
 265             Object o = component.get().getClientProperty("Nimbus.Overrides");
 266             if (o instanceof UIDefaults) {
 267                 Object i = component.get().getClientProperty(
 268                         "Nimbus.Overrides.InheritDefaults");
 269                 boolean inherit = i instanceof Boolean ? (Boolean)i : true;
 270                 UIDefaults d = (UIDefaults)o;
 271                 TreeMap<String, Object> map = new TreeMap<String, Object>();
 272                 for (Object obj : d.keySet()) {
 273                     if (obj instanceof String) {
 274                         String key = (String)obj;
 275                         if (key.startsWith(prefix)) {
 276                             map.put(key, d.get(key));
 277                         }
 278                     }
 279                 }
 280                 if (inherit) {
 281                     defaults.putAll(map);
 282                 } else {
 283                     defaults = map;
 284                 }
 285             }
 286         }
 287 
 288         //a list of the different types of states used by this style. This
 289         //list may contain only "standard" states (those defined by Synth),
 290         //or it may contain custom states, or it may contain only "standard"
 291         //states but list them in a non-standard order.
 292         List<State<?>> states = new ArrayList<>();
 293         //a map of state name to code
 294         Map<String,Integer> stateCodes = new HashMap<>();
 295         //This is a list of runtime "state" context objects. These contain
 296         //the values associated with each state.
 297         List<RuntimeState> runtimeStates = new ArrayList<>();
 298 
 299         //determine whether there are any custom states, or custom state
 300         //order. If so, then read all those custom states and define the
 301         //"values" stateTypes to be a non-null array.
 302         //Otherwise, let the "values" stateTypes be null to indicate that
 303         //there are no custom states or custom state ordering
 304         String statesString = (String)defaults.get(prefix + ".States");
 305         if (statesString != null) {
 306             String s[] = statesString.split(",");
 307             for (int i=0; i<s.length; i++) {
 308                 s[i] = s[i].trim();
 309                 if (!State.isStandardStateName(s[i])) {
 310                     //this is a non-standard state name, so look for the
 311                     //custom state associated with it
 312                     String stateName = prefix + "." + s[i];
 313                     State<?> customState = (State)defaults.get(stateName);
 314                     if (customState != null) {
 315                         states.add(customState);
 316                     }
 317                 } else {
 318                     states.add(State.getStandardState(s[i]));
 319                 }
 320             }
 321 
 322             //if there were any states defined, then set the stateTypes array
 323             //to be non-null. Otherwise, leave it null (meaning, use the
 324             //standard synth states).
 325             if (states.size() > 0) {
 326                 values.stateTypes = states.toArray(new State<?>[states.size()]);
 327             }
 328 
 329             //assign codes for each of the state types
 330             int code = 1;
 331             for (State<?> state : states) {
 332                 stateCodes.put(state.getName(), code);
 333                 code <<= 1;
 334             }
 335         } else {
 336             //since there were no custom states defined, setup the list of
 337             //standard synth states. Note that the "v.stateTypes" is not
 338             //being set here, indicating that at runtime the state selection
 339             //routines should use standard synth states instead of custom
 340             //states. I do need to popuplate this temp list now though, so that
 341             //the remainder of this method will function as expected.
 342             states.add(State.Enabled);
 343             states.add(State.MouseOver);
 344             states.add(State.Pressed);
 345             states.add(State.Disabled);
 346             states.add(State.Focused);
 347             states.add(State.Selected);
 348             states.add(State.Default);
 349 
 350             //assign codes for the states
 351             stateCodes.put("Enabled", ENABLED);
 352             stateCodes.put("MouseOver", MOUSE_OVER);
 353             stateCodes.put("Pressed", PRESSED);
 354             stateCodes.put("Disabled", DISABLED);
 355             stateCodes.put("Focused", FOCUSED);
 356             stateCodes.put("Selected", SELECTED);
 357             stateCodes.put("Default", DEFAULT);
 358         }
 359 
 360         //Now iterate over all the keys in the defaults table
 361         for (String key : defaults.keySet()) {
 362             //The key is something like JButton.Enabled.backgroundPainter,
 363             //or JButton.States, or JButton.background.
 364             //Remove the "JButton." portion of the key
 365             String temp = key.substring(prefix.length());
 366             //if there is a " or : then we skip it because it is a subregion
 367             //of some kind
 368             if (temp.indexOf('"') != -1 || temp.indexOf(':') != -1) continue;
 369             //remove the separator
 370             temp = temp.substring(1);
 371             //At this point, temp may be any of the following:
 372             //background
 373             //[Enabled].background
 374             //[Enabled+MouseOver].background
 375             //property.foo
 376 
 377             //parse out the states and the property
 378             String stateString = null;
 379             String property = null;
 380             int bracketIndex = temp.indexOf(']');
 381             if (bracketIndex < 0) {
 382                 //there is not a state string, so property = temp
 383                 property = temp;
 384             } else {
 385                 stateString = temp.substring(0, bracketIndex);
 386                 property = temp.substring(bracketIndex + 2);
 387             }
 388 
 389             //now that I have the state (if any) and the property, get the
 390             //value for this property and install it where it belongs
 391             if (stateString == null) {
 392                 //there was no state, just a property. Check for the custom
 393                 //"contentMargins" property (which is handled specially by
 394                 //Synth/Nimbus). Also check for the property being "States",
 395                 //in which case it is not a real property and should be ignored.
 396                 //otherwise, assume it is a property and install it on the
 397                 //values object
 398                 if ("contentMargins".equals(property)) {
 399                     values.contentMargins = (Insets)defaults.get(key);
 400                 } else if ("States".equals(property)) {
 401                     //ignore
 402                 } else {
 403                     values.defaults.put(property, defaults.get(key));
 404                 }
 405             } else {
 406                 //it is possible that the developer has a malformed UIDefaults
 407                 //entry, such that something was specified in the place of
 408                 //the State portion of the key but it wasn't a state. In this
 409                 //case, skip will be set to true
 410                 boolean skip = false;
 411                 //this variable keeps track of the int value associated with
 412                 //the state. See SynthState for details.
 413                 int componentState = 0;
 414                 //Multiple states may be specified in the string, such as
 415                 //Enabled+MouseOver
 416                 String[] stateParts = stateString.split("\\+");
 417                 //For each state, we need to find the State object associated
 418                 //with it, or skip it if it cannot be found.
 419                 for (String s : stateParts) {
 420                     if (stateCodes.containsKey(s)) {
 421                         componentState |= stateCodes.get(s);
 422                     } else {
 423                         //Was not a state. Maybe it was a subregion or something
 424                         //skip it.
 425                         skip = true;
 426                         break;
 427                     }
 428                 }
 429 
 430                 if (skip) continue;
 431 
 432                 //find the RuntimeState for this State
 433                 RuntimeState rs = null;
 434                 for (RuntimeState s : runtimeStates) {
 435                     if (s.state == componentState) {
 436                         rs = s;
 437                         break;
 438                     }
 439                 }
 440 
 441                 //couldn't find the runtime state, so create a new one
 442                 if (rs == null) {
 443                     rs = new RuntimeState(componentState, stateString);
 444                     runtimeStates.add(rs);
 445                 }
 446 
 447                 //check for a couple special properties, such as for the
 448                 //painters. If these are found, then set the specially on
 449                 //the runtime state. Else, it is just a normal property,
 450                 //so put it in the UIDefaults associated with that runtime
 451                 //state
 452                 if ("backgroundPainter".equals(property)) {
 453                     rs.backgroundPainter = getPainter(defaults, key);
 454                 } else if ("foregroundPainter".equals(property)) {
 455                     rs.foregroundPainter = getPainter(defaults, key);
 456                 } else if ("borderPainter".equals(property)) {
 457                     rs.borderPainter = getPainter(defaults, key);
 458                 } else {
 459                     rs.defaults.put(property, defaults.get(key));
 460                 }
 461             }
 462         }
 463 
 464         //now that I've collected all the runtime states, I'll sort them based
 465         //on their integer "state" (see SynthState for how this works).
 466         Collections.sort(runtimeStates, STATE_COMPARATOR);
 467 
 468         //finally, set the array of runtime states on the values object
 469         values.states = runtimeStates.toArray(new RuntimeState[runtimeStates.size()]);
 470     }
 471 
 472     private Painter<Object> getPainter(Map<String, Object> defaults, String key) {
 473         Object p = defaults.get(key);
 474         if (p instanceof UIDefaults.LazyValue) {
 475             p = ((UIDefaults.LazyValue)p).createValue(UIManager.getDefaults());
 476         }
 477         @SuppressWarnings("unchecked")
 478         Painter<Object> tmp = (p instanceof Painter ? (Painter)p : null);
 479         return tmp;
 480     }
 481 
 482     /**
 483      * {@inheritDoc}
 484      *
 485      * Overridden to cause this style to populate itself with data from
 486      * UIDefaults, if necessary.
 487      */
 488     @Override public Insets getInsets(SynthContext ctx, Insets in) {
 489         if (in == null) {
 490             in = new Insets(0, 0, 0, 0);
 491         }
 492 
 493         Values v = getValues(ctx);
 494 
 495         if (v.contentMargins == null) {
 496             in.bottom = in.top = in.left = in.right = 0;
 497             return in;
 498         } else {
 499             in.bottom = v.contentMargins.bottom;
 500             in.top = v.contentMargins.top;
 501             in.left = v.contentMargins.left;
 502             in.right = v.contentMargins.right;
 503             // Account for scale
 504             // The key "JComponent.sizeVariant" is used to match Apple's LAF
 505             String scaleKey = (String)ctx.getComponent().getClientProperty(
 506                     "JComponent.sizeVariant");
 507             if (scaleKey != null){
 508                 if (LARGE_KEY.equals(scaleKey)){
 509                     in.bottom *= LARGE_SCALE;
 510                     in.top *= LARGE_SCALE;
 511                     in.left *= LARGE_SCALE;
 512                     in.right *= LARGE_SCALE;
 513                 } else if (SMALL_KEY.equals(scaleKey)){
 514                     in.bottom *= SMALL_SCALE;
 515                     in.top *= SMALL_SCALE;
 516                     in.left *= SMALL_SCALE;
 517                     in.right *= SMALL_SCALE;
 518                 } else if (MINI_KEY.equals(scaleKey)){
 519                     in.bottom *= MINI_SCALE;
 520                     in.top *= MINI_SCALE;
 521                     in.left *= MINI_SCALE;
 522                     in.right *= MINI_SCALE;
 523                 }
 524             }
 525             return in;
 526         }
 527     }
 528 
 529     /**
 530      * {@inheritDoc}
 531      *
 532      * <p>Overridden to cause this style to populate itself with data from
 533      * UIDefaults, if necessary.</p>
 534      *
 535      * <p>In addition, NimbusStyle handles ColorTypes slightly differently from
 536      * Synth.</p>
 537      * <ul>
 538      *  <li>ColorType.BACKGROUND will equate to the color stored in UIDefaults
 539      *      named "background".</li>
 540      *  <li>ColorType.TEXT_BACKGROUND will equate to the color stored in
 541      *      UIDefaults named "textBackground".</li>
 542      *  <li>ColorType.FOREGROUND will equate to the color stored in UIDefaults
 543      *      named "textForeground".</li>
 544      *  <li>ColorType.TEXT_FOREGROUND will equate to the color stored in
 545      *      UIDefaults named "textForeground".</li>
 546      * </ul>
 547      */
 548     @Override protected Color getColorForState(SynthContext ctx, ColorType type) {
 549         String key = null;
 550         if (type == ColorType.BACKGROUND) {
 551             key = "background";
 552         } else if (type == ColorType.FOREGROUND) {
 553             //map FOREGROUND as TEXT_FOREGROUND
 554             key = "textForeground";
 555         } else if (type == ColorType.TEXT_BACKGROUND) {
 556             key = "textBackground";
 557         } else if (type == ColorType.TEXT_FOREGROUND) {
 558             key = "textForeground";
 559         } else if (type == ColorType.FOCUS) {
 560             key = "focus";
 561         } else if (type != null) {
 562             key = type.toString();
 563         } else {
 564             return DEFAULT_COLOR;
 565         }
 566         Color c = (Color) get(ctx, key);
 567         //if all else fails, return a default color (which is a ColorUIResource)
 568         if (c == null) c = DEFAULT_COLOR;
 569         return c;
 570     }
 571 
 572     /**
 573      * {@inheritDoc}
 574      *
 575      * Overridden to cause this style to populate itself with data from
 576      * UIDefaults, if necessary. If a value named "font" is not found in
 577      * UIDefaults, then the "defaultFont" font in UIDefaults will be returned
 578      * instead.
 579      */
 580     @Override protected Font getFontForState(SynthContext ctx) {
 581         Font f = (Font)get(ctx, "font");
 582         if (f == null) f = UIManager.getFont("defaultFont");
 583 
 584         // Account for scale
 585         // The key "JComponent.sizeVariant" is used to match Apple's LAF
 586         String scaleKey = (String)ctx.getComponent().getClientProperty(
 587                 "JComponent.sizeVariant");
 588         if (scaleKey != null){
 589             if (LARGE_KEY.equals(scaleKey)){
 590                 f = f.deriveFont(Math.round(f.getSize2D()*LARGE_SCALE));
 591             } else if (SMALL_KEY.equals(scaleKey)){
 592                 f = f.deriveFont(Math.round(f.getSize2D()*SMALL_SCALE));
 593             } else if (MINI_KEY.equals(scaleKey)){
 594                 f = f.deriveFont(Math.round(f.getSize2D()*MINI_SCALE));
 595             }
 596         }
 597         return f;
 598     }
 599 
 600     /**
 601      * {@inheritDoc}
 602      *
 603      * Returns the SynthPainter for this style, which ends up delegating to
 604      * the Painters installed in this style.
 605      */
 606     @Override public SynthPainter getPainter(SynthContext ctx) {
 607         return painter;
 608     }
 609 
 610     /**
 611      * {@inheritDoc}
 612      *
 613      * Overridden to cause this style to populate itself with data from
 614      * UIDefaults, if necessary. If opacity is not specified in UI defaults,
 615      * then it defaults to being non-opaque.
 616      */
 617     @Override public boolean isOpaque(SynthContext ctx) {
 618         // Force Table CellRenderers to be opaque
 619         if ("Table.cellRenderer".equals(ctx.getComponent().getName())) {
 620             return true;
 621         }
 622         Boolean opaque = (Boolean)get(ctx, "opaque");
 623         return opaque == null ? false : opaque;
 624     }
 625 
 626     /**
 627      * {@inheritDoc}
 628      *
 629      * <p>Overridden to cause this style to populate itself with data from
 630      * UIDefaults, if necessary.</p>
 631      *
 632      * <p>Properties in UIDefaults may be specified in a chained manner. For
 633      * example:
 634      * <pre>
 635      * background
 636      * Button.opacity
 637      * Button.Enabled.foreground
 638      * Button.Enabled+Selected.background
 639      * </pre>
 640      *
 641      * <p>In this example, suppose you were in the Enabled+Selected state and
 642      * searched for "foreground". In this case, we first check for
 643      * Button.Enabled+Selected.foreground, but no such color exists. We then
 644      * fall back to the next valid state, in this case,
 645      * Button.Enabled.foreground, and have a match. So we return it.</p>
 646      *
 647      * <p>Again, if we were in the state Enabled and looked for "background", we
 648      * wouldn't find it in Button.Enabled, or in Button, but would at the top
 649      * level in UIManager. So we return that value.</p>
 650      *
 651      * <p>One special note: the "key" passed to this method could be of the form
 652      * "background" or "Button.background" where "Button" equals the prefix
 653      * passed to the NimbusStyle constructor. In either case, it looks for
 654      * "background".</p>
 655      *
 656      * @param ctx SynthContext identifying requester
 657      * @param key must not be null
 658      */
 659     @Override public Object get(SynthContext ctx, Object key) {
 660         Values v = getValues(ctx);
 661 
 662         // strip off the prefix, if there is one.
 663         String fullKey = key.toString();
 664         String partialKey = fullKey.substring(fullKey.indexOf('.') + 1);
 665 
 666         Object obj = null;
 667         int xstate = getExtendedState(ctx, v);
 668 
 669         // check the cache
 670         tmpKey.init(partialKey, xstate);
 671         obj = v.cache.get(tmpKey);
 672         boolean wasInCache = obj != null;
 673         if (!wasInCache){
 674             // Search exact matching states and then lesser matching states
 675             RuntimeState s = null;
 676             int[] lastIndex = new int[] {-1};
 677             while (obj == null &&
 678                     (s = getNextState(v.states, lastIndex, xstate)) != null) {
 679                 obj = s.defaults.get(partialKey);
 680             }
 681             // Search Region Defaults
 682             if (obj == null && v.defaults != null) {
 683                 obj = v.defaults.get(partialKey);
 684             }
 685             // return found object
 686             // Search UIManager Defaults
 687             if (obj == null) obj = UIManager.get(fullKey);
 688             // Search Synth Defaults for InputMaps
 689             if (obj == null && partialKey.equals("focusInputMap")) {
 690                 obj = super.get(ctx, fullKey);
 691             }
 692             // if all we got was a null, store this fact for later use
 693             v.cache.put(new CacheKey(partialKey, xstate),
 694                     obj == null ? NULL : obj);
 695         }
 696         // return found object
 697         return obj == NULL ? null : obj;
 698     }
 699 
 700     @SuppressWarnings("unchecked")
 701     private static Painter<Object> paintFilter(@SuppressWarnings("rawtypes") Painter painter) {
 702         return (Painter<Object>) painter;
 703     }
 704 
 705 
 706     /**
 707      * Gets the appropriate background Painter, if there is one, for the state
 708      * specified in the given SynthContext. This method does appropriate
 709      * fallback searching, as described in #get.
 710      *
 711      * @param ctx The SynthContext. Must not be null.
 712      * @return The background painter associated for the given state, or null if
 713      * none could be found.
 714      */
 715     public Painter<Object> getBackgroundPainter(SynthContext ctx) {
 716         Values v = getValues(ctx);
 717         int xstate = getExtendedState(ctx, v);
 718         Painter<Object> p = null;
 719 
 720         // check the cache
 721         tmpKey.init("backgroundPainter$$instance", xstate);
 722         p = paintFilter((Painter)v.cache.get(tmpKey));
 723         if (p != null) return p;
 724 
 725         // not in cache, so lookup and store in cache
 726         RuntimeState s = null;
 727         int[] lastIndex = new int[] {-1};
 728         while ((s = getNextState(v.states, lastIndex, xstate)) != null) {
 729             if (s.backgroundPainter != null) {
 730                 p = paintFilter(s.backgroundPainter);
 731                 break;
 732             }
 733         }
 734         if (p == null) p = paintFilter((Painter)get(ctx, "backgroundPainter"));
 735         if (p != null) {
 736             v.cache.put(new CacheKey("backgroundPainter$$instance", xstate), p);
 737         }
 738         return p;
 739     }
 740 
 741     /**
 742      * Gets the appropriate foreground Painter, if there is one, for the state
 743      * specified in the given SynthContext. This method does appropriate
 744      * fallback searching, as described in #get.
 745      *
 746      * @param ctx The SynthContext. Must not be null.
 747      * @return The foreground painter associated for the given state, or null if
 748      * none could be found.
 749      */
 750     public Painter<Object> getForegroundPainter(SynthContext ctx) {
 751         Values v = getValues(ctx);
 752         int xstate = getExtendedState(ctx, v);
 753         Painter<Object> p = null;
 754 
 755         // check the cache
 756         tmpKey.init("foregroundPainter$$instance", xstate);
 757         p = paintFilter((Painter)v.cache.get(tmpKey));
 758         if (p != null) return p;
 759 
 760         // not in cache, so lookup and store in cache
 761         RuntimeState s = null;
 762         int[] lastIndex = new int[] {-1};
 763         while ((s = getNextState(v.states, lastIndex, xstate)) != null) {
 764             if (s.foregroundPainter != null) {
 765                 p = paintFilter(s.foregroundPainter);
 766                 break;
 767             }
 768         }
 769         if (p == null) p = paintFilter((Painter)get(ctx, "foregroundPainter"));
 770         if (p != null) {
 771             v.cache.put(new CacheKey("foregroundPainter$$instance", xstate), p);
 772         }
 773         return p;
 774     }
 775 
 776     /**
 777      * Gets the appropriate border Painter, if there is one, for the state
 778      * specified in the given SynthContext. This method does appropriate
 779      * fallback searching, as described in #get.
 780      *
 781      * @param ctx The SynthContext. Must not be null.
 782      * @return The border painter associated for the given state, or null if
 783      * none could be found.
 784      */
 785     public Painter<Object> getBorderPainter(SynthContext ctx) {
 786         Values v = getValues(ctx);
 787         int xstate = getExtendedState(ctx, v);
 788         Painter<Object> p = null;
 789 
 790         // check the cache
 791         tmpKey.init("borderPainter$$instance", xstate);
 792         p = paintFilter((Painter)v.cache.get(tmpKey));
 793         if (p != null) return p;
 794 
 795         // not in cache, so lookup and store in cache
 796         RuntimeState s = null;
 797         int[] lastIndex = new int[] {-1};
 798         while ((s = getNextState(v.states, lastIndex, xstate)) != null) {
 799             if (s.borderPainter != null) {
 800                 p = paintFilter(s.borderPainter);
 801                 break;
 802             }
 803         }
 804         if (p == null) p = paintFilter((Painter)get(ctx, "borderPainter"));
 805         if (p != null) {
 806             v.cache.put(new CacheKey("borderPainter$$instance", xstate), p);
 807         }
 808         return p;
 809     }
 810 
 811     /**
 812      * Utility method which returns the proper Values based on the given
 813      * SynthContext. Ensures that parsing of the values has occurred, or
 814      * reoccurs as necessary.
 815      *
 816      * @param ctx The SynthContext
 817      * @return a non-null values reference
 818      */
 819     private Values getValues(SynthContext ctx) {
 820         validate();
 821         return values;
 822     }
 823 
 824     /**
 825      * Simple utility method that searches the given array of Strings for the
 826      * given string. This method is only called from getExtendedState if
 827      * the developer has specified a specific state for the component to be
 828      * in (ie, has "wedged" the component in that state) by specifying
 829      * they client property "Nimbus.State".
 830      *
 831      * @param names a non-null array of strings
 832      * @param name the name to look for in the array
 833      * @return true or false based on whether the given name is in the array
 834      */
 835     private boolean contains(String[] names, String name) {
 836         assert name != null;
 837         for (int i=0; i<names.length; i++) {
 838             if (name.equals(names[i])) {
 839                 return true;
 840             }
 841         }
 842         return false;
 843     }
 844 
 845     /**
 846      * <p>Gets the extended state for a given synth context. Nimbus supports the
 847      * ability to define custom states. The algorithm used for choosing what
 848      * style information to use for a given state requires a single integer
 849      * bit string where each bit in the integer represents a different state
 850      * that the component is in. This method uses the componentState as
 851      * reported in the SynthContext, in addition to custom states, to determine
 852      * what this extended state is.</p>
 853      *
 854      * <p>In addition, this method checks the component in the given context
 855      * for a client property called "Nimbus.State". If one exists, then it will
 856      * decompose the String associated with that property to determine what
 857      * state to return. In this way, the developer can force a component to be
 858      * in a specific state, regardless of what the "real" state of the component
 859      * is.</p>
 860      *
 861      * <p>The string associated with "Nimbus.State" would be of the form:
 862      * <pre>Enabled+CustomState+MouseOver</pre></p>
 863      *
 864      * @param ctx
 865      * @param v
 866      * @return
 867      */
 868     @SuppressWarnings({"unchecked", "rawtypes"})
 869     private int getExtendedState(SynthContext ctx, Values v) {
 870         JComponent c = ctx.getComponent();
 871         int xstate = 0;
 872         int mask = 1;
 873         //check for the Nimbus.State client property
 874         //Performance NOTE: getClientProperty ends up inside a synchronized
 875         //block, so there is some potential for performance issues here, however
 876         //I'm not certain that there is one on a modern VM.
 877         Object property = c.getClientProperty("Nimbus.State");
 878         if (property != null) {
 879             String stateNames = property.toString();
 880             String[] states = stateNames.split("\\+");
 881             if (v.stateTypes == null){
 882                 // standard states only
 883                 for (String stateStr : states) {
 884                     State.StandardState s = State.getStandardState(stateStr);
 885                     if (s != null) xstate |= s.getState();
 886                 }
 887             } else {
 888                 // custom states
 889                 for (State<?> s : v.stateTypes) {
 890                     if (contains(states, s.getName())) {
 891                         xstate |= mask;
 892                     }
 893                     mask <<= 1;
 894                 }
 895             }
 896         } else {
 897             //if there are no custom states defined, then simply return the
 898             //state that Synth reported
 899             if (v.stateTypes == null) return ctx.getComponentState();
 900 
 901             //there are custom states on this values, so I'll have to iterate
 902             //over them all and return a custom extended state
 903             int state = ctx.getComponentState();
 904             for (State s : v.stateTypes) {
 905                 if (s.isInState(c, state)) {
 906                     xstate |= mask;
 907                 }
 908                 mask <<= 1;
 909             }
 910         }
 911         return xstate;
 912     }
 913 
 914     /**
 915      * <p>Gets the RuntimeState that most closely matches the state in the given
 916      * context, but is less specific than the given "lastState". Essentially,
 917      * this allows you to search for the next best state.</p>
 918      *
 919      * <p>For example, if you had the following three states:
 920      * <pre>
 921      * Enabled
 922      * Enabled+Pressed
 923      * Disabled
 924      * </pre>
 925      * And you wanted to find the state that best represented
 926      * ENABLED+PRESSED+FOCUSED and <code>lastState</code> was null (or an
 927      * empty array, or an array with a single int with index == -1), then
 928      * Enabled+Pressed would be returned. If you then call this method again but
 929      * pass the index of Enabled+Pressed as the "lastState", then
 930      * Enabled would be returned. If you call this method a third time and pass
 931      * the index of Enabled in as the <code>lastState</code>, then null would be
 932      * returned.</p>
 933      *
 934      * <p>The actual code path for determining the proper state is the same as
 935      * in Synth.</p>
 936      *
 937      * @param ctx
 938      * @param lastState a 1 element array, allowing me to do pass-by-reference.
 939      * @return
 940      */
 941     private RuntimeState getNextState(RuntimeState[] states,
 942                                       int[] lastState,
 943                                       int xstate) {
 944         // Use the StateInfo with the most bits that matches that of state.
 945         // If there are none, then fallback to
 946         // the StateInfo with a state of 0, indicating it'll match anything.
 947 
 948         // Consider if we have 3 StateInfos a, b and c with states:
 949         // SELECTED, SELECTED | ENABLED, 0
 950         //
 951         // Input                          Return Value
 952         // -----                          ------------
 953         // SELECTED                       a
 954         // SELECTED | ENABLED             b
 955         // MOUSE_OVER                     c
 956         // SELECTED | ENABLED | FOCUSED   b
 957         // ENABLED                        c
 958 
 959         if (states != null && states.length > 0) {
 960             int bestCount = 0;
 961             int bestIndex = -1;
 962             int wildIndex = -1;
 963 
 964             //if xstate is 0, then search for the runtime state with component
 965             //state of 0. That is, find the exact match and return it.
 966             if (xstate == 0) {
 967                 for (int counter = states.length - 1; counter >= 0; counter--) {
 968                     if (states[counter].state == 0) {
 969                         lastState[0] = counter;
 970                         return states[counter];
 971                     }
 972                 }
 973                 //an exact match couldn't be found, so there was no match.
 974                 lastState[0] = -1;
 975                 return null;
 976             }
 977 
 978             //xstate is some value != 0
 979 
 980             //determine from which index to start looking. If lastState[0] is -1
 981             //then we know to start from the end of the state array. Otherwise,
 982             //we start at the lastIndex - 1.
 983             int lastStateIndex = lastState == null || lastState[0] == -1 ?
 984                 states.length : lastState[0];
 985 
 986             for (int counter = lastStateIndex - 1; counter >= 0; counter--) {
 987                 int oState = states[counter].state;
 988 
 989                 if (oState == 0) {
 990                     if (wildIndex == -1) {
 991                         wildIndex = counter;
 992                     }
 993                 } else if ((xstate & oState) == oState) {
 994                     // This is key, we need to make sure all bits of the
 995                     // StateInfo match, otherwise a StateInfo with
 996                     // SELECTED | ENABLED would match ENABLED, which we
 997                     // don't want.
 998 
 999                     // This comes from BigInteger.bitCnt
1000                     int bitCount = oState;
1001                     bitCount -= (0xaaaaaaaa & bitCount) >>> 1;
1002                     bitCount = (bitCount & 0x33333333) + ((bitCount >>> 2) &
1003                             0x33333333);
1004                     bitCount = bitCount + (bitCount >>> 4) & 0x0f0f0f0f;
1005                     bitCount += bitCount >>> 8;
1006                     bitCount += bitCount >>> 16;
1007                     bitCount = bitCount & 0xff;
1008                     if (bitCount > bestCount) {
1009                         bestIndex = counter;
1010                         bestCount = bitCount;
1011                     }
1012                 }
1013             }
1014             if (bestIndex != -1) {
1015                 lastState[0] = bestIndex;
1016                 return states[bestIndex];
1017             }
1018             if (wildIndex != -1) {
1019                 lastState[0] = wildIndex;
1020                 return states[wildIndex];
1021             }
1022         }
1023         lastState[0] = -1;
1024         return null;
1025     }
1026 
1027     /**
1028      * Contains values such as the UIDefaults and painters associated with
1029      * a state. Whereas <code>State</code> represents a distinct state that a
1030      * component can be in (such as Enabled), this class represents the colors,
1031      * fonts, painters, etc associated with some state for this
1032      * style.
1033      */
1034     private final class RuntimeState implements Cloneable {
1035         int state;
1036         Painter<Object> backgroundPainter;
1037         Painter<Object> foregroundPainter;
1038         Painter<Object> borderPainter;
1039         String stateName;
1040         UIDefaults defaults = new UIDefaults(10, .7f);
1041 
1042         private RuntimeState(int state, String stateName) {
1043             this.state = state;
1044             this.stateName = stateName;
1045         }
1046 
1047         @Override
1048         public String toString() {
1049             return stateName;
1050         }
1051 
1052         @Override
1053         public RuntimeState clone() {
1054             RuntimeState clone = new RuntimeState(state, stateName);
1055             clone.backgroundPainter = backgroundPainter;
1056             clone.foregroundPainter = foregroundPainter;
1057             clone.borderPainter = borderPainter;
1058             clone.defaults.putAll(defaults);
1059             return clone;
1060         }
1061     }
1062 
1063     /**
1064      * Essentially a struct of data for a style. A default instance of this
1065      * class is used by NimbusStyle. Additional instances exist for each
1066      * component that has overrides.
1067      */
1068     private static final class Values {
1069         /**
1070          * The list of State types. A State represents a type of state, such
1071          * as Enabled, Default, WindowFocused, etc. These can be custom states.
1072          */
1073         State<?>[] stateTypes = null;
1074         /**
1075          * The list of actual runtime state representations. These can represent things such
1076          * as Enabled + Focused. Thus, they differ from States in that they contain
1077          * several states together, and have associated properties, data, etc.
1078          */
1079         RuntimeState[] states = null;
1080         /**
1081          * The content margins for this region.
1082          */
1083         Insets contentMargins;
1084         /**
1085          * Defaults on the region/component level.
1086          */
1087         UIDefaults defaults = new UIDefaults(10, .7f);
1088         /**
1089          * Simple cache. After a value has been looked up, it is stored
1090          * in this cache for later retrieval. The key is a concatenation of
1091          * the property being looked up, two dollar signs, and the extended
1092          * state. So for example:
1093          *
1094          * foo.bar$$2353
1095          */
1096         Map<CacheKey,Object> cache = new HashMap<CacheKey,Object>();
1097     }
1098 
1099     /**
1100      * This implementation presupposes that key is never null and that
1101      * the two keys being checked for equality are never null
1102      */
1103     private static final class CacheKey {
1104         private String key;
1105         private int xstate;
1106 
1107         CacheKey(Object key, int xstate) {
1108             init(key, xstate);
1109         }
1110 
1111         void init(Object key, int xstate) {
1112             this.key = key.toString();
1113             this.xstate = xstate;
1114         }
1115 
1116         @Override
1117         public boolean equals(Object obj) {
1118             final CacheKey other = (CacheKey) obj;
1119             if (obj == null) return false;
1120             if (this.xstate != other.xstate) return false;
1121             if (!this.key.equals(other.key)) return false;
1122             return true;
1123         }
1124 
1125         @Override
1126         public int hashCode() {
1127             int hash = 3;
1128             hash = 29 * hash + this.key.hashCode();
1129             hash = 29 * hash + this.xstate;
1130             return hash;
1131         }
1132     }
1133 }