src/java.desktop/share/classes/javax/swing/plaf/synth/SynthLookAndFeel.java
changeset 47216 71c04702a3d5
parent 47146 9910fab09c00
child 52248 2e330da7cbf4
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2002, 2017, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package javax.swing.plaf.synth;
       
    26 
       
    27 import java.awt.*;
       
    28 import java.beans.*;
       
    29 import java.io.*;
       
    30 import java.lang.ref.*;
       
    31 import java.net.*;
       
    32 import java.security.*;
       
    33 import java.text.*;
       
    34 import java.util.*;
       
    35 import javax.swing.*;
       
    36 import javax.swing.plaf.*;
       
    37 import javax.swing.plaf.basic.*;
       
    38 
       
    39 import sun.awt.*;
       
    40 import sun.security.action.*;
       
    41 import sun.swing.*;
       
    42 import sun.swing.plaf.synth.*;
       
    43 
       
    44 /**
       
    45  * SynthLookAndFeel provides the basis for creating a customized look and
       
    46  * feel. SynthLookAndFeel does not directly provide a look, all painting is
       
    47  * delegated.
       
    48  * You need to either provide a configuration file, by way of the
       
    49  * {@link #load} method, or provide your own {@link SynthStyleFactory}
       
    50  * to {@link #setStyleFactory}. Refer to the
       
    51  * <a href="package-summary.html">package summary</a> for an example of
       
    52  * loading a file, and {@link javax.swing.plaf.synth.SynthStyleFactory} for
       
    53  * an example of providing your own <code>SynthStyleFactory</code> to
       
    54  * <code>setStyleFactory</code>.
       
    55  * <p>
       
    56  * {@link SynthIcon} interface provides
       
    57  * {@code paintIcon(synthContext, graphics, x, y, width, height)} method that
       
    58  * allows to draw the icon with the given {@code SynthContext}.
       
    59  * <p>
       
    60  * <strong>Warning:</strong>
       
    61  * This class implements {@link Serializable} as a side effect of it
       
    62  * extending {@link BasicLookAndFeel}. It is not intended to be serialized.
       
    63  * An attempt to serialize it will
       
    64  * result in {@link NotSerializableException}.
       
    65  *
       
    66  * @serial exclude
       
    67  * @since 1.5
       
    68  * @author Scott Violet
       
    69  */
       
    70 @SuppressWarnings("serial") // Per above comment, not actually serializable
       
    71 public class SynthLookAndFeel extends BasicLookAndFeel {
       
    72     /**
       
    73      * Used in a handful of places where we need an empty Insets.
       
    74      */
       
    75     static final Insets EMPTY_UIRESOURCE_INSETS = new InsetsUIResource(
       
    76                                                             0, 0, 0, 0);
       
    77 
       
    78     /**
       
    79      * AppContext key to get the current SynthStyleFactory.
       
    80      */
       
    81     private static final Object STYLE_FACTORY_KEY =
       
    82                   new StringBuffer("com.sun.java.swing.plaf.gtk.StyleCache");
       
    83 
       
    84     /**
       
    85      * AppContext key to get selectedUI.
       
    86      */
       
    87     private static final Object SELECTED_UI_KEY = new StringBuilder("selectedUI");
       
    88 
       
    89     /**
       
    90      * AppContext key to get selectedUIState.
       
    91      */
       
    92     private static final Object SELECTED_UI_STATE_KEY = new StringBuilder("selectedUIState");
       
    93 
       
    94     /**
       
    95      * The last SynthStyleFactory that was asked for from AppContext
       
    96      * <code>lastContext</code>.
       
    97      */
       
    98     private static SynthStyleFactory lastFactory;
       
    99     /**
       
   100      * AppContext lastLAF came from.
       
   101      */
       
   102     private static AppContext lastContext;
       
   103 
       
   104     /**
       
   105      * SynthStyleFactory for the this SynthLookAndFeel.
       
   106      */
       
   107     private SynthStyleFactory factory;
       
   108 
       
   109     /**
       
   110      * Map of defaults table entries. This is populated via the load
       
   111      * method.
       
   112      */
       
   113     private Map<String, Object> defaultsMap;
       
   114 
       
   115     private Handler _handler;
       
   116 
       
   117     static ComponentUI getSelectedUI() {
       
   118         return (ComponentUI) AppContext.getAppContext().get(SELECTED_UI_KEY);
       
   119     }
       
   120 
       
   121     /**
       
   122      * Used by the renderers. For the most part the renderers are implemented
       
   123      * as Labels, which is problematic in so far as they are never selected.
       
   124      * To accommodate this SynthLabelUI checks if the current
       
   125      * UI matches that of <code>selectedUI</code> (which this methods sets), if
       
   126      * it does, then a state as set by this method is returned. This provides
       
   127      * a way for labels to have a state other than selected.
       
   128      */
       
   129     static void setSelectedUI(ComponentUI uix, boolean selected,
       
   130                               boolean focused, boolean enabled,
       
   131                               boolean rollover) {
       
   132         int selectedUIState = 0;
       
   133 
       
   134         if (selected) {
       
   135             selectedUIState = SynthConstants.SELECTED;
       
   136             if (focused) {
       
   137                 selectedUIState |= SynthConstants.FOCUSED;
       
   138             }
       
   139         }
       
   140         else if (rollover && enabled) {
       
   141             selectedUIState |=
       
   142                     SynthConstants.MOUSE_OVER | SynthConstants.ENABLED;
       
   143             if (focused) {
       
   144                 selectedUIState |= SynthConstants.FOCUSED;
       
   145             }
       
   146         }
       
   147         else {
       
   148             if (enabled) {
       
   149                 selectedUIState |= SynthConstants.ENABLED;
       
   150                 if (focused) {
       
   151                     selectedUIState |= SynthConstants.FOCUSED;
       
   152                 }
       
   153             }
       
   154             else {
       
   155                 selectedUIState |= SynthConstants.DISABLED;
       
   156             }
       
   157         }
       
   158 
       
   159         AppContext context = AppContext.getAppContext();
       
   160 
       
   161         context.put(SELECTED_UI_KEY, uix);
       
   162         context.put(SELECTED_UI_STATE_KEY, Integer.valueOf(selectedUIState));
       
   163     }
       
   164 
       
   165     static int getSelectedUIState() {
       
   166         Integer result = (Integer) AppContext.getAppContext().get(SELECTED_UI_STATE_KEY);
       
   167 
       
   168         return result == null ? 0 : result.intValue();
       
   169     }
       
   170 
       
   171     /**
       
   172      * Clears out the selected UI that was last set in setSelectedUI.
       
   173      */
       
   174     static void resetSelectedUI() {
       
   175         AppContext.getAppContext().remove(SELECTED_UI_KEY);
       
   176     }
       
   177 
       
   178 
       
   179     /**
       
   180      * Sets the SynthStyleFactory that the UI classes provided by
       
   181      * synth will use to obtain a SynthStyle.
       
   182      *
       
   183      * @param cache SynthStyleFactory the UIs should use.
       
   184      */
       
   185     public static void setStyleFactory(SynthStyleFactory cache) {
       
   186         // We assume the setter is called BEFORE the getter has been invoked
       
   187         // for a particular AppContext.
       
   188         synchronized(SynthLookAndFeel.class) {
       
   189             AppContext context = AppContext.getAppContext();
       
   190             lastFactory = cache;
       
   191             lastContext = context;
       
   192             context.put(STYLE_FACTORY_KEY, cache);
       
   193         }
       
   194     }
       
   195 
       
   196     /**
       
   197      * Returns the current SynthStyleFactory.
       
   198      *
       
   199      * @return SynthStyleFactory
       
   200      */
       
   201     public static SynthStyleFactory getStyleFactory() {
       
   202         synchronized(SynthLookAndFeel.class) {
       
   203             AppContext context = AppContext.getAppContext();
       
   204 
       
   205             if (lastContext == context) {
       
   206                 return lastFactory;
       
   207             }
       
   208             lastContext = context;
       
   209             lastFactory = (SynthStyleFactory) context.get(STYLE_FACTORY_KEY);
       
   210             return lastFactory;
       
   211         }
       
   212     }
       
   213 
       
   214     /**
       
   215      * Returns the component state for the specified component. This should
       
   216      * only be used for Components that don't have any special state beyond
       
   217      * that of ENABLED, DISABLED or FOCUSED. For example, buttons shouldn't
       
   218      * call into this method.
       
   219      */
       
   220     static int getComponentState(Component c) {
       
   221         if (c.isEnabled()) {
       
   222             if (c.isFocusOwner()) {
       
   223                 return SynthUI.ENABLED | SynthUI.FOCUSED;
       
   224             }
       
   225             return SynthUI.ENABLED;
       
   226         }
       
   227         return SynthUI.DISABLED;
       
   228     }
       
   229 
       
   230     /**
       
   231      * Gets a SynthStyle for the specified region of the specified component.
       
   232      * This is not for general consumption, only custom UIs should call this
       
   233      * method.
       
   234      *
       
   235      * @param c JComponent to get the SynthStyle for
       
   236      * @param region Identifies the region of the specified component
       
   237      * @return SynthStyle to use.
       
   238      */
       
   239     public static SynthStyle getStyle(JComponent c, Region region) {
       
   240         return getStyleFactory().getStyle(c, region);
       
   241     }
       
   242 
       
   243     /**
       
   244      * Returns true if the Style should be updated in response to the
       
   245      * specified PropertyChangeEvent. This forwards to
       
   246      * <code>shouldUpdateStyleOnAncestorChanged</code> as necessary.
       
   247      */
       
   248     static boolean shouldUpdateStyle(PropertyChangeEvent event) {
       
   249         LookAndFeel laf = UIManager.getLookAndFeel();
       
   250         return (laf instanceof SynthLookAndFeel &&
       
   251                 ((SynthLookAndFeel) laf).shouldUpdateStyleOnEvent(event));
       
   252     }
       
   253 
       
   254     /**
       
   255      * A convience method that will reset the Style of StyleContext if
       
   256      * necessary.
       
   257      *
       
   258      * @return newStyle
       
   259      */
       
   260     static SynthStyle updateStyle(SynthContext context, SynthUI ui) {
       
   261         SynthStyle newStyle = getStyle(context.getComponent(),
       
   262                                        context.getRegion());
       
   263         SynthStyle oldStyle = context.getStyle();
       
   264 
       
   265         if (newStyle != oldStyle) {
       
   266             if (oldStyle != null) {
       
   267                 oldStyle.uninstallDefaults(context);
       
   268             }
       
   269             context.setStyle(newStyle);
       
   270             newStyle.installDefaults(context, ui);
       
   271         }
       
   272         return newStyle;
       
   273     }
       
   274 
       
   275     /**
       
   276      * Updates the style associated with <code>c</code>, and all its children.
       
   277      * This is a lighter version of
       
   278      * <code>SwingUtilities.updateComponentTreeUI</code>.
       
   279      *
       
   280      * @param c Component to update style for.
       
   281      */
       
   282     public static void updateStyles(Component c) {
       
   283         if (c instanceof JComponent) {
       
   284             // Yes, this is hacky. A better solution is to get the UI
       
   285             // and cast, but JComponent doesn't expose a getter for the UI
       
   286             // (each of the UIs do), making that approach impractical.
       
   287             String name = c.getName();
       
   288             c.setName(null);
       
   289             if (name != null) {
       
   290                 c.setName(name);
       
   291             }
       
   292             ((JComponent)c).revalidate();
       
   293         }
       
   294         Component[] children = null;
       
   295         if (c instanceof JMenu) {
       
   296             children = ((JMenu)c).getMenuComponents();
       
   297         }
       
   298         else if (c instanceof Container) {
       
   299             children = ((Container)c).getComponents();
       
   300         }
       
   301         if (children != null) {
       
   302             for (Component child : children) {
       
   303                 updateStyles(child);
       
   304             }
       
   305         }
       
   306         c.repaint();
       
   307     }
       
   308 
       
   309     /**
       
   310      * Returns the Region for the JComponent <code>c</code>.
       
   311      *
       
   312      * @param c JComponent to fetch the Region for
       
   313      * @return Region corresponding to <code>c</code>
       
   314      */
       
   315     public static Region getRegion(JComponent c) {
       
   316         return Region.getRegion(c);
       
   317     }
       
   318 
       
   319     /**
       
   320      * A convenience method to return where the foreground should be
       
   321      * painted for the Component identified by the passed in
       
   322      * AbstractSynthContext.
       
   323      */
       
   324     static Insets getPaintingInsets(SynthContext state, Insets insets) {
       
   325         if (state.isSubregion()) {
       
   326             insets = state.getStyle().getInsets(state, insets);
       
   327         }
       
   328         else {
       
   329             insets = state.getComponent().getInsets(insets);
       
   330         }
       
   331         return insets;
       
   332     }
       
   333 
       
   334     /**
       
   335      * A convenience method that handles painting of the background.
       
   336      * All SynthUI implementations should override update and invoke
       
   337      * this method.
       
   338      */
       
   339     static void update(SynthContext state, Graphics g) {
       
   340         paintRegion(state, g, null);
       
   341     }
       
   342 
       
   343     /**
       
   344      * A convenience method that handles painting of the background for
       
   345      * subregions. All SynthUI's that have subregions should invoke
       
   346      * this method, than paint the foreground.
       
   347      */
       
   348     static void updateSubregion(SynthContext state, Graphics g,
       
   349                                 Rectangle bounds) {
       
   350         paintRegion(state, g, bounds);
       
   351     }
       
   352 
       
   353     private static void paintRegion(SynthContext state, Graphics g,
       
   354                                     Rectangle bounds) {
       
   355         JComponent c = state.getComponent();
       
   356         SynthStyle style = state.getStyle();
       
   357         int x, y, width, height;
       
   358 
       
   359         if (bounds == null) {
       
   360             x = 0;
       
   361             y = 0;
       
   362             width = c.getWidth();
       
   363             height = c.getHeight();
       
   364         }
       
   365         else {
       
   366             x = bounds.x;
       
   367             y = bounds.y;
       
   368             width = bounds.width;
       
   369             height = bounds.height;
       
   370         }
       
   371 
       
   372         // Fill in the background, if necessary.
       
   373         boolean subregion = state.isSubregion();
       
   374         if ((subregion && style.isOpaque(state)) ||
       
   375                           (!subregion && c.isOpaque())) {
       
   376             g.setColor(style.getColor(state, ColorType.BACKGROUND));
       
   377             g.fillRect(x, y, width, height);
       
   378         }
       
   379     }
       
   380 
       
   381     static boolean isLeftToRight(Component c) {
       
   382         return c.getComponentOrientation().isLeftToRight();
       
   383     }
       
   384 
       
   385     /**
       
   386      * Returns the ui that is of type <code>klass</code>, or null if
       
   387      * one can not be found.
       
   388      */
       
   389     static Object getUIOfType(ComponentUI ui, Class<?> klass) {
       
   390         if (klass.isInstance(ui)) {
       
   391             return ui;
       
   392         }
       
   393         return null;
       
   394     }
       
   395 
       
   396     /**
       
   397      * Creates the Synth look and feel <code>ComponentUI</code> for
       
   398      * the passed in <code>JComponent</code>.
       
   399      *
       
   400      * @param c JComponent to create the <code>ComponentUI</code> for
       
   401      * @return ComponentUI to use for <code>c</code>
       
   402      */
       
   403     public static ComponentUI createUI(JComponent c) {
       
   404         String key = c.getUIClassID().intern();
       
   405 
       
   406         if (key == "ButtonUI") {
       
   407             return SynthButtonUI.createUI(c);
       
   408         }
       
   409         else if (key == "CheckBoxUI") {
       
   410             return SynthCheckBoxUI.createUI(c);
       
   411         }
       
   412         else if (key == "CheckBoxMenuItemUI") {
       
   413             return SynthCheckBoxMenuItemUI.createUI(c);
       
   414         }
       
   415         else if (key == "ColorChooserUI") {
       
   416             return SynthColorChooserUI.createUI(c);
       
   417         }
       
   418         else if (key == "ComboBoxUI") {
       
   419             return SynthComboBoxUI.createUI(c);
       
   420         }
       
   421         else if (key == "DesktopPaneUI") {
       
   422             return SynthDesktopPaneUI.createUI(c);
       
   423         }
       
   424         else if (key == "DesktopIconUI") {
       
   425             return SynthDesktopIconUI.createUI(c);
       
   426         }
       
   427         else if (key == "EditorPaneUI") {
       
   428             return SynthEditorPaneUI.createUI(c);
       
   429         }
       
   430         else if (key == "FileChooserUI") {
       
   431             return SynthFileChooserUI.createUI(c);
       
   432         }
       
   433         else if (key == "FormattedTextFieldUI") {
       
   434             return SynthFormattedTextFieldUI.createUI(c);
       
   435         }
       
   436         else if (key == "InternalFrameUI") {
       
   437             return SynthInternalFrameUI.createUI(c);
       
   438         }
       
   439         else if (key == "LabelUI") {
       
   440             return SynthLabelUI.createUI(c);
       
   441         }
       
   442         else if (key == "ListUI") {
       
   443             return SynthListUI.createUI(c);
       
   444         }
       
   445         else if (key == "MenuBarUI") {
       
   446             return SynthMenuBarUI.createUI(c);
       
   447         }
       
   448         else if (key == "MenuUI") {
       
   449             return SynthMenuUI.createUI(c);
       
   450         }
       
   451         else if (key == "MenuItemUI") {
       
   452             return SynthMenuItemUI.createUI(c);
       
   453         }
       
   454         else if (key == "OptionPaneUI") {
       
   455             return SynthOptionPaneUI.createUI(c);
       
   456         }
       
   457         else if (key == "PanelUI") {
       
   458             return SynthPanelUI.createUI(c);
       
   459         }
       
   460         else if (key == "PasswordFieldUI") {
       
   461             return SynthPasswordFieldUI.createUI(c);
       
   462         }
       
   463         else if (key == "PopupMenuSeparatorUI") {
       
   464             return SynthSeparatorUI.createUI(c);
       
   465         }
       
   466         else if (key == "PopupMenuUI") {
       
   467             return SynthPopupMenuUI.createUI(c);
       
   468         }
       
   469         else if (key == "ProgressBarUI") {
       
   470             return SynthProgressBarUI.createUI(c);
       
   471         }
       
   472         else if (key == "RadioButtonUI") {
       
   473             return SynthRadioButtonUI.createUI(c);
       
   474         }
       
   475         else if (key == "RadioButtonMenuItemUI") {
       
   476             return SynthRadioButtonMenuItemUI.createUI(c);
       
   477         }
       
   478         else if (key == "RootPaneUI") {
       
   479             return SynthRootPaneUI.createUI(c);
       
   480         }
       
   481         else if (key == "ScrollBarUI") {
       
   482             return SynthScrollBarUI.createUI(c);
       
   483         }
       
   484         else if (key == "ScrollPaneUI") {
       
   485             return SynthScrollPaneUI.createUI(c);
       
   486         }
       
   487         else if (key == "SeparatorUI") {
       
   488             return SynthSeparatorUI.createUI(c);
       
   489         }
       
   490         else if (key == "SliderUI") {
       
   491             return SynthSliderUI.createUI(c);
       
   492         }
       
   493         else if (key == "SpinnerUI") {
       
   494             return SynthSpinnerUI.createUI(c);
       
   495         }
       
   496         else if (key == "SplitPaneUI") {
       
   497             return SynthSplitPaneUI.createUI(c);
       
   498         }
       
   499         else if (key == "TabbedPaneUI") {
       
   500             return SynthTabbedPaneUI.createUI(c);
       
   501         }
       
   502         else if (key == "TableUI") {
       
   503             return SynthTableUI.createUI(c);
       
   504         }
       
   505         else if (key == "TableHeaderUI") {
       
   506             return SynthTableHeaderUI.createUI(c);
       
   507         }
       
   508         else if (key == "TextAreaUI") {
       
   509             return SynthTextAreaUI.createUI(c);
       
   510         }
       
   511         else if (key == "TextFieldUI") {
       
   512             return SynthTextFieldUI.createUI(c);
       
   513         }
       
   514         else if (key == "TextPaneUI") {
       
   515             return SynthTextPaneUI.createUI(c);
       
   516         }
       
   517         else if (key == "ToggleButtonUI") {
       
   518             return SynthToggleButtonUI.createUI(c);
       
   519         }
       
   520         else if (key == "ToolBarSeparatorUI") {
       
   521             return SynthSeparatorUI.createUI(c);
       
   522         }
       
   523         else if (key == "ToolBarUI") {
       
   524             return SynthToolBarUI.createUI(c);
       
   525         }
       
   526         else if (key == "ToolTipUI") {
       
   527             return SynthToolTipUI.createUI(c);
       
   528         }
       
   529         else if (key == "TreeUI") {
       
   530             return SynthTreeUI.createUI(c);
       
   531         }
       
   532         else if (key == "ViewportUI") {
       
   533             return SynthViewportUI.createUI(c);
       
   534         }
       
   535         return null;
       
   536     }
       
   537 
       
   538 
       
   539     /**
       
   540      * Creates a SynthLookAndFeel.
       
   541      * <p>
       
   542      * For the returned <code>SynthLookAndFeel</code> to be useful you need to
       
   543      * invoke <code>load</code> to specify the set of
       
   544      * <code>SynthStyle</code>s, or invoke <code>setStyleFactory</code>.
       
   545      *
       
   546      * @see #load
       
   547      * @see #setStyleFactory
       
   548      */
       
   549     public SynthLookAndFeel() {
       
   550         factory = new DefaultSynthStyleFactory();
       
   551         _handler = new Handler();
       
   552     }
       
   553 
       
   554     /**
       
   555      * Loads the set of <code>SynthStyle</code>s that will be used by
       
   556      * this <code>SynthLookAndFeel</code>. <code>resourceBase</code> is
       
   557      * used to resolve any path based resources, for example an
       
   558      * <code>Image</code> would be resolved by
       
   559      * <code>resourceBase.getResource(path)</code>. Refer to
       
   560      * <a href="doc-files/synthFileFormat.html">Synth File Format</a>
       
   561      * for more information.
       
   562      *
       
   563      * @param input InputStream to load from
       
   564      * @param resourceBase used to resolve any images or other resources
       
   565      * @throws ParseException if there is an error in parsing
       
   566      * @throws IllegalArgumentException if input or resourceBase is <code>null</code>
       
   567      */
       
   568     public void load(InputStream input, Class<?> resourceBase) throws
       
   569                        ParseException {
       
   570         if (resourceBase == null) {
       
   571             throw new IllegalArgumentException(
       
   572                 "You must supply a valid resource base Class");
       
   573         }
       
   574 
       
   575         if (defaultsMap == null) {
       
   576             defaultsMap = new HashMap<String, Object>();
       
   577         }
       
   578 
       
   579         new SynthParser().parse(input, (DefaultSynthStyleFactory) factory,
       
   580                                 null, resourceBase, defaultsMap);
       
   581     }
       
   582 
       
   583     /**
       
   584      * Loads the set of <code>SynthStyle</code>s that will be used by
       
   585      * this <code>SynthLookAndFeel</code>. Path based resources are resolved
       
   586      * relatively to the specified <code>URL</code> of the style. For example
       
   587      * an <code>Image</code> would be resolved by
       
   588      * <code>new URL(synthFile, path)</code>. Refer to
       
   589      * <a href="doc-files/synthFileFormat.html">Synth File Format</a> for more
       
   590      * information.
       
   591      *
       
   592      * @param url the <code>URL</code> to load the set of
       
   593      *     <code>SynthStyle</code> from
       
   594      * @throws ParseException if there is an error in parsing
       
   595      * @throws IllegalArgumentException if synthSet is <code>null</code>
       
   596      * @throws IOException if synthSet cannot be opened as an <code>InputStream</code>
       
   597      * @since 1.6
       
   598      */
       
   599     public void load(URL url) throws ParseException, IOException {
       
   600         if (url == null) {
       
   601             throw new IllegalArgumentException(
       
   602                 "You must supply a valid Synth set URL");
       
   603         }
       
   604 
       
   605         if (defaultsMap == null) {
       
   606             defaultsMap = new HashMap<String, Object>();
       
   607         }
       
   608 
       
   609         InputStream input = url.openStream();
       
   610         new SynthParser().parse(input, (DefaultSynthStyleFactory) factory,
       
   611                                 url, null, defaultsMap);
       
   612     }
       
   613 
       
   614     /**
       
   615      * Called by UIManager when this look and feel is installed.
       
   616      */
       
   617     @Override
       
   618     public void initialize() {
       
   619         super.initialize();
       
   620         DefaultLookup.setDefaultLookup(new SynthDefaultLookup());
       
   621         setStyleFactory(factory);
       
   622         KeyboardFocusManager.getCurrentKeyboardFocusManager().
       
   623             addPropertyChangeListener(_handler);
       
   624     }
       
   625 
       
   626     /**
       
   627      * Called by UIManager when this look and feel is uninstalled.
       
   628      */
       
   629     @Override
       
   630     public void uninitialize() {
       
   631         KeyboardFocusManager.getCurrentKeyboardFocusManager().
       
   632             removePropertyChangeListener(_handler);
       
   633         // We should uninstall the StyleFactory here, but unfortunately
       
   634         // there are a handful of things that retain references to the
       
   635         // LookAndFeel and expect things to work
       
   636         super.uninitialize();
       
   637     }
       
   638 
       
   639     /**
       
   640      * Returns the defaults for this SynthLookAndFeel.
       
   641      *
       
   642      * @return Defaults table.
       
   643      */
       
   644     @Override
       
   645     public UIDefaults getDefaults() {
       
   646         UIDefaults table = new UIDefaults(60, 0.75f);
       
   647 
       
   648         Region.registerUIs(table);
       
   649         table.setDefaultLocale(Locale.getDefault());
       
   650         SwingAccessor.getUIDefaultsAccessor()
       
   651                      .addInternalBundle(table,
       
   652                              "com.sun.swing.internal.plaf.basic.resources.basic");
       
   653         SwingAccessor.getUIDefaultsAccessor()
       
   654                      .addInternalBundle(table,
       
   655                              "com.sun.swing.internal.plaf.synth.resources.synth");
       
   656 
       
   657         // SynthTabbedPaneUI supports rollover on tabs, GTK does not
       
   658         table.put("TabbedPane.isTabRollover", Boolean.TRUE);
       
   659 
       
   660         // These need to be defined for JColorChooser to work.
       
   661         table.put("ColorChooser.swatchesRecentSwatchSize",
       
   662                   new Dimension(10, 10));
       
   663         table.put("ColorChooser.swatchesDefaultRecentColor", Color.RED);
       
   664         table.put("ColorChooser.swatchesSwatchSize", new Dimension(10, 10));
       
   665 
       
   666         // These need to be defined for ImageView.
       
   667         table.put("html.pendingImage", SwingUtilities2.makeIcon(getClass(),
       
   668                                 BasicLookAndFeel.class,
       
   669                                 "icons/image-delayed.png"));
       
   670         table.put("html.missingImage", SwingUtilities2.makeIcon(getClass(),
       
   671                                 BasicLookAndFeel.class,
       
   672                                 "icons/image-failed.png"));
       
   673 
       
   674         // These are needed for PopupMenu.
       
   675         table.put("PopupMenu.selectedWindowInputMapBindings", new Object[] {
       
   676                   "ESCAPE", "cancel",
       
   677                     "DOWN", "selectNext",
       
   678                  "KP_DOWN", "selectNext",
       
   679                       "UP", "selectPrevious",
       
   680                    "KP_UP", "selectPrevious",
       
   681                     "LEFT", "selectParent",
       
   682                  "KP_LEFT", "selectParent",
       
   683                    "RIGHT", "selectChild",
       
   684                 "KP_RIGHT", "selectChild",
       
   685                    "ENTER", "return",
       
   686                    "ctrl ENTER", "return",
       
   687                    "SPACE", "return"
       
   688         });
       
   689         table.put("PopupMenu.selectedWindowInputMapBindings.RightToLeft",
       
   690                   new Object[] {
       
   691                     "LEFT", "selectChild",
       
   692                  "KP_LEFT", "selectChild",
       
   693                    "RIGHT", "selectParent",
       
   694                 "KP_RIGHT", "selectParent",
       
   695                   });
       
   696 
       
   697         // enabled antialiasing depending on desktop settings
       
   698         flushUnreferenced();
       
   699         SwingUtilities2.putAATextInfo(useLAFConditions(), table);
       
   700         new AATextListener(this);
       
   701 
       
   702         if (defaultsMap != null) {
       
   703             table.putAll(defaultsMap);
       
   704         }
       
   705         return table;
       
   706     }
       
   707 
       
   708     /**
       
   709      * Returns true, SynthLookAndFeel is always supported.
       
   710      *
       
   711      * @return true.
       
   712      */
       
   713     @Override
       
   714     public boolean isSupportedLookAndFeel() {
       
   715         return true;
       
   716     }
       
   717 
       
   718     /**
       
   719      * Returns false, SynthLookAndFeel is not a native look and feel.
       
   720      *
       
   721      * @return false
       
   722      */
       
   723     @Override
       
   724     public boolean isNativeLookAndFeel() {
       
   725         return false;
       
   726     }
       
   727 
       
   728     /**
       
   729      * Returns a textual description of SynthLookAndFeel.
       
   730      *
       
   731      * @return textual description of synth.
       
   732      */
       
   733     @Override
       
   734     public String getDescription() {
       
   735         return "Synth look and feel";
       
   736     }
       
   737 
       
   738     /**
       
   739      * Return a short string that identifies this look and feel.
       
   740      *
       
   741      * @return a short string identifying this look and feel.
       
   742      */
       
   743     @Override
       
   744     public String getName() {
       
   745         return "Synth look and feel";
       
   746     }
       
   747 
       
   748     /**
       
   749      * Return a string that identifies this look and feel.
       
   750      *
       
   751      * @return a short string identifying this look and feel.
       
   752      */
       
   753     @Override
       
   754     public String getID() {
       
   755         return "Synth";
       
   756     }
       
   757 
       
   758     /**
       
   759      * Returns whether or not the UIs should update their
       
   760      * <code>SynthStyles</code> from the <code>SynthStyleFactory</code>
       
   761      * when the ancestor of the <code>JComponent</code> changes. A subclass
       
   762      * that provided a <code>SynthStyleFactory</code> that based the
       
   763      * return value from <code>getStyle</code> off the containment hierarchy
       
   764      * would override this method to return true.
       
   765      *
       
   766      * @return whether or not the UIs should update their
       
   767      * <code>SynthStyles</code> from the <code>SynthStyleFactory</code>
       
   768      * when the ancestor changed.
       
   769      */
       
   770     public boolean shouldUpdateStyleOnAncestorChanged() {
       
   771         return false;
       
   772     }
       
   773 
       
   774     /**
       
   775      * Returns whether or not the UIs should update their styles when a
       
   776      * particular event occurs.
       
   777      *
       
   778      * @param ev a {@code PropertyChangeEvent}
       
   779      * @return whether or not the UIs should update their styles
       
   780      * @since 1.7
       
   781      */
       
   782     protected boolean shouldUpdateStyleOnEvent(PropertyChangeEvent ev) {
       
   783         String eName = ev.getPropertyName();
       
   784         if ("name" == eName || "componentOrientation" == eName) {
       
   785             return true;
       
   786         }
       
   787         if ("ancestor" == eName && ev.getNewValue() != null) {
       
   788             // Only update on an ancestor change when getting a valid
       
   789             // parent and the LookAndFeel wants this.
       
   790             return shouldUpdateStyleOnAncestorChanged();
       
   791         }
       
   792         return false;
       
   793     }
       
   794 
       
   795     /**
       
   796      * Returns the antialiasing information as specified by the host desktop.
       
   797      * Antialiasing might be forced off if the desktop is GNOME and the user
       
   798      * has set his locale to Chinese, Japanese or Korean. This is consistent
       
   799      * with what GTK does. See com.sun.java.swing.plaf.gtk.GtkLookAndFeel
       
   800      * for more information about CJK and antialiased fonts.
       
   801      *
       
   802      * @return the text antialiasing information associated to the desktop
       
   803      */
       
   804     private static boolean useLAFConditions() {
       
   805         String language = Locale.getDefault().getLanguage();
       
   806         String desktop =
       
   807             AccessController.doPrivileged(new GetPropertyAction("sun.desktop"));
       
   808 
       
   809         boolean isCjkLocale = (Locale.CHINESE.getLanguage().equals(language) ||
       
   810                 Locale.JAPANESE.getLanguage().equals(language) ||
       
   811                 Locale.KOREAN.getLanguage().equals(language));
       
   812         boolean isGnome = "gnome".equals(desktop);
       
   813         boolean isLocal = SwingUtilities2.isLocalDisplay();
       
   814 
       
   815         return isLocal && (!isGnome || !isCjkLocale);
       
   816     }
       
   817 
       
   818     private static ReferenceQueue<LookAndFeel> queue = new ReferenceQueue<LookAndFeel>();
       
   819 
       
   820     private static void flushUnreferenced() {
       
   821         AATextListener aatl;
       
   822         while ((aatl = (AATextListener) queue.poll()) != null) {
       
   823             aatl.dispose();
       
   824         }
       
   825     }
       
   826 
       
   827     private static class AATextListener
       
   828         extends WeakReference<LookAndFeel> implements PropertyChangeListener {
       
   829         private String key = SunToolkit.DESKTOPFONTHINTS;
       
   830 
       
   831         AATextListener(LookAndFeel laf) {
       
   832             super(laf, queue);
       
   833             Toolkit tk = Toolkit.getDefaultToolkit();
       
   834             tk.addPropertyChangeListener(key, this);
       
   835         }
       
   836 
       
   837         @Override
       
   838         public void propertyChange(PropertyChangeEvent pce) {
       
   839             UIDefaults defaults = UIManager.getLookAndFeelDefaults();
       
   840             if (defaults.getBoolean("Synth.doNotSetTextAA")) {
       
   841                 dispose();
       
   842                 return;
       
   843             }
       
   844 
       
   845             LookAndFeel laf = get();
       
   846             if (laf == null || laf != UIManager.getLookAndFeel()) {
       
   847                 dispose();
       
   848                 return;
       
   849             }
       
   850 
       
   851             SwingUtilities2.putAATextInfo(useLAFConditions(), defaults);
       
   852 
       
   853             updateUI();
       
   854         }
       
   855 
       
   856         void dispose() {
       
   857             Toolkit tk = Toolkit.getDefaultToolkit();
       
   858             tk.removePropertyChangeListener(key, this);
       
   859         }
       
   860 
       
   861         /**
       
   862          * Updates the UI of the passed in window and all its children.
       
   863          */
       
   864         private static void updateWindowUI(Window window) {
       
   865             updateStyles(window);
       
   866             Window ownedWins[] = window.getOwnedWindows();
       
   867             for (Window w : ownedWins) {
       
   868                 updateWindowUI(w);
       
   869             }
       
   870         }
       
   871 
       
   872         /**
       
   873          * Updates the UIs of all the known Frames.
       
   874          */
       
   875         private static void updateAllUIs() {
       
   876             Frame appFrames[] = Frame.getFrames();
       
   877             for (Frame frame : appFrames) {
       
   878                 updateWindowUI(frame);
       
   879             }
       
   880         }
       
   881 
       
   882         /**
       
   883          * Indicates if an updateUI call is pending.
       
   884          */
       
   885         private static boolean updatePending;
       
   886 
       
   887         /**
       
   888          * Sets whether or not an updateUI call is pending.
       
   889          */
       
   890         private static synchronized void setUpdatePending(boolean update) {
       
   891             updatePending = update;
       
   892         }
       
   893 
       
   894         /**
       
   895          * Returns true if a UI update is pending.
       
   896          */
       
   897         private static synchronized boolean isUpdatePending() {
       
   898             return updatePending;
       
   899         }
       
   900 
       
   901         protected void updateUI() {
       
   902             if (!isUpdatePending()) {
       
   903                 setUpdatePending(true);
       
   904                 Runnable uiUpdater = new Runnable() {
       
   905                     @Override
       
   906                     public void run() {
       
   907                         updateAllUIs();
       
   908                         setUpdatePending(false);
       
   909                     }
       
   910                 };
       
   911                 SwingUtilities.invokeLater(uiUpdater);
       
   912             }
       
   913         }
       
   914     }
       
   915 
       
   916     private void writeObject(java.io.ObjectOutputStream out)
       
   917             throws IOException {
       
   918         throw new NotSerializableException(this.getClass().getName());
       
   919     }
       
   920 
       
   921     private class Handler implements PropertyChangeListener {
       
   922         @Override
       
   923         public void propertyChange(PropertyChangeEvent evt) {
       
   924             String propertyName = evt.getPropertyName();
       
   925             Object newValue = evt.getNewValue();
       
   926             Object oldValue = evt.getOldValue();
       
   927 
       
   928             if ("focusOwner" == propertyName) {
       
   929                 if (oldValue instanceof JComponent) {
       
   930                     repaintIfBackgroundsDiffer((JComponent)oldValue);
       
   931 
       
   932                 }
       
   933 
       
   934                 if (newValue instanceof JComponent) {
       
   935                     repaintIfBackgroundsDiffer((JComponent)newValue);
       
   936                 }
       
   937             }
       
   938             else if ("managingFocus" == propertyName) {
       
   939                 // De-register listener on old keyboard focus manager and
       
   940                 // register it on the new one.
       
   941                 KeyboardFocusManager manager =
       
   942                     (KeyboardFocusManager)evt.getSource();
       
   943                 if (newValue.equals(Boolean.FALSE)) {
       
   944                     manager.removePropertyChangeListener(_handler);
       
   945                 }
       
   946                 else {
       
   947                     manager.addPropertyChangeListener(_handler);
       
   948                 }
       
   949             }
       
   950         }
       
   951 
       
   952         /**
       
   953          * This is a support method that will check if the background colors of
       
   954          * the specified component differ between focused and unfocused states.
       
   955          * If the color differ the component will then repaint itself.
       
   956          *
       
   957          * @comp the component to check
       
   958          */
       
   959         private void repaintIfBackgroundsDiffer(JComponent comp) {
       
   960             ComponentUI ui = comp.getUI();
       
   961             if (ui instanceof SynthUI) {
       
   962                 SynthUI synthUI = (SynthUI)ui;
       
   963                 SynthContext context = synthUI.getContext(comp);
       
   964                 SynthStyle style = context.getStyle();
       
   965                 int state = context.getComponentState();
       
   966 
       
   967                 // Get the current background color.
       
   968                 Color currBG = style.getColor(context, ColorType.BACKGROUND);
       
   969 
       
   970                 // Get the last background color.
       
   971                 state ^= SynthConstants.FOCUSED;
       
   972                 context.setComponentState(state);
       
   973                 Color lastBG = style.getColor(context, ColorType.BACKGROUND);
       
   974 
       
   975                 // Reset the component state back to original.
       
   976                 state ^= SynthConstants.FOCUSED;
       
   977                 context.setComponentState(state);
       
   978 
       
   979                 // Repaint the component if the backgrounds differed.
       
   980                 if (currBG != null && !currBG.equals(lastBG)) {
       
   981                     comp.repaint();
       
   982                 }
       
   983             }
       
   984         }
       
   985     }
       
   986 }