/*
* Copyright (c) 2005-2016 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.pushingpixels.substance.api;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.KeyboardFocusManager;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeMap;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JPasswordField;
import javax.swing.JRootPane;
import javax.swing.JTabbedPane;
import javax.swing.LookAndFeel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicLookAndFeel;
import org.pushingpixels.lafplugin.ComponentPluginManager;
import org.pushingpixels.lafplugin.LafPlugin;
import org.pushingpixels.lafplugin.PluginManager;
import org.pushingpixels.lafwidget.LafWidgetRepository;
import org.pushingpixels.lafwidget.animation.AnimationConfigurationManager;
import org.pushingpixels.lafwidget.animation.AnimationFacet;
import org.pushingpixels.lafwidget.contrib.intellij.UIUtil;
import org.pushingpixels.lafwidget.icon.HiDpiAwareIconUiResource;
import org.pushingpixels.substance.api.SubstanceConstants.MenuGutterFillKind;
import org.pushingpixels.substance.api.SubstanceConstants.SubstanceWidgetType;
import org.pushingpixels.substance.api.combo.ComboPopupPrototypeCallback;
import org.pushingpixels.substance.api.fonts.FontPolicy;
import org.pushingpixels.substance.api.fonts.FontSet;
import org.pushingpixels.substance.api.fonts.SubstanceFontUtilities;
import org.pushingpixels.substance.api.inputmaps.InputMapSet;
import org.pushingpixels.substance.api.inputmaps.SubstanceInputMapUtilities;
import org.pushingpixels.substance.api.shaper.ClassicButtonShaper;
import org.pushingpixels.substance.api.shaper.StandardButtonShaper;
import org.pushingpixels.substance.api.shaper.SubstanceButtonShaper;
import org.pushingpixels.substance.api.skin.SkinChangeListener;
import org.pushingpixels.substance.api.skin.SkinInfo;
import org.pushingpixels.substance.api.tabbed.BaseTabCloseListener;
import org.pushingpixels.substance.api.tabbed.TabCloseCallback;
import org.pushingpixels.substance.internal.contrib.jgoodies.looks.common.ShadowPopupFactory;
import org.pushingpixels.substance.internal.fonts.FontPolicies;
import org.pushingpixels.substance.internal.painter.DecorationPainterUtils;
import org.pushingpixels.substance.internal.plugin.SubstanceSkinPlugin;
import org.pushingpixels.substance.internal.ui.SubstanceRootPaneUI;
import org.pushingpixels.substance.internal.utils.LazyResettableHashMap;
import org.pushingpixels.substance.internal.utils.LocaleChangeListener;
import org.pushingpixels.substance.internal.utils.MemoryAnalyzer;
import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceImageCreator;
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
import org.pushingpixels.substance.internal.utils.SubstanceTitlePane;
import org.pushingpixels.substance.internal.utils.SubstanceWidgetManager;
import org.pushingpixels.substance.internal.utils.SubstanceWidgetSupport;
import org.pushingpixels.substance.internal.utils.TabCloseListenerManager;
/**
* <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.pushingpixels.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://github.com/kirill-grouchnikov/laf-plugin">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 changing locales.
*/
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;
/**
* Smart tree scroll animation facet. Disabled by default, use
* {@link AnimationConfigurationManager#allowAnimations(AnimationFacet)} 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
*/
public final static AnimationFacet TREE_SMART_SCROLL_ANIMATION_KIND = new AnimationFacet(
"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
*/
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>
* 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>
* Boolean.TRUE);
* </code>
*
* @since version 2.1
*/
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 on the specific button, its parent 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>
* Boolean.TRUE);
* </code>
*
* <p>
* Example of marking all application buttons to never paint background:
* </p>
* <code>
* UIManager.put(SubstanceLookAndFeel.BUTTON_PAINT_NEVER_PROPERTY, <br>
* Boolean.TRUE);
* </code>
*
* @since version 2.3
* @see #FLAT_PROPERTY
*/
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 the {@link SubstanceButtonShaper} implementations are not
* 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>
* SubstanceConstants.Side.RIGHT);
* </code>
*
* @since version 2.1
* @see #BUTTON_OPEN_SIDE_PROPERTY
*/
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>
* openSides);
* </code>
*
* @since version 3.1
* @see #BUTTON_SIDE_PROPERTY
*/
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>
* 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>
* 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
*/
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>
* Boolean.TRUE);
* </code>
*
* @since version 3.0
* @see #BUTTON_PAINT_NEVER_PROPERTY
*/
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
*/
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 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). In case the root pane belongs to a
* {@link JInternalFrame} and that frame is iconified (to a
* {@link JInternalFrame.JDesktopIcon}), the close button of the its desktop
* icon is animated as well.</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>
* public Changer() {<br>
* super("Changer");<br>
* <br>
* this.setLayout(new BorderLayout());<br>
* JTextPane textArea = new JTextPane();<br>
* this.add(textArea, BorderLayout.CENTER);<br>
* textArea.getDocument().addDocumentListener(new
* DocumentListener() {<br>
* private void handleChange() {<br>
* getRootPane().putClientProperty(<br>
* SubstanceLookAndFeel.WINDOW_MODIFIED,
* Boolean.TRUE);<br>
* }<br>
* <br>
* public void
* changedUpdate(DocumentEvent e) {<br>
* handleChange();<br>
* }<br>
* <br>
* public void
* insertUpdate(DocumentEvent e) {<br>
* handleChange();<br>
* }<br>
* <br>
* public void
* removeUpdate(DocumentEvent e) {<br>
* handleChange();<br>
* }<br>
* });<br>
* <br>
* JPanel buttons = new JPanel(new
* FlowLayout(FlowLayout.RIGHT));<br>
* JButton saveButton = new JButton("Save");<br>
* saveButton.addActionListener(new ActionListener() {<br>
* public void
* actionPerformed(ActionEvent e) {<br>
* getRootPane().putClientProperty(<br>
* SubstanceLookAndFeel.WINDOW_MODIFIED,
* Boolean.FALSE);<br>
* }<br>
* });<br>
* <br>
* buttons.add(saveButton);<br>
* this.add(buttons, BorderLayout.SOUTH);<br>
* <br>
* this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);<br>
* }<br>
* <br>
* public static void main(String ... args) {<br>
* try {<br>
* UIManager.setLookAndFeel(new
* SubstanceLookAndFeel());<br>
* }<br>
* catch (Exception exc) {}<br>
* JFrame.setDefaultLookAndFeelDecorated(true);<br>
* Changer ch = new Changer();<br>
* ch.setPreferredSize(new Dimension(200, 200));<br>
* ch.setSize(ch.getPreferredSize());<br>
* ch.setLocationRelativeTo(null);<br>
* ch.setVisible(true);<br>
* }<br> }
* </code>
*
* @since version 2.1
*/
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>
* Boolean.TRUE);
* </code>
*
* <p>
* A more complex example:
* </p>
* <code>
* UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY, <br>
* Boolean.TRUE);<br>
* JTabbedPane jtpMain = new JTabbedPane();<br>
* JTabbedPane jtpSecondary = new JTabbedPane();<br>
* jtpSecondary.putClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_PROPERTY, <br>
* 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>
* 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
*/
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>
* Boolean.TRUE);
* </code>
*
* <p>
* A more complex example:
* </p>
* <code>
* UIManager.put(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION, <br>
* Boolean.TRUE);<br>
* JTabbedPane jtpMain = new JTabbedPane();<br>
* JTabbedPane jtpSecondary = new JTabbedPane();<br>
* jtpSecondary.putClientProperty(SubstanceLookAndFeel.TABBED_PANE_CLOSE_BUTTONS_MODIFIED_ANIMATION, <br>
* 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>
* 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
*/
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>
* public TabCloseKind onAreaClick(JTabbedPane tabbedPane,<br>
* int tabIndex, MouseEvent mouseEvent) {<br>
* if (mouseEvent.getButton() != MouseEvent.BUTTON3)<br>
* return TabCloseKind.NONE;<br>
* if (mouseEvent.isShiftDown()) {<br>
* return TabCloseKind.ALL;<br>
* }<br>
* return TabCloseKind.THIS;<br>
* }<br>
* <br>
* public TabCloseKind onCloseButtonClick(JTabbedPane tabbedPane,<br>
* int tabIndex, MouseEvent mouseEvent) {<br>
* if (mouseEvent.isAltDown()) {<br>
* return TabCloseKind.ALL_BUT_THIS;<br>
* }<br>
* if (mouseEvent.isShiftDown()) {<br>
* return TabCloseKind.ALL;<br>
* }<br>
* return TabCloseKind.THIS;<br>
* }<br>
* <br>
* public String getAreaTooltip(JTabbedPane tabbedPane, int tabIndex) {<br>
* return null;<br>
* }<br>
* <br>
* public String getCloseButtonTooltip(JTabbedPane tabbedPane,<br>
* int tabIndex) {<br>
* StringBuffer result = new StringBuffer();<br>
* result.append("<html><body>");<br>
* result.append("Mouse click closes <b>"<br>
* + tabbedPane.getTitleAt(tabIndex) + "</b> tab");<br>
* result.append("<br><b>Alt</b>-Mouse click closes all tabs but <b>"<br>
* + tabbedPane.getTitleAt(tabIndex) + "</b> tab");<br>
* result.append("<br><b>Shift</b>-Mouse click closes all tabs");<br>
* result.append("</body></html>");<br>
* return result.toString();<br>
* }<br>
* };<br>
*
* JTabbedPane jtp = new JTabbedPane();<br>
* jtp.putClientProperty(<br>
* SubstanceLookAndFeel.TABBED_PANE_CLOSE_CALLBACK, <br>
* closeCallback);
* </code>
*
* @since version 2.3
*/
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_PLACEMENT}.
*
* <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>
* 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>
* TabContentPaneBorderKind.SINGLE_FULL);
* </code>
*
* @since version 4.1
*/
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>
* new Object[] { "Ester", "Jordi", "Jordina", "Jorge", "Sergi" });<br>
* cb.putClientProperty(SubstanceLookAndFeel.COMBO_BOX_POPUP_FLYOUT_ORIENTATION, <br>
* SwingConstants.CENTER);
* </code>
*
* @since version 2.3
* @see #COMBO_POPUP_PROTOTYPE
*/
public final static String COMBO_BOX_POPUP_FLYOUT_ORIENTATION = "substancelaf.comboboxpopupFlyoutOrientation";
/**
* 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
*/
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_FILL}.
*
* <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>
* JPanel topPanel = new JPanel();<br>
* topPanel.putClientProperty(SubstanceLookAndFeel.FOCUS_KIND, FocusKind.UNDERLINE);<br>
* JPanel panel1 = new JPanel();<br>
* JButton b1 = new JButton("button1");<br>
* b1.putClientProperty(SubstanceLookAndFeel.FOCUS_KIND, FocusKind.TEXT);<br>
* JButton b2 = new JButton("button2");<br>
* JButton b3 = new JButton("button3");<br>
* b3.putClientProperty(SubstanceLookAndFeel.FOCUS_KIND, FocusKind.ALL_INNER);<br>
* panel1.add(b1);<br>
* panel1.add(b2);<br>
* topPanel.add(panel1);<br>
* 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>
* "aaaaaaaaaa", "this one is the one", "aaaaaaaaaaaaaaaaaaaaa" });<br>
* comboProto1.setPrototypeDisplayValue("aaaaa");<br>
* comboProto1.putClientProperty(SubstanceLookAndFeel.COMBO_POPUP_PROTOTYPE,<br>
* "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>
* "this is not", "this one is not it",<br>
* "this one is it that is for the popup" });<br>
* comboProto3.setPrototypeDisplayValue("aaaaa");<br>
* comboProto3.putClientProperty(SubstanceLookAndFeel.COMBO_POPUP_PROTOTYPE,<br>
* new ComboPopupPrototypeCallback() {<br>
* public Object getPopupPrototypeDisplayValue(JComboBox jc) {<br>
* return jc.getModel().getElementAt(<br>
* jc.getModel().getSize() - 1);<br>
* }<br>
* });
* </code>
*
* @since version 3.0
* @see #COMBO_BOX_POPUP_FLYOUT_ORIENTATION
*/
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
*/
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>
* 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>
* new Integer(2));
* </code>
*
* @since version 2.2
*/
public final static String PASSWORD_ECHO_PER_CHAR = "substancelaf.passwordEchoPerChar";
/**
* <p>
* Client property name for specifying that icons on controls such 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
*/
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>
* 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)
*/
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";
/**
* Internal client property name for storing application-specific input map
* set.
*
* @since version 6.1
* @see #setInputMapSet(InputMapSet)
* @see #getInputMapSet()
*/
protected final static String SUBSTANCE_INPUT_MAP_SET_KEY = "substancelaf.inputMapSetKey";
/**
* 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>
* new ClassicButtonShaper());
* </code>
*
* @since version 2.1
*/
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)
*/
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.pushingpixels.substance.internal.resources.Labels",
Locale.getDefault());
} else {
SubstanceLookAndFeel.LABEL_BUNDLE = ResourceBundle
.getBundle(
"org.pushingpixels.substance.internal.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.pushingpixels.substance.internal.resources.Labels",
locale);
} else {
return ResourceBundle.getBundle(
"org.pushingpixels.substance.internal.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()
*/
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.pushingpixels.substance.internal.ui.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",
"ViewportUI", UI_CLASSNAME_PREFIX + "ViewportUI",
};
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
*/
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&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)
*/
public static FontPolicy getFontPolicy() {
FontPolicy policy = (FontPolicy) UIManager
.get(SUBSTANCE_FONT_POLICY_KEY);
if (policy != null)
return policy;
// return default policy
return SubstanceFontUtilities.getDefaultFontPolicy();
}
/**
* Sets the {@link InputMapSet} to be used with Substance family. If the
* specified set 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 inputMapSet
* The {@link InputMapSet} to be used with Substance family, or
* <code>null</code> to reset to the default
*
* @see #getInputMapSet()
* @see SubstanceLookAndFeel#SUBSTANCE_INPUT_MAP_SET_KEY
*/
public static void setInputMapSet(InputMapSet inputMapSet) {
UIManager.put(SUBSTANCE_INPUT_MAP_SET_KEY, inputMapSet);
SubstanceLookAndFeel.setSkin(SubstanceLookAndFeel.getCurrentSkin());
}
/**
* Looks up and retrieves the {@link InputMapSet} used by the Substance
* family. If a {@link InputMapSet} has been set, it'll be returned. If no
* {@link InputMapSet} has been set for this look, the default Substance
* input map set will be returned.
*
* @return the {@link InputMapSet} set for this Look&feel - if any, or
* the default Substance input map set.
*
* @see #setInputMapSet(InputMapSet)
*/
public static InputMapSet getInputMapSet() {
InputMapSet inputMapSet = (InputMapSet) UIManager
.get(SUBSTANCE_INPUT_MAP_SET_KEY);
if (inputMapSet != null)
return inputMapSet;
// return system input map set
return SubstanceInputMapUtilities.getSystemInputMapSet();
}
/**
* 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();
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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)
*/
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.
*/
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.
*/
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.
*/
public static void registerLocaleChangeListener(
LocaleChangeListener localeListener) {
SubstanceLookAndFeel.localeChangeListeners.add(localeListener);
}
/**
* Unregisters a listener on locale change.
*
* @param localeListener
* The listener to unregister.
*/
public static void unregisterLocaleChangeListener(
LocaleChangeListener localeListener) {
SubstanceLookAndFeel.localeChangeListeners.remove(localeListener);
}
/**
* Returns all listeners registered on locale change.
*
* @return All listeners registered on locale change.
*/
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. This method should not be called from inside the
* initialization sequence of your window. If the specific widget needs to
* be visible when the window is shown, wrap the call with
* {@link SwingUtilities#invokeLater(Runnable)}.
*
* @param rootPane
* Root pane. May be <code>null</code>.
* @param visible
* Visibility indication.
* @param substanceWidgets
* Widget types.
* @since version 5.0
*/
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)
*/
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()
*/
public static void setToUseConstantThemesOnDialogs(
boolean toUseConstantThemesOnDialogs) {
SubstanceLookAndFeel.toUseConstantThemesOnDialogs = toUseConstantThemesOnDialogs;
SwingUtilities.invokeLater(() -> {
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)
*/
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 lafDefaults = UIManager.getLookAndFeelDefaults();
UIDefaults defaults = lafDefaults;
// The table will be null when the skin is set using a custom
// LAF
if (defaults != null) {
initFontDefaults(lafDefaults, SubstanceLookAndFeel
.getFontPolicy().getFontSet("Substance", null));
newSkin.addCustomEntriesToTable(lafDefaults);
SubstanceLookAndFeel.componentPlugins
.processAllDefaultsEntries(lafDefaults, newSkin);
}
// file chooser strings go to the main UIManager table
ResourceBundle substanceBundle = SubstanceLookAndFeel
.getLabelBundle();
Enumeration<String> keyEn = substanceBundle.getKeys();
while (keyEn.hasMoreElements()) {
String key = keyEn.nextElement();
if (key.indexOf("FileChooser") != -1) {
String value = substanceBundle.getString(key);
UIManager.put(key, value);
}
}
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.
ncdfe.printStackTrace(System.out);
return false;
} catch (Exception e) {
e.printStackTrace(System.out);
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()
*/
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.
*/
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
*/
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.
*/
public static void setDecorationType(JComponent comp,
DecorationAreaType type) {
DecorationPainterUtils.setDecorationType(comp, type);
}
/**
* Returns the decoration area type of the specified component. The
* component and its ancestor hierarchy are scanned for the registered
* decoration area type. If
* {@link #setDecorationType(JComponent, DecorationAreaType)} has been
* called on the specified component, the matching decoration type is
* returned. Otherwise, the component hierarchy is scanned to find the
* closest ancestor that was passed to
* {@link #setDecorationType(JComponent, DecorationAreaType)} - and its
* decoration type is returned. If neither the component, nor any one of its
* parent components has been passed to the setter method,
* {@link DecorationAreaType#NONE} is returned.
*
* @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(
icon.getIconWidth(), icon.getIconHeight());
Graphics2D g2d = intermediate.createGraphics();
g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
int scaleFactor = UIUtil.getScaleFactor();
g2d.drawImage(result, 0, 0, result.getWidth() / scaleFactor,
result.getHeight() / scaleFactor, null);
g2d.dispose();
result = intermediate;
}
return new HiDpiAwareIconUiResource(result);
}
}