Package org.jvnet.substance

Source Code of org.jvnet.substance.SkinDerivedLookAndFeel

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

import java.awt.*;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;

import javax.swing.*;
import javax.swing.plaf.IconUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;

import org.jvnet.lafplugin.*;
import org.jvnet.lafwidget.LafWidgetRepository;
import org.jvnet.lafwidget.animation.FadeConfigurationManager;
import org.jvnet.lafwidget.animation.FadeKind;
import org.jvnet.substance.api.*;
import org.jvnet.substance.api.SubstanceConstants.MenuGutterFillKind;
import org.jvnet.substance.api.SubstanceConstants.SubstanceWidgetType;
import org.jvnet.substance.api.combo.ComboPopupPrototypeCallback;
import org.jvnet.substance.api.tabbed.BaseTabCloseListener;
import org.jvnet.substance.api.tabbed.TabCloseCallback;
import org.jvnet.substance.fonts.*;
import org.jvnet.substance.painter.decoration.DecorationAreaType;
import org.jvnet.substance.painter.utils.DecorationPainterUtils;
import org.jvnet.substance.plugin.SubstanceSkinPlugin;
import org.jvnet.substance.shaper.*;
import org.jvnet.substance.skin.SkinChangeListener;
import org.jvnet.substance.skin.SkinInfo;
import org.jvnet.substance.utils.*;

import contrib.com.jgoodies.looks.common.ShadowPopupFactory;

/**
* <p>
* Main class for <b>Substance </b> look and feel. <b>All</b> static methods in
* this class should be called when Substance is the currently set look and feel
* unless explicitly stated otherwise.
* </p>
*
* <p>
* Since version 5.0 this class is abstract. There are three options to use
* Substance:
* </p>
*
* <ul>
* <li>Use one of the core skin-based look-and-feels in the
* <code>org.jvnet.substance.skin</code> package.</li>
* <li>Extend this class and pass a skin instance to the
* {@link SubstanceLookAndFeel#SubstanceLookAndFeel(SubstanceSkin)} constructor.
* </li>
* <li>Call {@link SubstanceLookAndFeel#setSkin(String)} or
* {@link SubstanceLookAndFeel#setSkin(SubstanceSkin)} static methods. These
* methods do not require Substance to be the current look-and-feel.</li>
* </ul>
*
* @author Kirill Grouchnikov
*/
public abstract class SubstanceLookAndFeel extends BasicLookAndFeel {
  /**
   * The name of plugin configuration XML resource name. This is used for the
   * <a href="https://laf-plugin.dev.java.net">laf-plugin</a> support layer of
   * third-party components.
   */
  public static final String PLUGIN_XML = "META-INF/substance-plugin.xml";

  /**
   * Plugin manager for component plugins.
   */
  private static ComponentPluginManager componentPlugins;

  /**
   * Plugin manager for skin plugins.
   */
  private static PluginManager skinPlugins;

  /**
   * List of all listeners on skin changes.
   */
  protected final static Set<SkinChangeListener> skinChangeListeners = new HashSet<SkinChangeListener>();

  /**
   * List of all listeners on closing tabs.
   */
  protected final static Set<LocaleChangeListener> localeChangeListeners = new HashSet<LocaleChangeListener>();

  /**
   * Indicates whether option dialogs (error, question, warning, info) should
   * use constant color schemes for icon coloring. Note that since version
   * 4.0, the default setting is <code>true</code> (use constant color
   * scheme). To use color scheme-consistent coloring, call
   * {@link #setToUseConstantThemesOnDialogs(boolean)} and pass
   * <code>false</code>.
   *
   * @see #isToUseConstantThemesOnDialogs()
   * @see #setToUseConstantThemesOnDialogs(boolean)
   */
  private static boolean toUseConstantThemesOnDialogs = true;

  /**
   * Change listener on keyboard focus manager - fix for defect 208.
   */
  protected PropertyChangeListener focusOwnerChangeListener;

  /**
   * The current keyboard focus manager - fix for defect 208.
   */
  protected KeyboardFocusManager currentKeyboardFocusManager;

  /**
   * Fade kind for smart tree scroll. Disabled by default, use
   * {@link FadeConfigurationManager#allowFades(FadeKind)} to enable. </p>
   *
   * <p>
   * Smart tree scroll is relevant for scroll panes containing a tree. When
   * enabled, it automatically scrolls the tree horizontally when the viewport
   * shows mainly empty area (especially relevant for multi-level trees with
   * narrow viewports).
   * </p>
   *
   * @since 4.0
   */
  @SubstanceApi
  public final static FadeKind TREE_SMART_SCROLL_ANIMATION_KIND = new FadeKind(
      "substancelaf.treeSmartScrollAnimationKind", false);

  /**
   * Client property name for requesting that watermark should be painted on
   * the component and its descendants. This property can be set either as
   * client property on some component or as global property on
   * {@link UIManager}. The value should be either {@link Boolean#TRUE} or
   * {@link Boolean#FALSE}.
   *
   * <p>
   * In order to compute whether the current watermark should be painted on a
   * given component, its hierarchy is traversed bottom up. The first
   * component that has this property set defines the watermark visibility. If
   * neither component nor its ancestors define this property, the global
   * setting on {@link UIManager} is checked. If there is no global setting,
   * the watermark is <b>not</b> ignored (it is painted).
   * </p>
   *
   * <p>
   * There is special default setting for trees, tables, lists and text
   * components. These show watermark only when this property is explicitly
   * set to {@link Boolean#TRUE} on the component itself, one of its ancestors
   * or the {@link UIManager}.
   * </p>
   *
   * @since version 5.0
   */
  @SubstanceApi
  public static final String WATERMARK_VISIBLE = "substancelaf.watermark.visible";

  /**
   * Client property name for ignoring the default (minimum) dimension for a
   * single button. This property can be set either on the specific button or
   * as a global setting on {@link UIManager}. The value should be either
   * {@link Boolean#TRUE} or {@link Boolean#FALSE}.
   * <p>
   * Note that {@link SubstanceButtonShaper} implementations are not required
   * to respect this property. The current implementations of the default
   * {@link StandardButtonShaper} and {@link ClassicButtonShaper} respect this
   * property.
   * </p>
   *
   * <p>
   * Example of marking a button to ignore minimum dimension settings:
   * </p>
   * <code>
   * JButton button = new JButton("text");<br>
   * button.putClientProperty(SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);
   * </code>
   * <p>
   * Example of marking all application buttons to ignore minimum dimension
   * settings:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.BUTTON_NO_MIN_SIZE_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);
   * </code>
   *
   * @since version 2.1
   */
  @SubstanceApi
  public static final String BUTTON_NO_MIN_SIZE_PROPERTY = "substancelaf.buttonnominsize";

  /**
   * Client property name for specifying that a single button / all
   * application buttons should not paint the background. This property can be
   * set either on the specific button or as a global setting on
   * {@link UIManager}. The value should be either {@link Boolean#TRUE} or
   * {@link Boolean#FALSE}. Note that unlike the {@link #FLAT_PROPERTY}, a
   * button marked with this property will <b>never</b> show the background
   * (will always be painted flat).
   *
   * <p>
   * Example of marking a button to never paint background:
   * </p>
   * <code>
   * JButton button = new JButton("text");<br>
   * button.putClientProperty(SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);
   * </code>
   *
   * <p>
   * Example of marking all application buttons to never paint background:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);
   * </code>
   *
   * @since version 2.3
   * @see #FLAT_PROPERTY
   */
  @SubstanceApi
  public static final String BUTTON_PAINT_NEVER_PROPERTY = "substancelaf.buttonpaintnever";

  /**
   * Client property name for specifying a straight side for a single button.
   * This property must be set on the specific button. The value can be:
   *
   * <p>
   * <ul>
   * <li>A value in {@link SubstanceConstants.Side} enum.
   * <li>Set of values in {@link SubstanceConstants.Side} enum.
   * </ul>
   *
   * <p>
   * Note that not the {@link SubstanceButtonShaper} implementations are
   * required to respect this property. The default
   * {@link StandardButtonShaper} and {@link ClassicButtonShaper} respect this
   * property.
   * </p>
   *
   * <p>
   * Example of marking a button to have straight north side:
   * </p>
   * <code>
   * JButton button = new JButton("text");<br>
   * button.putClientProperty(SubstanceLookAndFeel.BUTTON_SIDE_PROPERTY,<br>
   * &nbsp;&nbsp;SubstanceConstants.Side.RIGHT);
   * </code>
   *
   * @since version 2.1
   * @see #BUTTON_OPEN_SIDE_PROPERTY
   */
  @SubstanceApi
  public static final String BUTTON_SIDE_PROPERTY = "substancelaf.buttonside";

  /**
   * Client property name for specifying an open side for a single button.
   * This property must be set on the specific button. The value can be:
   *
   * <p>
   * <ul>
   * <li>A value in {@link SubstanceConstants.Side} enum.
   * <li>Set of values in {@link SubstanceConstants.Side} enum.
   * </ul>
   * </p>
   * <p>
   * Example of marking a button to have open top and west sides:
   * </p>
   * <code>
   * JButton button = new JButton("text");<br>
   * Set<Side> openSides = EnumSet.of(Side.TOP, Side.WEST);<br>
   * button.putClientProperty(SubstanceLookAndFeel.BUTTON_OPEN_SIDE_PROPERTY, <br>
   * &nbsp;&nbsp;openSides);
   * </code>
   *
   * @since version 3.1
   * @see #BUTTON_SIDE_PROPERTY
   */
  @SubstanceApi
  public static final String BUTTON_OPEN_SIDE_PROPERTY = "substancelaf.buttonopenSide";

  /**
   * Client property name for specifying the corner radius for buttons.
   * Currently, this property is respected only on toolbar buttons. This
   * property can be set on the specific toolbar button, on the specific
   * toolbar (will hold for all buttons in the toolbar) or as a global setting
   * on {@link UIManager}. The value should be a positive {@link Float}.
   *
   * <p>
   * Example of specifying a (toolbar) button to have corner radius of 5
   * pixels:
   * </p>
   * <code>
   * JButton button = new JButton("text");<br>
   * button.putClientProperty(SubstanceLookAndFeel.CORNER_RADIUS, <br>
   * &nbsp;&nbsp;Float.valueOf(5.0f));
   * </code>
   *
   * <p>
   * Example of specifying all buttons of a toolbar to have corner radius of 3
   * pixels:
   * </p>
   * <code>
   * JToolBar toolbar = new JToolBar("toolbar");<br>
   * toolbar.putClientProperty(SubstanceLookAndFeel.CORNER_RADIUS, <br>
   * &nbsp;&nbsp;Float.valueOf(3.0f));
   * </code>
   *
   * <p>
   * Example of specifying all toolbar buttons to have corner radius of 0
   * pixels:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.CORNER_RADIUS, Float.valueOf(0.0f));
   * </code>
   *
   * @since version 3.0
   */
  @SubstanceApi
  public static final String CORNER_RADIUS = "substancelaf.cornerRadius";

  /**
   * Property name for specifying that the component should be painted flat
   * (no background / border) when it's inactive. This property should be
   * specified on a specific component or its parent and must have either
   * {@link Boolean#TRUE} or {@link Boolean#FALSE} value.
   *
   * <p>
   * Example how to mark a button to appear flat:
   * </p>
   *
   * <code>
   * JButton button = new JButton("text");<br>
   * button.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);
   * </code>
   *
   * @since version 3.0
   * @see #BUTTON_PAINT_NEVER_PROPERTY
   */
  @SubstanceApi
  public static final String FLAT_PROPERTY = "substancelaf.componentFlat";

  /**
   * VM property name for specifying the heap status trace file. The trace
   * file will contain information on the status of heap. The property value
   * is used as a filename for tracing the heap status. Example for specifying
   * the trace file name:
   *
   * <p>
   * <code>
   * -Dsubstancelaf.heapStatusTraceFile=C:/temp/myApp.heap.log
   * </code>
   * </p>
   *
   * @since version 5.0
   */
  @SubstanceApi
  public static final String HEAP_STATUS_TRACE_FILE = "substancelaf.heapStatusTraceFile";

  /**
   * Client property name for specifying that contents of a frame, dialog,
   * internal frame, desktop icon or tab have been modified and not saved. The
   * property can be set on:
   * <p>
   * <ul>
   * <li>{@link JInternalFrame} - the <b>close</b> button of the specific
   * internal frame will be animated (in case that the internal frame has
   * decorated title pane). In addition, once such internal frame is iconified
   * (to a {@link JInternalFrame.JDesktopIcon}), the close button of the its
   * desktop icon is animated as well.</li>
   * <li>{@link JRootPane} - the <b>close</b> button of the title pane of the
   * matching frame / dialog will be animated (in case that the frame / dialog
   * have decorated title pane).</li>
   * <li>{@link JComponent} in a {@link JTabbedPane}. Based on the
   * {@link #TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION} property presence,
   * either the entire tab or its close button area is animated. In this case,
   * this property must be set on the tab component itself, <b>not</b> on one
   * of its child components.</li>
   * </ul>
   * </p>
   * <p>
   * The animation cycles between red, orange and yellow color schemes. In
   * most cases (all but tabs not marked with
   * {@link #TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION} property), the
   * animation will be visible only when the mouse hovers over the close
   * button of the matching container (frame, dialog, internal frame, desktop
   * icon, tab). The tooltip of the close button is changed as well to reflect
   * that the container contents are marked as modified.
   * </p>
   *
   * <p>
   * Here is a sample text editing application that illustrates the use of
   * this property. Once the contents of the text pane are changed, the frame
   * is marked as modified. The <b>Save</b> button marks the frame as
   * not-modified. In the real application, the listener on this button will
   * need to persist the changes as well.
   * </p>
   *
   * <code>
   * public class Changer extends JFrame {<br>
   * &nbsp;&nbsp;public Changer() {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;super("Changer");<br>
   * <br>
   * &nbsp;&nbsp;&nbsp;&nbsp;this.setLayout(new BorderLayout());<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;JTextPane textArea = new JTextPane();<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;this.add(textArea, BorderLayout.CENTER);<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;textArea.getDocument().addDocumentListener(new
   * DocumentListener() {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;private void handleChange() {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getRootPane().putClientProperty(<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SubstanceLookAndFeel.WINDOW_MODIFIED,
   * Boolean.TRUE);<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * <br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void
   * changedUpdate(DocumentEvent e) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handleChange();<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * <br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void
   * insertUpdate(DocumentEvent e) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handleChange();<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * <br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void
   * removeUpdate(DocumentEvent e) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;handleChange();<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;});<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;JPanel buttons = new JPanel(new
   * FlowLayout(FlowLayout.RIGHT));<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;JButton saveButton = new JButton("Save");<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;saveButton.addActionListener(new ActionListener() {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void
   * actionPerformed(ActionEvent e) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;getRootPane().putClientProperty(<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SubstanceLookAndFeel.WINDOW_MODIFIED,
   * Boolean.FALSE);<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;});<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;buttons.add(saveButton);<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;this.add(buttons, BorderLayout.SOUTH);<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br>
   * &nbsp;&nbsp;}<br>
   * <br>
   * &nbsp;&nbsp;public static void main(String ... args) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;try {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;UIManager.setLookAndFeel(new
   * SubstanceLookAndFeel());<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;catch (Exception exc) {}<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;JFrame.setDefaultLookAndFeelDecorated(true);<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;Changer ch = new Changer();<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;ch.setPreferredSize(new Dimension(200, 200));<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;ch.setSize(ch.getPreferredSize());<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;ch.setLocationRelativeTo(null);<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;ch.setVisible(true);<br>
   * &nbsp;&nbsp;}<br> }
   * </code>
   *
   * @since version 2.1
   */
  @SubstanceApi
  public final static String WINDOW_MODIFIED = "windowModified";

  /**
   * Client property name for adding close buttons on tabs. This property can
   * be specified on a single tab component, on a {@link JTabbedPane} itself
   * (will hold for all tab components that don't define this property) or on
   * {@link UIManager}. The value should be either {@link Boolean#TRUE} or
   * {@link Boolean#FALSE}. By default, the close buttons are not displayed.
   *
   * <p>
   * Example of setting that all tabs in the application will have close
   * buttons:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);
   * </code>
   *
   * <p>
   * A more complex example:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);<br>
   * JTabbedPane jtpMain = new JTabbedPane();<br>
   * JTabbedPane jtpSecondary = new JTabbedPane();<br>
   * jtpSecondary.putClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.FALSE);<br>
   * JPanel panelSecondary = new JPanel();<br>
   * jtpMain.addTab(jtpSecondary);<br>
   * jtpMain.addTab(panelSecondary);<br>
   * JPanel tab1 = new JPanel();<br>
   * JPanel tab2 = new JPanel();<br>
   * tab2.putClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY, <br>
   * &nbsp;&nbsp;Boolean.TRUE);<br>
   * jtpSecondary.addTab(tab1);<br>
   * jtpSecondary.addTab(tab2);
   * </code>
   *
   * <p>
   * In the example above, the first first-level child (<b>jtpSecondary</b>)
   * doesn't have the close button (since it overrides the global setting).
   * The second first-level child tab (<b>panelSecondary</b>) has close button
   * (from the global <b>UIManager</b> setting). The first second-level tab
   * doesn't have the close button (setting inherited from the parent
   * <b>jtpSecondary</b> tab that overrides the global setting). The second
   * second-level tab has the close button (since its setting overrides the
   * parent setting).
   * </p>
   *
   * @since version 2.1
   * @see #TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION
   * @see #TABBED_PANE_CLOSE_CALLBACK
   */
  @SubstanceApi
  public final static String TABBED_PANE_CLOSE_BUTTONS_PROPERTY = "substancelaf.tabbedpanehasclosebuttons";

  /**
   * Client property name for specifying that only the close button of a
   * marked-as-modified tab component should pulsate. This property can be
   * specified on a single tab component, on a {@link JTabbedPane} itself
   * (will hold for all tab components that don't define this property) or on
   * {@link UIManager}. The value should be either {@link Boolean#TRUE} or
   * {@link Boolean#FALSE}. By default, the animation on modified tabs is on
   * the entire tab rectangle. Note that this setting is only relevant for
   * tabs marked with {@link #WINDOW_MODIFIED} property.
   *
   * <p>
   * Example of setting that all tabs in the application will have modified
   * animation on close button:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION, <br>
   * &nbsp;&nbsp;Boolean.TRUE);
   * </code>
   *
   * <p>
   * A more complex example:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION, <br>
   * &nbsp;&nbsp;Boolean.TRUE);<br>
   * JTabbedPane jtpMain = new JTabbedPane();<br>
   * JTabbedPane jtpSecondary = new JTabbedPane();<br>
   * jtpSecondary.putClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION, <br>
   * &nbsp;&nbsp;Boolean.FALSE);<br>
   * JPanel panelSecondary = new JPanel();<br>
   * jtpMain.addTab(jtpSecondary);<br>
   * jtpMain.addTab(panelSecondary);<br>
   * JPanel tab1 = new JPanel();<br>
   * JPanel tab2 = new JPanel();<br>
   * tab2.putClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION, <br>
   * &nbsp;&nbsp;Boolean.TRUE);<br>
   * jtpSecondary.addTab(tab1);<br>
   * jtpSecondary.addTab(tab2);
   * </code>
   *
   * <p>
   * In the example above, the first first-level child (<b>jtpSecondary</b>)
   * has the animation on the entire tab (since it overrides the global
   * setting). The second first-level child tab (<b>panelSecondary</b>) has
   * animation on the close button (from the global <b>UIManager</b> setting).
   * The first second-level tab has the animation on the entire tab (setting
   * inherited from the parent <b>jtpSecondary</b> tab that overrides the
   * global setting). The second second-level tab has animation on the close
   * button (since its setting overrides the parent setting).
   * </p>
   *
   * @since version 2.2
   * @see #TABBED_PANE_CLOSE_BUTTONS_PROPERTY
   * @see #TABBED_PANE_CLOSE_CALLBACK
   */
  @SubstanceApi
  public final static String TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION = "substancelaf.tabbedpaneclosebuttonsmodifiedanimation";

  /**
   * Client property name for specifying the callback for deciding on the tab
   * close type. This property can be specified on a single tab component, on
   * a {@link JTabbedPane} itself (will hold for all tab components that don't
   * define this property) or on {@link UIManager}. The value should be an
   * instance of {@link TabCloseCallback}. Note that this setting is only
   * relevant for tabs marked with {@link #TABBED_PANE_CLOSE_BUTTONS_PROPERTY}
   * property.
   *
   * <p>
   * Example of custom tab close callback set on a tabbed pane:
   * </p>
   * <code>
   * TabCloseCallback closeCallback = new TabCloseCallback() {<br>
   * &nbsp;&nbsp;public TabCloseKind onAreaClick(JTabbedPane tabbedPane,<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int tabIndex, MouseEvent mouseEvent) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;if (mouseEvent.getButton() != MouseEvent.BUTTON3)<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return TabCloseKind.NONE;<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;if (mouseEvent.isShiftDown()) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return TabCloseKind.ALL;<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;return TabCloseKind.THIS;<br>
   * &nbsp;&nbsp;}<br>
   * <br>
   * &nbsp;&nbsp;public TabCloseKind onCloseButtonClick(JTabbedPane tabbedPane,<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int tabIndex, MouseEvent mouseEvent) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;if (mouseEvent.isAltDown()) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return TabCloseKind.ALL_BUT_THIS;<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;if (mouseEvent.isShiftDown()) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return TabCloseKind.ALL;<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;}<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;return TabCloseKind.THIS;<br>
   * &nbsp;&nbsp;}<br>
   * <br>
   * &nbsp;&nbsp;public String getAreaTooltip(JTabbedPane tabbedPane, int tabIndex) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;return null;<br>
   * &nbsp;&nbsp;}<br>
   * <br>
   * &nbsp;&nbsp;public String getCloseButtonTooltip(JTabbedPane tabbedPane,<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int tabIndex) {<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;StringBuffer result = new StringBuffer();<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;result.append("&lt;html&gt;&lt;body&gt;");<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;result.append("Mouse click closes &lt;b&gt;"<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ tabbedPane.getTitleAt(tabIndex) + "&lt;/b&gt; tab");<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;result.append("&lt;br&gt;&lt;b&gt;Alt&lt;/b&gt;-Mouse click closes all tabs but &lt;b&gt;"<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;+ tabbedPane.getTitleAt(tabIndex) + "&lt;/b&gt; tab");<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;result.append("&lt;br&gt;&lt;b&gt;Shift&lt;/b&gt;-Mouse click closes all tabs");<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;result.append("&lt;/body&gt;&lt;/html&gt;");<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;return result.toString();<br>
   * &nbsp;&nbsp;}<br>
   * };<br>
   *
   * JTabbedPane jtp = new JTabbedPane();<br>
   * jtp.putClientProperty(<br>
   * &nbsp;&nbsp;&nbsp;&nbsp;SubstanceLookAndFeel.TABBED_PANE_CLOSE_CALLBACK, <br>
   * &nbsp;&nbsp;&nbsp;&nbsp;closeCallback);
   * </code>
   *
   * @since version 2.3
   */
  @SubstanceApi
  public final static String TABBED_PANE_CLOSE_CALLBACK = "substancelaf.tabbedpanecloseCallback";

  /**
   * Client property name for specifying the content pane border kind. This
   * property can be specified either on a single {@link JTabbedPane} or on
   * {@link UIManager}. The value should be one of
   * {@link SubstanceConstants.TabContentPaneBorderKind} enum. By default, the
   * border kind is
   * {@link SubstanceConstants.TabContentPaneBorderKind#DOUBLE_FULL}.
   *
   * <p>
   * Example of setting that all tabbed panes in the application have single
   * full border (default setting prior to version 4.1):
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, <br>
   * &nbsp;&nbsp;TabContentPaneBorderKind.SINGLE_FULL);
   * </code>
   *
   * <p>
   * Example of specifying that the specific tabbed pane has single full
   * border (default setting prior to version 4.1):
   * </p>
   * <code>
   * JTabbedPane jtpMain = new JTabbedPane();<br>
   * jtpMain.putClientProperty(SubstanceLookAndFeel.TABBED_PANE_CONTENT_BORDER_KIND, <br>
   * &nbsp;&nbsp;TabContentPaneBorderKind.SINGLE_FULL);
   * </code>
   *
   * @since version 4.1
   */
  @SubstanceApi
  public final static String TABBED_PANE_CONTENT_BORDER_KIND = "substancelaf.tabbedPaneContentBorderKind";

  /**
   * Client property name for specifying combo popup flyout orientation. This
   * property can be set on either a specific {@link JComboBox} or globally on
   * {@link UIManager}. The value should be one of the {@link Integer}s below:
   *
   * <p>
   * <ul>
   * <li>The default {@link SwingConstants#SOUTH} - the popup is displayed
   * directly below the combo aligned to the left.
   * <li>{@link SwingConstants#NORTH} - the popup is displayed directly above
   * the combo aligned to the left.
   * <li>{@link SwingConstants#EAST} - the popup is displayed to the left of
   * the combo aligned to the top.
   * <li>{@link SwingConstants#WEST} - the popup is displayed to the right of
   * the combo aligned to the top.
   * <li>{@link SwingConstants#CENTER} - the popup is displayed centered
   * vertically over the combo aligned to the left.
   * </ul>
   * </p>
   *
   * <p>
   * Note that the combo arrow changes in accordance with the combo popup
   * flyout orientation. Example of setting a combobox with a custom flyout
   * orientation:
   * </p>
   * <code>
   * JComboBox cb = new JComboBox(<br>
   * &nbsp;&nbsp;new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" });<br>
   * cb.putClientProperty(SubstanceLookAndFeel.COMBO_BOX_POPUP_FLYOUT_ORIENTATION, <br>
   * &nbsp;&nbsp;SwingConstants.CENTER);
   * </code>
   *
   * @since version 2.3
   * @see #COMBO_POPUP_PROTOTYPE
   */
  public final static String COMBO_BOX_POPUP_FLYOUT_ORIENTATION = "substancelaf.comboboxpopupFlyoutOrientation";

  /**
   * Client property name for specifying scroll pane button policy. This
   * property can be set on either a specific {@link JScrollPane} or globally
   * on {@link UIManager}. The value should be one of the
   * {@link SubstanceConstants.ScrollPaneButtonPolicyKind} enum. Example of
   * setting a scroll pane with a custom button policy:
   *
   * <p>
   * <code>
   * JScrollPane jsp = new JScrollPane(new JPanel());<br>
   * jsp.putClientProperty(SubstanceLookAndFeel.SCROLL_PANE_BUTTONS_POLICY,<br>
   * &nbsp;&nbsp;ScrollPaneButtonPolicyKind.MULTIPLE);
   * </code>
   *
   * @since version 3.1
   */
  @SubstanceApi
  public final static String SCROLL_PANE_BUTTONS_POLICY = "substancelaf.scrollPaneButtonsPolicy";

  /**
   * Property name for specifying that extra UI elements (such as menu items
   * in system menu or lock borders) should be shown. This property can be set
   * as a global setting on {@link UIManager} or as a client property on a
   * specific component. The value should be either {@link Boolean#TRUE} or
   * {@link Boolean#FALSE}.
   *
   * <p>
   * Example of setting this property on {@link UIManager}:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.SHOW_EXTRA_WIDGETS, Boolean.TRUE);
   * SwingUtilities.updateComponentTree(myFrame);
   * </code>
   *
   * @since version 5.0
   */
  @SubstanceApi
  public final static String SHOW_EXTRA_WIDGETS = "substancelaf.addWidgets";

  /**
   * Property name for specifying menu gutter fill kind. Menu gutter is the
   * part of the menu where checkmarks and icons are painted. The value should
   * be one of {@link MenuGutterFillKind} enum. This property can be set
   * globally on the {@link UIManager}. The default value is
   * {@link MenuGutterFillKind#HARD}.
   *
   * <p>
   * Example of setting soft fill kind:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.MENU_GUTTER_FILL_KIND, MenuGutterFillKind.SOFT);
   * </code>
   *
   * @since version 3.2
   */
  public final static String MENU_GUTTER_FILL_KIND = "substancelaf.menuGutterFillKind";

  /**
   * Client property name for specifying the kind of focus indication on
   * buttons, check boxes and radio buttons. The value should be one of
   * {@link SubstanceConstants.FocusKind} enum. This property can be set
   * either on the specific component or as global property on
   * {@link UIManager}.
   *
   * <p>
   * In order to compute the kind of focus indication for some component, the
   * component's hierarchy is traversed bottom up. The first component that
   * has this property set, defines the focus indication kind. If neither
   * component nor its ancestors define this property, the global setting on
   * {@link UIManager} is checked. If there is no global setting, the default
   * {@link SubstanceConstants.FocusKind#ALL_INNER} is used. Here is an
   * example to illustrate the above:
   * </p>
   *
   * <code>
   * &nbsp;&nbsp;JPanel topPanel = new JPanel();<br>
   * &nbsp;&nbsp;topPanel.putClientProperty(SubstanceLookAndFeel.FOCUS_KIND, FocusKind.UNDERLINE);<br>
   * &nbsp;&nbsp;JPanel panel1 = new JPanel();<br>
   * &nbsp;&nbsp;JButton b1 = new JButton("button1");<br>
   * &nbsp;&nbsp;b1.putClientProperty(SubstanceLookAndFeel.FOCUS_KIND, FocusKind.TEXT);<br>
   * &nbsp;&nbsp;JButton b2 = new JButton("button2");<br>
   * &nbsp;&nbsp;JButton b3 = new JButton("button3");<br>
   * &nbsp;&nbsp;b3.putClientProperty(SubstanceLookAndFeel.FOCUS_KIND, FocusKind.ALL_INNER);<br>
   * &nbsp;&nbsp;panel1.add(b1);<br>
   * &nbsp;&nbsp;panel1.add(b2);<br>
   * &nbsp;&nbsp;topPanel.add(panel1);<br>
   * &nbsp;&nbsp;topPanel.add(b3);<br>
   * </code>
   *
   * <p>
   * In the code above:
   * </p>
   * <ul>
   * <li>Button <b>b1</b> will have {@link SubstanceConstants.FocusKind#NONE}
   * focus kind which is set directly on the button.
   * <li>Button <b>b2</b> will have
   * {@link SubstanceConstants.FocusKind#UNDERLINE} focus kind which is
   * inherited from its <b>topPanel</b> parent.
   * <li>Button <b>b3</b> will have
   * {@link SubstanceConstants.FocusKind#ALL_INNER} focus kind which is set
   * directly on the button.
   * </ul>
   *
   * @since 2.2
   * @see SubstanceConstants.FocusKind
   */
  public final static String FOCUS_KIND = "substancelaf.focusKind";

  /**
   * Property name for specifying the combobox popup prototype display value
   * which is used to compute the width of the popup at runtime. The property
   * value should be one of:
   *
   * <p>
   * <ul>
   * <li>{@link ComboPopupPrototypeCallback} - will provide
   * application-specific logic at runtime.
   * <li>{@link Object} - will point to the prototype entry itself.
   * </ul>
   * </p>
   *
   * <p>
   * This property can be set either on a specific {@link JComboBox} or
   * globally on {@link UIManager}.
   * </p>
   *
   * <p>
   * Here is an example of combo popup prototype set to a model element:
   * </p>
   * <code>
   *   JComboBox comboProto1 = new JComboBox(new Object[] { "aa", "aaaaa",<br>
   *   &nbsp;&nbsp;"aaaaaaaaaa", "this one is the one", "aaaaaaaaaaaaaaaaaaaaa" });<br>
   *   comboProto1.setPrototypeDisplayValue("aaaaa");<br>
   *   comboProto1.putClientProperty(SubstanceLookAndFeel.COMBO_POPUP_PROTOTYPE,<br>
   *   &nbsp;&nbsp;"this one is the one");
   * </code>
   *
   * <p>
   * Here is an example of combo popup prototype set to a dynamic callback.
   * This callback always returns the last model element:
   * </p>
   * <code>
   *  JComboBox comboProto3 = new JComboBox(new Object[] { "aa", "aaaaa",<br>
   *   &nbsp;&nbsp;"this is not", "this one is not it",<br>
   *   &nbsp;&nbsp;"this one is it that is for the popup" });<br>
   *   comboProto3.setPrototypeDisplayValue("aaaaa");<br>
   *   comboProto3.putClientProperty(SubstanceLookAndFeel.COMBO_POPUP_PROTOTYPE,<br>
   *   &nbsp;&nbsp;new ComboPopupPrototypeCallback() {<br>
   *   &nbsp;&nbsp;&nbsp;&nbsp;public Object getPopupPrototypeDisplayValue(JComboBox jc) {<br>
   *   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return jc.getModel().getElementAt(<br>
   *   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jc.getModel().getSize() - 1);<br>
   *   &nbsp;&nbsp;&nbsp;&nbsp;}<br>
   *   &nbsp;&nbsp;});
   *  </code>
   *
   * @since version 3.0
   * @see #COMBO_BOX_POPUP_FLYOUT_ORIENTATION
   */
  @SubstanceApi
  public final static String COMBO_POPUP_PROTOTYPE = "substancelaf.comboPopupPrototype";

  /**
   * VM property name for specifying the trace file. The trace file will
   * contain output of the memory analyser which can be used to pinpoint the
   * memory leaks. The property value is used as a filename for tracing the
   * memory allocations. Example for specifying the trace file name:
   *
   * <p>
   * <code>
   * -Dsubstancelaf.traceFile=C:/temp/myApp.substance.log
   * </code>
   * </p>
   *
   * @since version 2.0
   */
  @SubstanceApi
  public final static String TRACE_FILE = "substancelaf.traceFile";

  /**
   * Client property name for specifying the number of echo characters for
   * each password character. The value should be an instance of
   * {@link Integer}, otherwise will be ignored. This property can be set
   * either on a specific {@link JPasswordField} or globally on
   * {@link UIManager}.
   *
   * <p>
   * Example of having all password fields echo 3 characters per each typed
   * user character:
   * </p>
   * <code>
   * UIManager.put(SubstanceLookAndFeel.PASSWORD_ECHO_PER_CHAR, <br>
   * &nbsp;&nbsp;new Integer(3));
   * </code>
   *
   * <p>
   * Example of having a specific password field echo 2 characters per each
   * typed user character:
   * </p>
   * <code>
   * JPasswordField jpf = new JPasswordField();<br>
   * jpf.putClientProperty(SubstanceLookAndFeel.PASSWORD_ECHO_PER_CHAR, <br>
   * &nbsp;&nbsp;new Integer(2));
   * </code>
   *
   * @since version 2.2
   */
  @SubstanceApi
  public final static String PASSWORD_ECHO_PER_CHAR = "substancelaf.passwordEchoPerChar";

  /**
   * <p>
   * Client property name for specifying that icons on controls sch as
   * buttons, toggle buttons, labels, tabs and menu items should match the
   * color of the current color scheme when they are in default state. The
   * control is in default state when it's not pressed, not selected, not
   * armed and not rolled over. The value should be an instance of
   * {@link Boolean}. By default, all controls show regular (full-color
   * original) icons. The value can be set globally on {@link UIManager}.
   * </p>
   *
   * @since version 3.3
   */
  @SubstanceApi
  public final static String USE_THEMED_DEFAULT_ICONS = "substancelaf.useThemedDefaultIcons";

  /**
   * <p>
   * Client property name for specifying the colorization amount applied to
   * the background and foreground of the current color scheme and the
   * specific control. By default, when the application does not use any
   * custom colors, all the controls are painted with the colors of the
   * current color scheme / skin. The colors coming from the look-and-feel
   * implement the marker {@link UIResource} interface which allows the UI
   * delegates to differentiate between application-specific colors which are
   * not changed, and the LAF-provide colors that are changed on LAF switch.
   * </p>
   *
   * <p>
   * This new client property installs the "smart colorization" mode which
   * uses the colors of the current color scheme and the custom background /
   * foreground colors (when installed by application) to colorize the
   * relevant portions of the control. For example, on checkbox the custom
   * background color will be used to colorize the check box itself, while the
   * custom foreground color will be applied to the check box text and the
   * check mark.
   * </p>
   *
   * <p>
   * The value of this property specifies the actual colorization amount.
   * Value of 0.0 results in Substance completely <strong>ignoring</strong>
   * the custom application background and foreground colors set on the
   * components - no colorization. Values closer to 1.0 result in almost full
   * usage of the custom application background and foreground colors set on
   * the components. Note that in order to maintain the gradients (fill,
   * border, etc), even value of 1.0 does not result in full custom color
   * being applied to the relevant visuals of the control.
   * </p>
   *
   * <p>
   * This property can be specified globally on {@link UIManager}, applying on
   * all controls, or on the specific component / container. In the later
   * case, the value will be applied to the component / container itself and
   * all its children that do not specify a custom value for this property.
   * </p>
   *
   * <p>
   * The default colorization amount (when this property is not set at all) is
   * 0.5. This means that applications that install custom background /
   * foreground colors on their UI controls will see them colorized with 50%
   * "strength", even without setting this property.
   * </p>
   *
   * <p>
   * The value should be an instance of {@link Double} in 0.0-1.0 range.
   * </p>
   *
   * <p>
   * Example of marking a button to have a custom background color and
   * colorizing it with 40%:
   * </p>
   * <code>
   * JButton jb = new JButton("sample", myIcon);<br>
   * jb.setBackground(Color.red);<br>
   * jb.putClientProperty(SubstanceLookAndFeel.COLORIZATION_FACTOR, <br>
   * &nbsp;&nbsp;new Double(0.4));
   * </code>
   *
   * <p>
   * Note that components in decoration areas registered on the current skin
   * will ignore the colorization on custom background color. The background
   * of such components is always painted by the skin's decoration painter to
   * ensure consistent background painting of the relevant decoration area.
   * </p>
   *
   * @since version 4.2
   * @see Component#setBackground(Color)
   * @see Component#setForeground(Color)
   */
  @SubstanceApi
  public final static String COLORIZATION_FACTOR = "substancelaf.colorizationFactor";

  /**
   * Internal client property name for storing application-specific font
   * policy.
   *
   * @since version 3.3
   * @see #setFontPolicy(FontPolicy)
   * @see #getFontPolicy()
   */
  protected final static String SUBSTANCE_FONT_POLICY_KEY = "substancelaf.fontPolicyKey";

  /**
   * Property name for specifying outline shaper. This property is used a
   * client property that can be set on a specific control.
   *
   * <p>
   * The value must be a {@link SubstanceButtonShaper} object.
   * </p>
   *
   * <p>
   * Example of using a {@link SubstanceButtonShaper} object as client
   * property value:
   * </p>
   * <code>
   * JButton b = new JButton("text");<br>
   * b.putClientProperty(SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY, <br>
   * &nbsp;&nbsp;new ClassicButtonShaper());
   * </code>
   *
   * @since version 2.1
   */
  @SubstanceApi
  public static final String BUTTON_SHAPER_PROPERTY = "substancelaf.buttonShaper";

  /**
   * Property name for specifying a skin to be used on the specific root pane.
   * This property can only be installed on a {@link JRootPane} and will
   * affect all the controls in that root pane. The value must be an instance
   * of {@link SubstanceSkin}. After setting this property, call
   * {@link SwingUtilities#updateComponentTreeUI(Component)} on the matching
   * window.
   *
   * @since version 5.0
   * @see #getCurrentSkin(Component)
   */
  @SubstanceApi
  public static final String SKIN_PROPERTY = "substancelaf.skin";

  /**
   * Resource bundle for <b>Substance</b> labels.
   */
  private static ResourceBundle LABEL_BUNDLE = null;

  /**
   * Class loader for {@link #LABEL_BUNDLE}.
   */
  private static ClassLoader labelBundleClassLoader;

  /**
   * The skin of this look-and-feel instance.
   */
  protected SubstanceSkin skin;

  /**
   * The name of this look-and-feel instance.
   */
  protected String name;

  /**
   * Creates a new skin-based Substance look-and-feel. This is the only way to
   * create an instance of {@link SubstanceLookAndFeel} class.
   *
   * @param skin
   *            Skin.
   */
  protected SubstanceLookAndFeel(SubstanceSkin skin) {
    this.skin = skin;
    this.name = "Substance " + skin.getDisplayName();

    initPluginsIfNecessary();
  }

  /**
   * Initializes the plugins if necessary.
   */
  protected static void initPluginsIfNecessary() {
    if (SubstanceLookAndFeel.skinPlugins != null)
      return;
    SubstanceLookAndFeel.skinPlugins = new PluginManager(
        SubstanceLookAndFeel.PLUGIN_XML, LafPlugin.TAG_MAIN,
        SubstanceSkinPlugin.TAG_SKIN_PLUGIN_CLASS);
    SubstanceLookAndFeel.componentPlugins = new ComponentPluginManager(
        SubstanceLookAndFeel.PLUGIN_XML);
  }

  /**
   * Retrieves the current label bundle.
   *
   * @return The current label bundle.
   * @see #resetLabelBundle()
   */
  public static synchronized ResourceBundle getLabelBundle() {
    if (SubstanceLookAndFeel.LABEL_BUNDLE == null) {
      // fix for RFE 157 (allowing custom class loader for
      // resource bundles which can remove server calls
      // in applets)
      if (SubstanceLookAndFeel.labelBundleClassLoader == null) {
        SubstanceLookAndFeel.LABEL_BUNDLE = ResourceBundle.getBundle(
            "org.jvnet.substance.resources.Labels", Locale
                .getDefault());
      } else {
        SubstanceLookAndFeel.LABEL_BUNDLE = ResourceBundle.getBundle(
            "org.jvnet.substance.resources.Labels", Locale
                .getDefault(),
            SubstanceLookAndFeel.labelBundleClassLoader);
      }
      for (LocaleChangeListener lcl : SubstanceLookAndFeel.localeChangeListeners)
        lcl.localeChanged();
    }
    return SubstanceLookAndFeel.LABEL_BUNDLE;
  }

  /**
   * Retrieves the label bundle for the specified locale.
   *
   * @param locale
   *            Locale.
   * @return The label bundle for the specified locale.
   */
  public static synchronized ResourceBundle getLabelBundle(Locale locale) {
    // fix for RFE 157 (allowing custom class loader for
    // resource bundles which can remove server calls
    // in applets)
    if (SubstanceLookAndFeel.labelBundleClassLoader == null) {
      return ResourceBundle.getBundle(
          "org.jvnet.substance.resources.Labels", locale);
    } else {
      return ResourceBundle.getBundle(
          "org.jvnet.substance.resources.Labels", locale,
          SubstanceLookAndFeel.labelBundleClassLoader);
    }
  }

  /**
   * Resets the current label bundle. Useful when the application changes
   * Locale at runtime.
   *
   * @see #getLabelBundle()
   */
  public static synchronized void resetLabelBundle() {
    SubstanceLookAndFeel.LABEL_BUNDLE = null;
    LafWidgetRepository.resetLabelBundle();
  }

  /**
   * Returns the current global skin. If the current look-and-feel is not
   * Substance, this method returns <code>null</code>. This method is for
   * internal use only. Applications should use the
   * {@link #getCurrentSkin(Component)}.
   *
   * @return Current global skin.
   * @see #getCurrentSkin(Component)
   */
  public static SubstanceSkin getCurrentSkin() {
    LookAndFeel current = UIManager.getLookAndFeel();
    if (current instanceof SubstanceLookAndFeel) {
      return currentSkin;
    }
    return null;
  }

  /**
   * Returns the current skin for the specified component. If the current
   * look-and-feel is not Substance, this method returns <code>null</code>.
   *
   * @param c
   *            Component. May be <code>null</code> - in this case the global
   *            current Substance skin will be returned.
   * @return Current skin for the specified component.
   * @see #SKIN_PROPERTY
   * @see #getCurrentSkin()
   */
  @SubstanceApi
  public static SubstanceSkin getCurrentSkin(Component c) {
    return SubstanceCoreUtilities.getSkin(c);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.LookAndFeel#getDescription()
   */
  @Override
  public String getDescription() {
    return "Substance Look and Feel by Kirill Grouchnikov";
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.LookAndFeel#getID()
   */
  @Override
  public String getID() {
    return this.name;
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.LookAndFeel#getName()
   */
  @Override
  public String getName() {
    return this.name;
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.LookAndFeel#isNativeLookAndFeel()
   */
  @Override
  public boolean isNativeLookAndFeel() {
    return false;
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.LookAndFeel#isSupportedLookAndFeel()
   */
  @Override
  public boolean isSupportedLookAndFeel() {
    return true;
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * javax.swing.plaf.basic.BasicLookAndFeel#initClassDefaults(javax.swing
   * .UIDefaults)
   */
  @Override
  protected void initClassDefaults(UIDefaults table) {
    super.initClassDefaults(table);

    String UI_CLASSNAME_PREFIX = "org.jvnet.substance.Substance";
    Object[] uiDefaults = {

    "ButtonUI", UI_CLASSNAME_PREFIX + "ButtonUI",

    "CheckBoxUI", UI_CLASSNAME_PREFIX + "CheckBoxUI",

    "ComboBoxUI", UI_CLASSNAME_PREFIX + "ComboBoxUI",

    "CheckBoxMenuItemUI", UI_CLASSNAME_PREFIX + "CheckBoxMenuItemUI",

    "DesktopIconUI", UI_CLASSNAME_PREFIX + "DesktopIconUI",

    "DesktopPaneUI", UI_CLASSNAME_PREFIX + "DesktopPaneUI",

    "EditorPaneUI",
        UI_CLASSNAME_PREFIX + "EditorPaneUI",

        "FileChooserUI",
        UI_CLASSNAME_PREFIX + "FileChooserUI",

        // "FileChooserUI", "javax.swing.plaf.metal.MetalFileChooserUI",

        "FormattedTextFieldUI",
        UI_CLASSNAME_PREFIX + "FormattedTextFieldUI",

        "InternalFrameUI", UI_CLASSNAME_PREFIX + "InternalFrameUI",

        "LabelUI", UI_CLASSNAME_PREFIX + "LabelUI",

        "ListUI", UI_CLASSNAME_PREFIX + "ListUI",

        "MenuUI", UI_CLASSNAME_PREFIX + "MenuUI",

        "MenuBarUI", UI_CLASSNAME_PREFIX + "MenuBarUI",

        "MenuItemUI", UI_CLASSNAME_PREFIX + "MenuItemUI",

        "OptionPaneUI", UI_CLASSNAME_PREFIX + "OptionPaneUI",

        "PanelUI", UI_CLASSNAME_PREFIX + "PanelUI",

        "PasswordFieldUI", UI_CLASSNAME_PREFIX + "PasswordFieldUI",

        "PopupMenuUI", UI_CLASSNAME_PREFIX + "PopupMenuUI",

        "PopupMenuSeparatorUI",
        UI_CLASSNAME_PREFIX + "PopupMenuSeparatorUI",

        "ProgressBarUI", UI_CLASSNAME_PREFIX + "ProgressBarUI",

        "RadioButtonUI", UI_CLASSNAME_PREFIX + "RadioButtonUI",

        "RadioButtonMenuItemUI",
        UI_CLASSNAME_PREFIX + "RadioButtonMenuItemUI",

        "RootPaneUI", UI_CLASSNAME_PREFIX + "RootPaneUI",

        "ScrollBarUI", UI_CLASSNAME_PREFIX + "ScrollBarUI",

        "ScrollPaneUI", UI_CLASSNAME_PREFIX + "ScrollPaneUI",

        "SeparatorUI", UI_CLASSNAME_PREFIX + "SeparatorUI",

        "SliderUI", UI_CLASSNAME_PREFIX + "SliderUI",

        "SpinnerUI", UI_CLASSNAME_PREFIX + "SpinnerUI",

        "SplitPaneUI", UI_CLASSNAME_PREFIX + "SplitPaneUI",

        "TabbedPaneUI", UI_CLASSNAME_PREFIX + "TabbedPaneUI",

        "TableUI", UI_CLASSNAME_PREFIX + "TableUI",

        "TableHeaderUI", UI_CLASSNAME_PREFIX + "TableHeaderUI",

        "TextAreaUI", UI_CLASSNAME_PREFIX + "TextAreaUI",

        "TextFieldUI", UI_CLASSNAME_PREFIX + "TextFieldUI",

        "TextPaneUI", UI_CLASSNAME_PREFIX + "TextPaneUI",

        "ToggleButtonUI", UI_CLASSNAME_PREFIX + "ToggleButtonUI",

        "ToolBarUI", UI_CLASSNAME_PREFIX + "ToolBarUI",

        "ToolBarSeparatorUI",
        UI_CLASSNAME_PREFIX + "ToolBarSeparatorUI",

        "ToolTipUI", UI_CLASSNAME_PREFIX + "ToolTipUI",

        "TreeUI", UI_CLASSNAME_PREFIX + "TreeUI",

    };
    table.putDefaults(uiDefaults);
  }

  /*
   * (non-Javadoc)
   *
   * @see
   * javax.swing.plaf.basic.BasicLookAndFeel#initComponentDefaults(javax.swing
   * .UIDefaults)
   */
  @Override
  protected void initComponentDefaults(UIDefaults table) {
    super.initComponentDefaults(table);

    initFontDefaults(table);
    this.skin.addCustomEntriesToTable(table);
  }

  /**
   * Sets the {@link FontPolicy} to be used with Substance family. If the
   * specified policy is <code>null</code>, the default will be reset. This
   * method does not require Substance to be the current look-and-feel, and
   * will cause Substance to be set as the current application look-and-feel.
   *
   * @param fontPolicy
   *            The {@link FontPolicy} to be used with Substance family, or
   *            <code>null</code> to reset to the default
   *
   * @see #getFontPolicy()
   * @see SubstanceLookAndFeel#SUBSTANCE_FONT_POLICY_KEY
   */
  @SubstanceApi
  public static void setFontPolicy(FontPolicy fontPolicy) {
    UIManager.put(SUBSTANCE_FONT_POLICY_KEY, fontPolicy);
    SubstanceSizeUtils.setControlFontSize(-1);
    SubstanceSizeUtils.resetPointsToPixelsRatio(fontPolicy);
    SubstanceLookAndFeel.setSkin(SubstanceLookAndFeel.getCurrentSkin());
  }

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

    // return default policy
    return SubstanceFontUtilities.getDefaultFontPolicy();
  }

  /**
   * Looks up the correct control font and sets it for all controls.
   *
   * @param table
   *            The UI defaults table.
   */
  private void initFontDefaults(UIDefaults table) {
    FontSet substanceFontSet = getFontPolicy()
        .getFontSet("Substance", null);
    initFontDefaults(table, substanceFontSet);
  }

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

    // System.out.println("Control: " + fontSet.getControlFont());
    // System.out.println("Menu: " + fontSet.getMenuFont());
    // System.out.println("Message: " + fontSet.getMessageFont());
    // System.out.println("Small: " + fontSet.getSmallFont());
    // System.out.println("Title: " + fontSet.getTitleFont());
    // System.out.println("Window title: " + fontSet.getWindowTitleFont());

    Object[] defaults = {

    "Button.font", controlFont,

    "CheckBox.font", controlFont,

    "ColorChooser.font", controlFont,

    "ComboBox.font", controlFont,

    "EditorPane.font", controlFont,

    "FormattedTextField.font", controlFont,

    "Label.font", controlFont,

    "List.font", controlFont,

    "Panel.font", controlFont,

    "PasswordField.font", controlFont,

    "ProgressBar.font", controlFont,

    "RadioButton.font", controlFont,

    "ScrollPane.font", controlFont,

    "Spinner.font", controlFont,

    "TabbedPane.font", controlFont,

    "Table.font", controlFont,

    "TableHeader.font", controlFont,

    "TextArea.font", controlFont,

    "TextField.font", controlFont,

    "TextPane.font", controlFont,

    "ToolBar.font", controlFont,

    "ToggleButton.font", controlFont,

    "Tree.font", controlFont,

    "Viewport.font", controlFont,

    "InternalFrame.titleFont", windowFont,

    "DesktopIcon.titleFont", windowFont,

    "OptionPane.font", messageFont,

    "OptionPane.messageFont", messageFont,

    "OptionPane.buttonFont", messageFont,

    "TitledBorder.font", titleFont,

    "ToolTip.font", toolTipFont,

    "CheckBoxMenuItem.font", menuFont,

    "CheckBoxMenuItem.acceleratorFont", menuFont,

    "Menu.font", menuFont,

    "Menu.acceleratorFont", menuFont,

    "MenuBar.font", menuFont,

    "MenuItem.font", menuFont,

    "MenuItem.acceleratorFont", menuFont,

    "PopupMenu.font", menuFont,

    "RadioButtonMenuItem.font", menuFont,

    "RadioButtonMenuItem.acceleratorFont", menuFont,
    // ?
    };
    table.putDefaults(defaults);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.plaf.basic.BasicLookAndFeel#getDefaults()
   */
  @Override
  public UIDefaults getDefaults() {
    UIDefaults table = super.getDefaults();

    SubstanceLookAndFeel.componentPlugins.processAllDefaultsEntries(table,
        this.skin);
    return table;
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.plaf.basic.BasicLookAndFeel#initialize()
   */
  @Override
  public void initialize() {
    super.initialize();
    ShadowPopupFactory.install();

    setSkin(this.skin, false);

    // tracer for memory analysis
    String paramTraceFile = SubstanceCoreUtilities
        .getVmParameter(SubstanceLookAndFeel.TRACE_FILE);
    if (paramTraceFile != null) {
      MemoryAnalyzer.commence(1000, paramTraceFile);
      for (Object plugin : SubstanceLookAndFeel.componentPlugins
          .getAvailablePlugins(true))
        MemoryAnalyzer.enqueueUsage("Has plugin '"
            + plugin.getClass().getName() + "'");
    }

    // to show heap status panel in title pane?
    String heapStatusPanelParam = SubstanceCoreUtilities
        .getVmParameter(SubstanceLookAndFeel.HEAP_STATUS_TRACE_FILE);
    SubstanceTitlePane.setHeapStatusLogfileName(heapStatusPanelParam);

    // initialize component plugins
    SubstanceLookAndFeel.componentPlugins.initializeAll();

    // initialize widget support
    LafWidgetRepository.getRepository().setLafSupport(
        new SubstanceWidgetSupport());

    // fix for defect 208 - tracking changes to focus owner
    // and repainting the default button
    this.focusOwnerChangeListener = new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        if ("focusOwner".equals(evt.getPropertyName())) {
          Component newFocusOwner = (Component) evt.getNewValue();
          if (newFocusOwner != null) {
            JRootPane rootPane = SwingUtilities
                .getRootPane(newFocusOwner);
            if (rootPane == null)
              return;
            JButton defaultButton = rootPane.getDefaultButton();
            if (defaultButton == null)
              return;
            defaultButton.repaint();
          }
        }
        if ("managingFocus".equals(evt.getPropertyName())) {
          if (Boolean.FALSE.equals(evt.getNewValue())) {
            // new keyboard focus manager has been installed
            currentKeyboardFocusManager
                .removePropertyChangeListener(focusOwnerChangeListener);
            currentKeyboardFocusManager = KeyboardFocusManager
                .getCurrentKeyboardFocusManager();
            currentKeyboardFocusManager
                .addPropertyChangeListener(focusOwnerChangeListener);
          }
        }
      }
    };
    this.currentKeyboardFocusManager = KeyboardFocusManager
        .getCurrentKeyboardFocusManager();
    this.currentKeyboardFocusManager
        .addPropertyChangeListener(this.focusOwnerChangeListener);
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.plaf.basic.BasicLookAndFeel#uninitialize()
   */
  @Override
  public void uninitialize() {
    super.uninitialize();

    SubstanceLookAndFeel.currentSkin = null;

    ShadowPopupFactory.uninstall();

    SubstanceCoreUtilities.stopThreads();

    // fix for defect 109 - memory leak on watermarks
    if (this.skin.getWatermark() != null)
      this.skin.getWatermark().dispose();

    // uninitialize component plugins
    SubstanceLookAndFeel.componentPlugins.uninitializeAll();

    // reset widget support
    LafWidgetRepository.getRepository().unsetLafSupport();

    // clear caches
    LazyResettableHashMap.reset();

    this.currentKeyboardFocusManager
        .removePropertyChangeListener(this.focusOwnerChangeListener);
    this.focusOwnerChangeListener = null;
    this.currentKeyboardFocusManager = null;
  }

  /**
   * Registers a new listener on skin change.
   *
   * @param skinChangeListener
   *            New listener on skin change.
   * @see #setSkin(String)
   * @see #setSkin(SubstanceSkin)
   * @see #unregisterSkinChangeListener(SkinChangeListener)
   */
  @SubstanceApi
  public static void registerSkinChangeListener(
      SkinChangeListener skinChangeListener) {
    SubstanceLookAndFeel.skinChangeListeners.add(skinChangeListener);
  }

  /**
   * Unregisters a listener on skin change.
   *
   * @param skinChangeListener
   *            The listener to unregister.
   * @see #setSkin(String)
   * @see #setSkin(SubstanceSkin)
   * @see #registerSkinChangeListener(SkinChangeListener)
   */
  @SubstanceApi
  public static void unregisterSkinChangeListener(
      SkinChangeListener skinChangeListener) {
    SubstanceLookAndFeel.skinChangeListeners.remove(skinChangeListener);
  }

  /**
   * Registers the specified listener on tab-close events on <b>all</b> tabbed
   * panes.
   *
   * @param tabCloseListener
   *            Listener to register.
   * @see #registerTabCloseChangeListener(JTabbedPane, BaseTabCloseListener)
   * @see #unregisterTabCloseChangeListener(BaseTabCloseListener)
   * @see #unregisterTabCloseChangeListener(JTabbedPane, BaseTabCloseListener)
   */
  @SubstanceApi
  public static void registerTabCloseChangeListener(
      BaseTabCloseListener tabCloseListener) {
    TabCloseListenerManager.getInstance()
        .registerListener(tabCloseListener);
  }

  /**
   * Registers the specified listener on tab-close events on <b>the
   * specified</b> tabbed pane.
   *
   * @param tabbedPane
   *            Tabbed pane. If <code>null</code>, the tab close listener is
   *            registered globally (for all tabbed panes).
   * @param tabCloseListener
   *            Listener to register.
   * @see #registerTabCloseChangeListener(BaseTabCloseListener)
   * @see #unregisterTabCloseChangeListener(BaseTabCloseListener)
   * @see #unregisterTabCloseChangeListener(JTabbedPane, BaseTabCloseListener)
   */
  @SubstanceApi
  public static void registerTabCloseChangeListener(JTabbedPane tabbedPane,
      BaseTabCloseListener tabCloseListener) {
    TabCloseListenerManager.getInstance().registerListener(tabbedPane,
        tabCloseListener);
  }

  /**
   * Unregisters the specified listener on tab-close events on <b>all</b>
   * tabbed panes.
   *
   * @param tabCloseListener
   *            Listener to unregister.
   * @see #registerTabCloseChangeListener(BaseTabCloseListener)
   * @see #registerTabCloseChangeListener(JTabbedPane, BaseTabCloseListener)
   * @see #unregisterTabCloseChangeListener(JTabbedPane, BaseTabCloseListener)
   */
  @SubstanceApi
  public static void unregisterTabCloseChangeListener(
      BaseTabCloseListener tabCloseListener) {
    TabCloseListenerManager.getInstance().unregisterListener(
        tabCloseListener);
  }

  /**
   * Unregisters the specified listener on tab-close events on <b>the
   * specified</b> tabbed pane.
   *
   * @param tabbedPane
   *            Tabbed pane. If <code>null</code>, the tab close listener is
   *            unregistered globally (for all tabbed panes).
   * @param tabCloseListener
   *            Listener to unregister.
   * @see #registerTabCloseChangeListener(BaseTabCloseListener)
   * @see #registerTabCloseChangeListener(JTabbedPane, BaseTabCloseListener)
   * @see #unregisterTabCloseChangeListener(BaseTabCloseListener)
   */
  @SubstanceApi
  public static void unregisterTabCloseChangeListener(JTabbedPane tabbedPane,
      BaseTabCloseListener tabCloseListener) {
    TabCloseListenerManager.getInstance().unregisterListener(tabbedPane,
        tabCloseListener);
  }

  /**
   * Returns the set of all listeners registered on tab-close events on
   * <b>all</b> tabbed panes.
   *
   * @return Set of all listeners registered on tab-close events on <b>all</b>
   *         tabbed panes.
   */
  @SubstanceApi
  public static Set<BaseTabCloseListener> getAllTabCloseListeners() {
    return TabCloseListenerManager.getInstance().getListeners();
  }

  /**
   * Returns all listeners registered on tab closing of the specified tabbed
   * pane.
   *
   * @param tabbedPane
   *            A tabbed pane. If <code>null</code>, all globally registered
   *            tab close listeners are returned.
   * @return All listeners registered on tab closing of the specified tabbed
   *         pane.
   */
  @SubstanceApi
  public static Set<BaseTabCloseListener> getAllTabCloseListeners(
      JTabbedPane tabbedPane) {
    return TabCloseListenerManager.getInstance().getListeners(tabbedPane);
  }

  /**
   * Registers a new listener on locale change.
   *
   * @param localeListener
   *            New listener on locale change.
   */
  @SubstanceApi
  public static void registerLocaleChangeListener(
      LocaleChangeListener localeListener) {
    SubstanceLookAndFeel.localeChangeListeners.add(localeListener);
  }

  /**
   * Unregisters a listener on locale change.
   *
   * @param localeListener
   *            The listener to unregister.
   */
  @SubstanceApi
  public static void unregisterLocaleChangeListener(
      LocaleChangeListener localeListener) {
    SubstanceLookAndFeel.localeChangeListeners.remove(localeListener);
  }

  /**
   * Returns all listeners registered on locale change.
   *
   * @return All listeners registered on locale change.
   */
  @SubstanceApi
  public static Set<LocaleChangeListener> getLocaleListeners() {
    return Collections
        .unmodifiableSet(SubstanceLookAndFeel.localeChangeListeners);
  }

  /**
   * Sets the visibility of the specified widget kind(s). If the first
   * <code>rootPane</code> parameter is <code>null</code>, this call applies
   * to all root panes.
   *
   * @param rootPane
   *            Root pane. May be <code>null</code>.
   * @param visible
   *            Visibility indication.
   * @param substanceWidgets
   *            Widget types.
   * @since version 5.0
   */
  @SubstanceApi
  public static void setWidgetVisible(JRootPane rootPane, boolean visible,
      SubstanceWidgetType... substanceWidgets) {
    SubstanceWidgetManager.getInstance().register(rootPane, visible,
        substanceWidgets);
    if (rootPane != null) {
      SwingUtilities.updateComponentTreeUI(rootPane);
    } else {
      for (Window window : Window.getWindows()) {
        JRootPane root = SwingUtilities.getRootPane(window);
        SwingUtilities.updateComponentTreeUI(root);
      }
    }
  }

  /**
   * Checks whether the <code>JOptionPane</code>s created with predefined
   * message types should use constant color schemes for the icons.
   *
   * @return <code>true</code> if the <code>JOptionPane</code>s created with
   *         predefined message types should use constant color schemes for
   *         the icons, <code>false</code> otherwise.
   * @see #setToUseConstantThemesOnDialogs(boolean)
   */
  @SubstanceApi
  public static boolean isToUseConstantThemesOnDialogs() {
    return SubstanceLookAndFeel.toUseConstantThemesOnDialogs;
  }

  /**
   * Sets the new setting for the icons of the <code>JOptionPane</code>s
   * created with predefined message types.
   *
   * @param toUseConstantThemesOnDialogs
   *            if <code>true</code>, the <code>JOptionPane</code>s created
   *            with predefined message types should use constant color
   *            schemes for the icons.
   * @see #isToUseConstantThemesOnDialogs()
   */
  @SubstanceApi
  public static void setToUseConstantThemesOnDialogs(
      boolean toUseConstantThemesOnDialogs) {
    SubstanceLookAndFeel.toUseConstantThemesOnDialogs = toUseConstantThemesOnDialogs;
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        for (Window window : Window.getWindows()) {
          SwingUtilities.updateComponentTreeUI(window);
        }
      }
    });
  }

  /**
   * The current Substance skin.
   */
  private static SubstanceSkin currentSkin = null;

  /**
   * Sets the specified skin. If the current look-and-feel is not Substance,
   * this method will create a new Substance look-and-feel based on the
   * specified skin and set it on {@link UIManager}. This method does not
   * require Substance to be the current look-and-feel.
   *
   * @param newSkin
   *            Skin to set.
   * @param toUpdateWindows
   *            if <code>true</code>, the
   *            {@link SwingUtilities#updateComponentTreeUI(Component)} is
   *            called on all windows returned by {@link Window#getWindows()}
   *            API.
   * @return <code>true</code> if the specified skin has been set
   *         successfully, <code>false</code> otherwise.
   * @see #setSkin(SubstanceSkin)
   */
  @SubstanceApi
  private static boolean setSkin(SubstanceSkin newSkin,
      boolean toUpdateWindows) {
    if (!SwingUtilities.isEventDispatchThread()) {
      throw new IllegalStateException(
          "This method must be called on the Event Dispatch Thread");
    }

    if (!newSkin.isValid())
      return false;

    boolean isSubstance = (UIManager.getLookAndFeel() instanceof SubstanceLookAndFeel);
    if (!isSubstance) {
      class SkinDerivedLookAndFeel extends SubstanceLookAndFeel {
        public SkinDerivedLookAndFeel(SubstanceSkin newSkin) {
          super(newSkin);
        }
      }

      LookAndFeel derived = new SkinDerivedLookAndFeel(newSkin);
      try {
        UIManager.setLookAndFeel(derived);
      } catch (UnsupportedLookAndFeelException ulafe) {
        return false;
      }
      if (!(UIManager.getLookAndFeel() instanceof SubstanceLookAndFeel)) {
        return false;
      }
      for (Window window : Window.getWindows()) {
        SwingUtilities.updateComponentTreeUI(window);
      }
      return true;
    }

    try {
      // Required skin settings must be non-null
      if (!newSkin.isValid()) {
        return false;
      }

      // fix for defect 109 - memory leak on watermark switch
      if ((currentSkin != null) && (currentSkin.getWatermark() != null)) {
        currentSkin.getWatermark().dispose();
      }
      if (newSkin.getWatermark() != null) {
        if (!newSkin.getWatermark().updateWatermarkImage(newSkin)) {
          return false;
        }
      }

      UIDefaults defaults = UIManager.getLookAndFeelDefaults();
      // The table will be null when the skin is set using a custom
      // LAF
      if (defaults != null) {
        initFontDefaults(UIManager.getLookAndFeelDefaults(),
            SubstanceLookAndFeel.getFontPolicy().getFontSet(
                "Substance", null));
        newSkin.addCustomEntriesToTable(UIManager
            .getLookAndFeelDefaults());
        SubstanceLookAndFeel.componentPlugins
            .processAllDefaultsEntries(UIManager
                .getLookAndFeelDefaults(), newSkin);
      }

      if (isSubstance)
        LazyResettableHashMap.reset();

      currentSkin = newSkin;

      if (toUpdateWindows) {
        for (Window window : Window.getWindows()) {
          SwingUtilities.updateComponentTreeUI(window);
        }
      }

      // SwingUtilities.invokeLater(new Runnable() {
      // public void run() {
      for (SkinChangeListener skinChangeListener : SubstanceLookAndFeel.skinChangeListeners)
        skinChangeListener.skinChanged();
      // }
      // });
      return true;
    } catch (NoClassDefFoundError ncdfe) {
      // this may happen when a skin references some class
      // that can't be found in the classpath.
      return false;
    } catch (Exception e) {
      return false;
    }
  }

  /**
   * Sets the specified skin. If the current look-and-feel is not Substance,
   * this method will create a new Substance look-and-feel based on the
   * specified skin and set it on {@link UIManager}. This method does not
   * require Substance to be the current look-and-feel. Calling this method
   * will call {@link SwingUtilities#updateComponentTreeUI(Component)} on all
   * open top-level windows.
   *
   * @param newSkin
   *            Skin to set.
   * @return <code>true</code> if the specified skin has been set
   *         successfully, <code>false</code> otherwise.
   * @throws IllegalStateException
   *             When called outside the Event Dispatch Thread.
   * @see #registerSkinChangeListener(SkinChangeListener)
   * @see #unregisterSkinChangeListener(SkinChangeListener)
   * @see SubstanceSkin#isValid()
   */
  public static boolean setSkin(SubstanceSkin newSkin) {
    return setSkin(newSkin, true);
  }

  /**
   * Sets the specified skin. If the current look-and-feel is not Substance,
   * this method will create a new Substance look-and-feel based on the
   * specified skin and set it on {@link UIManager}. This method does not
   * require Substance to be the current look-and-feel. Calling this method
   * will call {@link SwingUtilities#updateComponentTreeUI(Component)} on all
   * open top-level windows.
   *
   * @param skinClassName
   *            Skin to set.
   * @return <code>true</code> if the specified skin has been set
   *         successfully, <code>false</code> otherwise.
   * @throws IllegalStateException
   *             When called outside the Event Dispatch Thread.
   * @since version 3.1
   * @see #setSkin(SubstanceSkin)
   * @see #registerSkinChangeListener(SkinChangeListener)
   * @see #unregisterSkinChangeListener(SkinChangeListener)
   * @see SubstanceSkin#isValid()
   */
  @SubstanceApi
  public static boolean setSkin(String skinClassName) {
    try {
      Class<?> skinClass = Class.forName(skinClassName);
      if (skinClass == null) {
        return false;
      }
      Object obj = skinClass.newInstance();
      if (obj == null) {
        return false;
      }
      if (!(obj instanceof SubstanceSkin)) {
        return false;
      }
      return SubstanceLookAndFeel.setSkin((SubstanceSkin) obj);
    } catch (Exception exc) {
      exc.printStackTrace();
      return false;
    }
  }

  /**
   * Returns all available skins.
   *
   * @return All available skins. Key - skin display name, value - skin
   *         information.
   */
  @SubstanceApi
  public static Map<String, SkinInfo> getAllSkins() {
    initPluginsIfNecessary();
    Map<String, SkinInfo> result = new TreeMap<String, SkinInfo>();
    for (Object skinPlugin : SubstanceLookAndFeel.skinPlugins
        .getAvailablePlugins(true)) {
      for (SkinInfo skinInfo : ((SubstanceSkinPlugin) skinPlugin)
          .getSkins()) {
        result.put(skinInfo.getDisplayName(), skinInfo);
      }
    }
    return result;
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.LookAndFeel#getSupportsWindowDecorations()
   */
  @Override
  public boolean getSupportsWindowDecorations() {
    return true;
  }

  /**
   * Sets the class loader for {@link #LABEL_BUNDLE}.
   *
   * @param labelBundleClassLoader
   *            Class loader for {@link #LABEL_BUNDLE}.
   * @since version 3.1
   */
  public static void setLabelBundleClassLoader(
      ClassLoader labelBundleClassLoader) {
    SubstanceLookAndFeel.labelBundleClassLoader = labelBundleClassLoader;
    LafWidgetRepository.setLabelBundleClassLoader(labelBundleClassLoader);
  }

  /**
   * Returns the title pane of the specified top-level window.
   *
   * @param window
   *            Top-level window.
   * @return If the parameter is either {@link JFrame} or {@link JDialog} and
   *         has custom decorations, the result is the title pane,
   *         <code>null</code> otherwise.
   * @since version 3.1
   */
  @SubstanceApi
  public static JComponent getTitlePaneComponent(Window window) {
    JRootPane rootPane = null;
    if (window instanceof JFrame) {
      JFrame f = (JFrame) window;
      rootPane = f.getRootPane();
    }
    if (window instanceof JDialog) {
      JDialog d = (JDialog) window;
      rootPane = d.getRootPane();
    }
    if (rootPane != null) {
      SubstanceRootPaneUI ui = (SubstanceRootPaneUI) rootPane.getUI();
      return ui.getTitlePane();
    }
    return null;
  }

  /**
   * Sets the decoration type of the specified component and all its children.
   *
   * @param comp
   *            Component.
   * @param type
   *            Decoration type of the component and all its children.
   */
  @SubstanceApi
  public static void setDecorationType(JComponent comp,
      DecorationAreaType type) {
    DecorationPainterUtils.setDecorationType(comp, type);
  }

  // /**
  // * Sets the decoration type of the specified component.
  // *
  // * @param comp
  // * Component.
  // * @param type
  // * Decoration type of the component and its children if
  // * <code>isPropagatingToChildren</code> is <code>true</code>.
  // * @param isPropagatingToChildren
  // * If <code>true</code>, the decoration type will be applied to
  // * the component children.
  // */
  // @SubstanceApi
  // public static void setDecorationType(JComponent comp,
  // DecorationAreaType type, boolean isPropagatingToChildren) {
  // SubstanceDecorationUtilities.setDecorationType(comp, type,
  // isPropagatingToChildren);
  // }

  /**
   * Returns the decoration area type of the specified component. The
   * component and its ancestor hierarchy are scanned for the registered
   * decoration area type. The farthest ancestor that was passed to
   * {@link #setDecorationType(JComponent, DecorationAreaType, boolean)} with
   * the <code>isPropagatingToChildren</code> not set to <code>false</code>
   * (the second part is not checked for the component itself) defines the
   * result.
   *
   * @param comp
   *            Component.
   * @return Decoration area type of the component.
   */
  public static DecorationAreaType getDecorationType(Component comp) {
    return DecorationPainterUtils.getDecorationType(comp);
  }

  /**
   * Returns the immediate decoration area type of the specified component.
   * The component is checked for the registered decoration area type. If
   * {@link #setDecorationType(JComponent, DecorationAreaType, boolean)} was
   * not called on this component, this method returns <code>null</code>.
   *
   * @param comp
   *            Component.
   * @return Immediate decoration area type of the component.
   */
  public static DecorationAreaType getImmediateDecorationType(Component comp) {
    return DecorationPainterUtils.getImmediateDecorationType(comp);
  }

  /**
   * Checks whether Substance is the current look-and-feel. This method is for
   * internal use only.
   *
   * @return <code>true</code> if Substance is the current look-and-feel,
   *         <code>false</code> otherwise.
   */
  public static boolean isCurrentLookAndFeel() {
    return ((UIManager.getLookAndFeel() instanceof SubstanceLookAndFeel) && (currentSkin != null));
  }

  /*
   * (non-Javadoc)
   *
   * @see javax.swing.LookAndFeel#getDisabledIcon(javax.swing.JComponent,
   * javax.swing.Icon)
   */
  @Override
  public Icon getDisabledIcon(JComponent component, Icon icon) {
    if (icon == null)
      return null;
    SubstanceColorScheme colorScheme = SubstanceColorSchemeUtilities
        .getColorScheme(component, ComponentState.DISABLED_UNSELECTED);
    BufferedImage result = SubstanceImageCreator.getColorSchemeImage(
        component, icon, colorScheme, 0.5f);
    float alpha = SubstanceColorSchemeUtilities.getAlpha(component,
        ComponentState.DISABLED_UNSELECTED);
    if (alpha < 1.0f) {
      BufferedImage intermediate = SubstanceCoreUtilities.getBlankImage(
          result.getWidth(), result.getHeight());
      Graphics2D g2d = intermediate.createGraphics();
      g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
      g2d.drawImage(result, 0, 0, null);
      g2d.dispose();
      result = intermediate;
    }

    return new IconUIResource(new ImageIcon(result));
  }
}
TOP

Related Classes of org.jvnet.substance.SkinDerivedLookAndFeel

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.