src/java.desktop/share/classes/com/sun/java/swing/plaf/windows/WindowsComboBoxUI.java
branchhttp-client-branch
changeset 55982 b6ff245c0db6
parent 55981 907bddce488c
parent 48332 651a95f30dfb
child 55983 e4a1f0c9d4c6
equal deleted inserted replaced
55981:907bddce488c 55982:b6ff245c0db6
     1 /*
       
     2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.java.swing.plaf.windows;
       
    27 
       
    28 import java.beans.PropertyChangeListener;
       
    29 import java.beans.PropertyChangeEvent;
       
    30 import javax.swing.plaf.basic.*;
       
    31 import javax.swing.plaf.*;
       
    32 import javax.swing.border.*;
       
    33 import javax.swing.*;
       
    34 import java.awt.event.*;
       
    35 import java.awt.*;
       
    36 
       
    37 import static com.sun.java.swing.plaf.windows.TMSchema.Part;
       
    38 import static com.sun.java.swing.plaf.windows.TMSchema.State;
       
    39 import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
       
    40 
       
    41 import sun.swing.DefaultLookup;
       
    42 import sun.swing.StringUIClientPropertyKey;
       
    43 
       
    44 import com.sun.java.swing.plaf.windows.WindowsBorders.DashedBorder;
       
    45 
       
    46 /**
       
    47  * Windows combo box.
       
    48  * <p>
       
    49  * <strong>Warning:</strong>
       
    50  * Serialized objects of this class will not be compatible with
       
    51  * future Swing releases.  The current serialization support is appropriate
       
    52  * for short term storage or RMI between applications running the same
       
    53  * version of Swing.  A future release of Swing will provide support for
       
    54  * long term persistence.
       
    55  *
       
    56  * @author Tom Santos
       
    57  * @author Igor Kushnirskiy
       
    58  */
       
    59 
       
    60 public class WindowsComboBoxUI extends BasicComboBoxUI {
       
    61 
       
    62     private static final MouseListener rolloverListener =
       
    63         new MouseAdapter() {
       
    64             private void handleRollover(MouseEvent e, boolean isRollover) {
       
    65                 JComboBox<?> comboBox = getComboBox(e);
       
    66                 WindowsComboBoxUI comboBoxUI = getWindowsComboBoxUI(e);
       
    67                 if (comboBox == null || comboBoxUI == null) {
       
    68                     return;
       
    69                 }
       
    70                 if (! comboBox.isEditable()) {
       
    71                     //mouse over editable ComboBox does not switch rollover
       
    72                     //for the arrow button
       
    73                     ButtonModel m = null;
       
    74                     if (comboBoxUI.arrowButton != null) {
       
    75                         m = comboBoxUI.arrowButton.getModel();
       
    76                     }
       
    77                     if (m != null ) {
       
    78                         m.setRollover(isRollover);
       
    79                     }
       
    80                 }
       
    81                 comboBoxUI.isRollover = isRollover;
       
    82                 comboBox.repaint();
       
    83             }
       
    84 
       
    85             public void mouseEntered(MouseEvent e) {
       
    86                 handleRollover(e, true);
       
    87             }
       
    88 
       
    89             public void mouseExited(MouseEvent e) {
       
    90                 handleRollover(e, false);
       
    91             }
       
    92 
       
    93             private JComboBox<?> getComboBox(MouseEvent event) {
       
    94                 Object source = event.getSource();
       
    95                 JComboBox<?> rv = null;
       
    96                 if (source instanceof JComboBox) {
       
    97                     rv = (JComboBox) source;
       
    98                 } else if (source instanceof XPComboBoxButton) {
       
    99                     rv = ((XPComboBoxButton) source)
       
   100                         .getWindowsComboBoxUI().comboBox;
       
   101                 } else if (source instanceof JTextField &&
       
   102                         ((JTextField) source).getParent() instanceof JComboBox) {
       
   103                     rv = (JComboBox) ((JTextField) source).getParent();
       
   104                 }
       
   105                 return rv;
       
   106             }
       
   107 
       
   108             private WindowsComboBoxUI getWindowsComboBoxUI(MouseEvent event) {
       
   109                 JComboBox<?> comboBox = getComboBox(event);
       
   110                 WindowsComboBoxUI rv = null;
       
   111                 if (comboBox != null
       
   112                     && comboBox.getUI() instanceof WindowsComboBoxUI) {
       
   113                     rv = (WindowsComboBoxUI) comboBox.getUI();
       
   114                 }
       
   115                 return rv;
       
   116             }
       
   117 
       
   118         };
       
   119     private boolean isRollover = false;
       
   120 
       
   121     private static final PropertyChangeListener componentOrientationListener =
       
   122         new PropertyChangeListener() {
       
   123             public void propertyChange(PropertyChangeEvent e) {
       
   124                 String propertyName = e.getPropertyName();
       
   125                 Object source = null;
       
   126                 if ("componentOrientation" == propertyName
       
   127                     && (source = e.getSource()) instanceof JComboBox
       
   128                     && ((JComboBox) source).getUI() instanceof
       
   129                       WindowsComboBoxUI) {
       
   130                     JComboBox<?> comboBox = (JComboBox) source;
       
   131                     WindowsComboBoxUI comboBoxUI = (WindowsComboBoxUI) comboBox.getUI();
       
   132                     if (comboBoxUI.arrowButton instanceof XPComboBoxButton) {
       
   133                         ((XPComboBoxButton) comboBoxUI.arrowButton).setPart(
       
   134                                     (comboBox.getComponentOrientation() ==
       
   135                                        ComponentOrientation.RIGHT_TO_LEFT)
       
   136                                     ? Part.CP_DROPDOWNBUTTONLEFT
       
   137                                     : Part.CP_DROPDOWNBUTTONRIGHT);
       
   138                             }
       
   139                         }
       
   140                     }
       
   141                 };
       
   142 
       
   143     public static ComponentUI createUI(JComponent c) {
       
   144         return new WindowsComboBoxUI();
       
   145     }
       
   146 
       
   147     public void installUI( JComponent c ) {
       
   148         super.installUI( c );
       
   149         isRollover = false;
       
   150         comboBox.setRequestFocusEnabled( true );
       
   151         if (XPStyle.getXP() != null && arrowButton != null) {
       
   152             //we can not do it in installListeners because arrowButton
       
   153             //is initialized after installListeners is invoked
       
   154             comboBox.addMouseListener(rolloverListener);
       
   155             arrowButton.addMouseListener(rolloverListener);
       
   156             // set empty border as default to see vista animated border
       
   157             comboBox.setBorder(new EmptyBorder(0,0,0,0));
       
   158         }
       
   159     }
       
   160 
       
   161     public void uninstallUI(JComponent c ) {
       
   162         comboBox.removeMouseListener(rolloverListener);
       
   163         if(arrowButton != null) {
       
   164             arrowButton.removeMouseListener(rolloverListener);
       
   165         }
       
   166         super.uninstallUI( c );
       
   167     }
       
   168 
       
   169     /**
       
   170      * {@inheritDoc}
       
   171      * @since 1.6
       
   172      */
       
   173     @Override
       
   174     protected void installListeners() {
       
   175         super.installListeners();
       
   176         XPStyle xp = XPStyle.getXP();
       
   177         //button glyph for LTR and RTL combobox might differ
       
   178         if (xp != null
       
   179               && xp.isSkinDefined(comboBox, Part.CP_DROPDOWNBUTTONRIGHT)) {
       
   180             comboBox.addPropertyChangeListener("componentOrientation",
       
   181                                                componentOrientationListener);
       
   182         }
       
   183     }
       
   184 
       
   185     /**
       
   186      * {@inheritDoc}
       
   187      * @since 1.6
       
   188      */
       
   189     @Override
       
   190     protected void uninstallListeners() {
       
   191         super.uninstallListeners();
       
   192         comboBox.removePropertyChangeListener("componentOrientation",
       
   193                                               componentOrientationListener);
       
   194     }
       
   195 
       
   196     /**
       
   197      * {@inheritDoc}
       
   198      * @since 1.6
       
   199      */
       
   200     protected void configureEditor() {
       
   201         super.configureEditor();
       
   202         if (XPStyle.getXP() != null) {
       
   203             editor.addMouseListener(rolloverListener);
       
   204         }
       
   205     }
       
   206 
       
   207     /**
       
   208      * {@inheritDoc}
       
   209      * @since 1.6
       
   210      */
       
   211     protected void unconfigureEditor() {
       
   212         super.unconfigureEditor();
       
   213         editor.removeMouseListener(rolloverListener);
       
   214     }
       
   215 
       
   216     /**
       
   217      * {@inheritDoc}
       
   218      * @since 1.6
       
   219      */
       
   220     public void paint(Graphics g, JComponent c) {
       
   221         if (XPStyle.getXP() != null) {
       
   222             paintXPComboBoxBackground(g, c);
       
   223         }
       
   224         super.paint(g, c);
       
   225     }
       
   226 
       
   227     State getXPComboBoxState(JComponent c) {
       
   228         State state = State.NORMAL;
       
   229         if (!c.isEnabled()) {
       
   230             state = State.DISABLED;
       
   231         } else if (isPopupVisible(comboBox)) {
       
   232             state = State.PRESSED;
       
   233         } else if (comboBox.isEditable()
       
   234                 && comboBox.getEditor().getEditorComponent().isFocusOwner()) {
       
   235             state = State.PRESSED;
       
   236         } else if (isRollover) {
       
   237             state = State.HOT;
       
   238         }
       
   239         return state;
       
   240     }
       
   241 
       
   242     private void paintXPComboBoxBackground(Graphics g, JComponent c) {
       
   243         XPStyle xp = XPStyle.getXP();
       
   244         if (xp == null) {
       
   245             return;
       
   246         }
       
   247         State state = getXPComboBoxState(c);
       
   248         Skin skin = null;
       
   249         if (! comboBox.isEditable()
       
   250               && xp.isSkinDefined(c, Part.CP_READONLY)) {
       
   251             skin = xp.getSkin(c, Part.CP_READONLY);
       
   252         }
       
   253         if (skin == null) {
       
   254             skin = xp.getSkin(c, Part.CP_BORDER);
       
   255         }
       
   256         skin.paintSkin(g, 0, 0, c.getWidth(), c.getHeight(), state);
       
   257     }
       
   258 
       
   259     /**
       
   260      * If necessary paints the currently selected item.
       
   261      *
       
   262      * @param g Graphics to paint to
       
   263      * @param bounds Region to paint current value to
       
   264      * @param hasFocus whether or not the JComboBox has focus
       
   265      * @throws NullPointerException if any of the arguments are null.
       
   266      * @since 1.5
       
   267      */
       
   268     public void paintCurrentValue(Graphics g, Rectangle bounds,
       
   269                                   boolean hasFocus) {
       
   270         XPStyle xp = XPStyle.getXP();
       
   271         if ( xp != null) {
       
   272             bounds.x += 2;
       
   273             bounds.y += 2;
       
   274             bounds.width -= 4;
       
   275             bounds.height -= 4;
       
   276         } else {
       
   277             bounds.x += 1;
       
   278             bounds.y += 1;
       
   279             bounds.width -= 2;
       
   280             bounds.height -= 2;
       
   281         }
       
   282         if (! comboBox.isEditable()
       
   283             && xp != null
       
   284             && xp.isSkinDefined(comboBox, Part.CP_READONLY)) {
       
   285             // On vista for READNLY ComboBox
       
   286             // color for currentValue is the same as for any other item
       
   287 
       
   288             // mostly copied from javax.swing.plaf.basic.BasicComboBoxUI.paintCurrentValue
       
   289             ListCellRenderer<Object> renderer = comboBox.getRenderer();
       
   290             Component c;
       
   291             if ( hasFocus && !isPopupVisible(comboBox) ) {
       
   292                 c = renderer.getListCellRendererComponent(
       
   293                         listBox,
       
   294                         comboBox.getSelectedItem(),
       
   295                         -1,
       
   296                         true,
       
   297                         false );
       
   298             } else {
       
   299                 c = renderer.getListCellRendererComponent(
       
   300                         listBox,
       
   301                         comboBox.getSelectedItem(),
       
   302                         -1,
       
   303                         false,
       
   304                         false );
       
   305             }
       
   306             c.setFont(comboBox.getFont());
       
   307             if ( comboBox.isEnabled() ) {
       
   308                 c.setForeground(comboBox.getForeground());
       
   309                 c.setBackground(comboBox.getBackground());
       
   310             } else {
       
   311                 c.setForeground(DefaultLookup.getColor(
       
   312                          comboBox, this, "ComboBox.disabledForeground", null));
       
   313                 c.setBackground(DefaultLookup.getColor(
       
   314                          comboBox, this, "ComboBox.disabledBackground", null));
       
   315             }
       
   316             boolean shouldValidate = false;
       
   317             if (c instanceof JPanel)  {
       
   318                 shouldValidate = true;
       
   319             }
       
   320             currentValuePane.paintComponent(g, c, comboBox, bounds.x, bounds.y,
       
   321                                             bounds.width, bounds.height, shouldValidate);
       
   322 
       
   323         } else {
       
   324             super.paintCurrentValue(g, bounds, hasFocus);
       
   325         }
       
   326     }
       
   327 
       
   328     /**
       
   329      * {@inheritDoc}
       
   330      * @since 1.6
       
   331      */
       
   332     public void paintCurrentValueBackground(Graphics g, Rectangle bounds,
       
   333                                             boolean hasFocus) {
       
   334         if (XPStyle.getXP() == null) {
       
   335             super.paintCurrentValueBackground(g, bounds, hasFocus);
       
   336         }
       
   337     }
       
   338 
       
   339     public Dimension getMinimumSize( JComponent c ) {
       
   340         Dimension d = super.getMinimumSize(c);
       
   341         if (XPStyle.getXP() != null) {
       
   342             d.width += 7;
       
   343             boolean isEditable = false;
       
   344             if (c instanceof JComboBox) {
       
   345                 isEditable = ((JComboBox) c).isEditable();
       
   346             }
       
   347             d.height += isEditable ? 4 : 6;
       
   348         } else {
       
   349             d.width += 4;
       
   350             d.height += 2;
       
   351         }
       
   352         return d;
       
   353     }
       
   354 
       
   355     /**
       
   356      * Creates a layout manager for managing the components which make up the
       
   357      * combo box.
       
   358      *
       
   359      * @return an instance of a layout manager
       
   360      */
       
   361     protected LayoutManager createLayoutManager() {
       
   362         return new BasicComboBoxUI.ComboBoxLayoutManager() {
       
   363             public void layoutContainer(Container parent) {
       
   364                 super.layoutContainer(parent);
       
   365 
       
   366                 if (XPStyle.getXP() != null && arrowButton != null) {
       
   367                     Dimension d = parent.getSize();
       
   368                     Insets insets = getInsets();
       
   369                     int buttonWidth = arrowButton.getPreferredSize().width;
       
   370                     arrowButton.setBounds(WindowsGraphicsUtils.isLeftToRight((JComboBox)parent)
       
   371                                           ? (d.width - insets.right - buttonWidth)
       
   372                                           : insets.left,
       
   373                                           insets.top,
       
   374                                           buttonWidth, d.height - insets.top - insets.bottom);
       
   375                 }
       
   376             }
       
   377         };
       
   378     }
       
   379 
       
   380     protected void installKeyboardActions() {
       
   381         super.installKeyboardActions();
       
   382     }
       
   383 
       
   384     protected ComboPopup createPopup() {
       
   385         return new WinComboPopUp(comboBox);
       
   386     }
       
   387 
       
   388     /**
       
   389      * Creates the default editor that will be used in editable combo boxes.
       
   390      * A default editor will be used only if an editor has not been
       
   391      * explicitly set with <code>setEditor</code>.
       
   392      *
       
   393      * @return a <code>ComboBoxEditor</code> used for the combo box
       
   394      * @see javax.swing.JComboBox#setEditor
       
   395      */
       
   396     protected ComboBoxEditor createEditor() {
       
   397         return new WindowsComboBoxEditor();
       
   398     }
       
   399 
       
   400     /**
       
   401      * {@inheritDoc}
       
   402      * @since 1.6
       
   403      */
       
   404     @Override
       
   405     protected ListCellRenderer<Object> createRenderer() {
       
   406         XPStyle xp = XPStyle.getXP();
       
   407         if (xp != null && xp.isSkinDefined(comboBox, Part.CP_READONLY)) {
       
   408             return new WindowsComboBoxRenderer();
       
   409         } else {
       
   410             return super.createRenderer();
       
   411         }
       
   412     }
       
   413 
       
   414     /**
       
   415      * Creates an button which will be used as the control to show or hide
       
   416      * the popup portion of the combo box.
       
   417      *
       
   418      * @return a button which represents the popup control
       
   419      */
       
   420     protected JButton createArrowButton() {
       
   421         XPStyle xp = XPStyle.getXP();
       
   422         if (xp != null) {
       
   423             return new XPComboBoxButton(xp);
       
   424         } else {
       
   425             return super.createArrowButton();
       
   426         }
       
   427     }
       
   428 
       
   429     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   430     private class XPComboBoxButton extends XPStyle.GlyphButton {
       
   431         private State prevState = null;
       
   432 
       
   433         public XPComboBoxButton(XPStyle xp) {
       
   434             super(comboBox,
       
   435                   (! xp.isSkinDefined(comboBox, Part.CP_DROPDOWNBUTTONRIGHT))
       
   436                    ? Part.CP_DROPDOWNBUTTON
       
   437                    : (comboBox.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
       
   438                      ? Part.CP_DROPDOWNBUTTONLEFT
       
   439                      : Part.CP_DROPDOWNBUTTONRIGHT
       
   440                   );
       
   441             setRequestFocusEnabled(false);
       
   442         }
       
   443 
       
   444         @Override
       
   445         protected State getState() {
       
   446             State rv;
       
   447 
       
   448             getModel().setPressed(comboBox.isPopupVisible());
       
   449 
       
   450             rv = super.getState();
       
   451             XPStyle xp = XPStyle.getXP();
       
   452             if (rv != State.DISABLED
       
   453                     && comboBox != null && ! comboBox.isEditable()
       
   454                     && xp != null && xp.isSkinDefined(comboBox,
       
   455                             Part.CP_DROPDOWNBUTTONRIGHT)) {
       
   456                 /*
       
   457                  * for non editable ComboBoxes Vista seems to have the
       
   458                  * same glyph for all non DISABLED states
       
   459                  */
       
   460                 rv = State.NORMAL;
       
   461             }
       
   462             if (rv == State.NORMAL && (prevState == State.HOT || prevState == State.PRESSED)) {
       
   463                 /*
       
   464                  * State NORMAL of combobox button cannot overpaint states HOT or PRESSED
       
   465                  * Therefore HOT state must be painted from alpha 1 to 0 and not as usual that
       
   466                  * NORMAL state is painted from alpha 0 to alpha 1.
       
   467                  */
       
   468                 skin.switchStates(true);
       
   469             }
       
   470             if (rv != prevState) {
       
   471                 prevState = rv;
       
   472             }
       
   473 
       
   474             return rv;
       
   475         }
       
   476 
       
   477         public Dimension getPreferredSize() {
       
   478             return new Dimension(17, 21);
       
   479         }
       
   480 
       
   481         void setPart(Part part) {
       
   482             setPart(comboBox, part);
       
   483         }
       
   484 
       
   485         WindowsComboBoxUI getWindowsComboBoxUI() {
       
   486             return WindowsComboBoxUI.this;
       
   487         }
       
   488     }
       
   489 
       
   490 
       
   491     /**
       
   492      * Subclassed to add Windows specific Key Bindings.
       
   493      * This class is now obsolete and doesn't do anything.
       
   494      * Only included for backwards API compatibility.
       
   495      * Do not call or override.
       
   496      *
       
   497      * @deprecated As of Java 2 platform v1.4.
       
   498      */
       
   499     @Deprecated
       
   500     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   501     protected class WindowsComboPopup extends BasicComboPopup {
       
   502 
       
   503         public WindowsComboPopup( JComboBox<Object> cBox ) {
       
   504             super( cBox );
       
   505         }
       
   506 
       
   507         protected KeyListener createKeyListener() {
       
   508             return new InvocationKeyHandler();
       
   509         }
       
   510 
       
   511         protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
       
   512             protected InvocationKeyHandler() {
       
   513                 WindowsComboPopup.this.super();
       
   514             }
       
   515         }
       
   516     }
       
   517 
       
   518     @SuppressWarnings("serial") // Same-version serialization only
       
   519     protected class WinComboPopUp extends BasicComboPopup {
       
   520         private Skin listBoxBorder = null;
       
   521         private XPStyle xp;
       
   522 
       
   523         public WinComboPopUp(JComboBox<Object> combo) {
       
   524             super(combo);
       
   525             xp = XPStyle.getXP();
       
   526             if (xp != null && xp.isSkinDefined(combo, Part.LBCP_BORDER_NOSCROLL)) {
       
   527                 this.listBoxBorder = new Skin(combo, Part.LBCP_BORDER_NOSCROLL);
       
   528                 this.setBorder(new EmptyBorder(1,1,1,1));
       
   529             }
       
   530         }
       
   531 
       
   532         protected KeyListener createKeyListener() {
       
   533             return new InvocationKeyHandler();
       
   534         }
       
   535 
       
   536         protected class InvocationKeyHandler extends BasicComboPopup.InvocationKeyHandler {
       
   537             protected InvocationKeyHandler() {
       
   538                 WinComboPopUp.this.super();
       
   539             }
       
   540         }
       
   541 
       
   542         protected void paintComponent(Graphics g) {
       
   543             super.paintComponent(g);
       
   544             if (this.listBoxBorder != null) {
       
   545                 this.listBoxBorder.paintSkinRaw(g, this.getX(), this.getY(),
       
   546                         this.getWidth(), this.getHeight(), State.HOT);
       
   547             }
       
   548         }
       
   549     }
       
   550 
       
   551 
       
   552     /**
       
   553      * Subclassed to highlight selected item in an editable combo box.
       
   554      */
       
   555     public static class WindowsComboBoxEditor
       
   556         extends BasicComboBoxEditor.UIResource {
       
   557 
       
   558         /**
       
   559          * {@inheritDoc}
       
   560          * @since 1.6
       
   561          */
       
   562         protected JTextField createEditorComponent() {
       
   563             JTextField editor = super.createEditorComponent();
       
   564             Border border = (Border)UIManager.get("ComboBox.editorBorder");
       
   565 
       
   566             if (border != null) {
       
   567                 editor.setBorder(border);
       
   568             }
       
   569             editor.setOpaque(false);
       
   570             return editor;
       
   571         }
       
   572 
       
   573         public void setItem(Object item) {
       
   574             super.setItem(item);
       
   575             Object focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
       
   576             if ((focus == editor) || (focus == editor.getParent())) {
       
   577                 editor.selectAll();
       
   578             }
       
   579         }
       
   580     }
       
   581 
       
   582     /**
       
   583      * Subclassed to set opacity {@code false} on the renderer
       
   584      * and to show border for focused cells.
       
   585      */
       
   586     @SuppressWarnings("serial") // Superclass is not serializable across versions
       
   587     private static class WindowsComboBoxRenderer
       
   588           extends BasicComboBoxRenderer.UIResource {
       
   589         private static final Object BORDER_KEY
       
   590             = new StringUIClientPropertyKey("BORDER_KEY");
       
   591         private static final Border NULL_BORDER = new EmptyBorder(0, 0, 0, 0);
       
   592 
       
   593         // Create own version of DashedBorder with more space on left side
       
   594         private class WindowsComboBoxDashedBorder extends DashedBorder {
       
   595 
       
   596             public WindowsComboBoxDashedBorder(Color color, int thickness) {
       
   597                 super(color, thickness);
       
   598             }
       
   599 
       
   600             public WindowsComboBoxDashedBorder(Color color) {
       
   601                 super(color);
       
   602             }
       
   603 
       
   604             @Override
       
   605             public Insets getBorderInsets(Component c, Insets i) {
       
   606                 return new Insets(0,2,0,0);
       
   607             }
       
   608         }
       
   609 
       
   610         public WindowsComboBoxRenderer() {
       
   611             super();
       
   612 
       
   613             // correct space on the left side of text items in the combo popup list
       
   614             Insets i = getBorder().getBorderInsets(this);
       
   615             setBorder(new EmptyBorder(0, 2, 0, i.right));
       
   616         }
       
   617         /**
       
   618          * {@inheritDoc}
       
   619          */
       
   620         @Override
       
   621         public Component getListCellRendererComponent(
       
   622                                                  JList<?> list,
       
   623                                                  Object value,
       
   624                                                  int index,
       
   625                                                  boolean isSelected,
       
   626                                                  boolean cellHasFocus) {
       
   627             Component rv =
       
   628                 super.getListCellRendererComponent(list, value, index,
       
   629                                                    isSelected, cellHasFocus);
       
   630             if (rv instanceof JComponent) {
       
   631                 JComponent component = (JComponent) rv;
       
   632                 if (index == -1 && isSelected) {
       
   633                     Border border = component.getBorder();
       
   634                     Border dashedBorder =
       
   635                         new WindowsComboBoxDashedBorder(list.getForeground());
       
   636                     component.setBorder(dashedBorder);
       
   637                     //store current border in client property if needed
       
   638                     if (component.getClientProperty(BORDER_KEY) == null) {
       
   639                         component.putClientProperty(BORDER_KEY,
       
   640                                        (border == null) ? NULL_BORDER : border);
       
   641                     }
       
   642                 } else {
       
   643                     if (component.getBorder() instanceof
       
   644                           WindowsBorders.DashedBorder) {
       
   645                         Object storedBorder = component.getClientProperty(BORDER_KEY);
       
   646                         if (storedBorder instanceof Border) {
       
   647                             component.setBorder(
       
   648                                 (storedBorder == NULL_BORDER) ? null
       
   649                                     : (Border) storedBorder);
       
   650                         }
       
   651                         component.putClientProperty(BORDER_KEY, null);
       
   652                     }
       
   653                 }
       
   654                 if (index == -1) {
       
   655                     component.setOpaque(false);
       
   656                     component.setForeground(list.getForeground());
       
   657                 } else {
       
   658                     component.setOpaque(true);
       
   659                 }
       
   660             }
       
   661             return rv;
       
   662         }
       
   663 
       
   664     }
       
   665 }