Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2011, 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 javax.swing.plaf.basic;
   27   
   28   import java.awt;
   29   import java.awt.event;
   30   import javax.swing;
   31   import javax.accessibility;
   32   import javax.swing.plaf;
   33   import javax.swing.text;
   34   import javax.swing.event;
   35   import java.beans.PropertyChangeListener;
   36   import java.beans.PropertyChangeEvent;
   37   import sun.awt.AppContext;
   38   import sun.swing.DefaultLookup;
   39   import sun.swing.UIAction;
   40   
   41   /**
   42    * Basic UI implementation for JComboBox.
   43    * <p>
   44    * The combo box is a compound component which means that it is an agregate of
   45    * many simpler components. This class creates and manages the listeners
   46    * on the combo box and the combo box model. These listeners update the user
   47    * interface in response to changes in the properties and state of the combo box.
   48    * <p>
   49    * All event handling is handled by listener classes created with the
   50    * <code>createxxxListener()</code> methods and internal classes.
   51    * You can change the behavior of this class by overriding the
   52    * <code>createxxxListener()</code> methods and supplying your own
   53    * event listeners or subclassing from the ones supplied in this class.
   54    * <p>
   55    * For adding specific actions,
   56    * overide <code>installKeyboardActions</code> to add actions in response to
   57    * KeyStroke bindings. See the article <a href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">Keyboard Bindings in Swing</a>
   58    * at <a href="http://java.sun.com/products/jfc/tsc"><em>The Swing Connection</em></a>.
   59    *
   60    * @author Arnaud Weber
   61    * @author Tom Santos
   62    * @author Mark Davidson
   63    */
   64   public class BasicComboBoxUI extends ComboBoxUI {
   65       protected JComboBox comboBox;
   66       /**
   67        * This protected field is implementation specific. Do not access directly
   68        * or override.
   69        */
   70       protected boolean   hasFocus = false;
   71   
   72       // Control the selection behavior of the JComboBox when it is used
   73       // in the JTable DefaultCellEditor.
   74       private boolean isTableCellEditor = false;
   75       private static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor";
   76   
   77       // This list is for drawing the current item in the combo box.
   78       protected JList   listBox;
   79   
   80       // Used to render the currently selected item in the combo box.
   81       // It doesn't have anything to do with the popup's rendering.
   82       protected CellRendererPane currentValuePane = new CellRendererPane();
   83   
   84       // The implementation of ComboPopup that is used to show the popup.
   85       protected ComboPopup popup;
   86   
   87       // The Component that the ComboBoxEditor uses for editing
   88       protected Component editor;
   89   
   90       // The arrow button that invokes the popup.
   91       protected JButton   arrowButton;
   92   
   93       // Listeners that are attached to the JComboBox
   94       /**
   95        * This protected field is implementation specific. Do not access directly
   96        * or override. Override the listener construction method instead.
   97        *
   98        * @see #createKeyListener
   99        */
  100       protected KeyListener keyListener;
  101       /**
  102        * This protected field is implementation specific. Do not access directly
  103        * or override. Override the listener construction method instead.
  104        *
  105        * @see #createFocusListener
  106        */
  107       protected FocusListener focusListener;
  108       /**
  109        * This protected field is implementation specific. Do not access directly
  110        * or override. Override the listener construction method instead.
  111        *
  112        * @see #createPropertyChangeListener
  113        */
  114       protected PropertyChangeListener propertyChangeListener;
  115   
  116       /**
  117        * This protected field is implementation specific. Do not access directly
  118        * or override. Override the listener construction method instead.
  119        *
  120        * @see #createItemListener
  121        */
  122       protected ItemListener itemListener;
  123   
  124       // Listeners that the ComboPopup produces.
  125       protected MouseListener popupMouseListener;
  126       protected MouseMotionListener popupMouseMotionListener;
  127       protected KeyListener popupKeyListener;
  128   
  129       // This is used for knowing when to cache the minimum preferred size.
  130       // If the data in the list changes, the cached value get marked for recalc.
  131       // Added to the current JComboBox model
  132       /**
  133        * This protected field is implementation specific. Do not access directly
  134        * or override. Override the listener construction method instead.
  135        *
  136        * @see #createListDataListener
  137        */
  138       protected ListDataListener listDataListener;
  139   
  140       /**
  141        * Implements all the Listeners needed by this class, all existing
  142        * listeners redirect to it.
  143        */
  144       private Handler handler;
  145   
  146       /**
  147        * The time factor to treate the series of typed alphanumeric key
  148        * as prefix for first letter navigation.
  149        */
  150       private long timeFactor = 1000L;
  151   
  152       /**
  153        * This is tricky, this variables is needed for DefaultKeySelectionManager
  154        * to take into account time factor.
  155        */
  156       private long lastTime = 0L;
  157       private long time = 0L;
  158   
  159       /**
  160        * The default key selection manager
  161        */
  162       JComboBox.KeySelectionManager keySelectionManager;
  163   
  164       // Flag for recalculating the minimum preferred size.
  165       protected boolean isMinimumSizeDirty = true;
  166   
  167       // Cached minimum preferred size.
  168       protected Dimension cachedMinimumSize = new Dimension( 0, 0 );
  169   
  170       // Flag for calculating the display size
  171       private boolean isDisplaySizeDirty = true;
  172   
  173       // Cached the size that the display needs to render the largest item
  174       private Dimension cachedDisplaySize = new Dimension( 0, 0 );
  175   
  176       // Key used for lookup of the DefaultListCellRenderer in the AppContext.
  177       private static final Object COMBO_UI_LIST_CELL_RENDERER_KEY =
  178                           new StringBuffer("DefaultListCellRendererKey");
  179   
  180       static final StringBuffer HIDE_POPUP_KEY
  181                     = new StringBuffer("HidePopupKey");
  182   
  183       /**
  184        * Whether or not all cells have the same baseline.
  185        */
  186       private boolean sameBaseline;
  187   
  188       /**
  189        * Indicates whether or not the combo box button should be square.
  190        * If square, then the width and height are equal, and are both set to
  191        * the height of the combo minus appropriate insets.
  192        *
  193        * @since 1.7
  194        */
  195       protected boolean squareButton = true;
  196   
  197       /**
  198        * If specified, these insets act as padding around the cell renderer when
  199        * laying out and painting the "selected" item in the combo box. These
  200        * insets add to those specified by the cell renderer.
  201        *
  202        * @since 1.7
  203        */
  204       protected Insets padding;
  205   
  206       // Used for calculating the default size.
  207       private static ListCellRenderer getDefaultListCellRenderer() {
  208           ListCellRenderer renderer = (ListCellRenderer)AppContext.
  209                            getAppContext().get(COMBO_UI_LIST_CELL_RENDERER_KEY);
  210   
  211           if (renderer == null) {
  212               renderer = new DefaultListCellRenderer();
  213               AppContext.getAppContext().put(COMBO_UI_LIST_CELL_RENDERER_KEY,
  214                                              new DefaultListCellRenderer());
  215           }
  216           return renderer;
  217       }
  218   
  219       /**
  220        * Populates ComboBox's actions.
  221        */
  222       static void loadActionMap(LazyActionMap map) {
  223           map.put(new Actions(Actions.HIDE));
  224           map.put(new Actions(Actions.PAGE_DOWN));
  225           map.put(new Actions(Actions.PAGE_UP));
  226           map.put(new Actions(Actions.HOME));
  227           map.put(new Actions(Actions.END));
  228           map.put(new Actions(Actions.DOWN));
  229           map.put(new Actions(Actions.DOWN_2));
  230           map.put(new Actions(Actions.TOGGLE));
  231           map.put(new Actions(Actions.TOGGLE_2));
  232           map.put(new Actions(Actions.UP));
  233           map.put(new Actions(Actions.UP_2));
  234           map.put(new Actions(Actions.ENTER));
  235       }
  236   
  237       //========================
  238       // begin UI Initialization
  239       //
  240   
  241       public static ComponentUI createUI(JComponent c) {
  242           return new BasicComboBoxUI();
  243       }
  244   
  245       @Override
  246       public void installUI( JComponent c ) {
  247           isMinimumSizeDirty = true;
  248   
  249           comboBox = (JComboBox)c;
  250           installDefaults();
  251           popup = createPopup();
  252           listBox = popup.getList();
  253   
  254           // Is this combo box a cell editor?
  255           Boolean inTable = (Boolean)c.getClientProperty(IS_TABLE_CELL_EDITOR );
  256           if (inTable != null) {
  257               isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
  258           }
  259   
  260           if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
  261               comboBox.setRenderer( createRenderer() );
  262           }
  263   
  264           if ( comboBox.getEditor() == null || comboBox.getEditor() instanceof UIResource ) {
  265               comboBox.setEditor( createEditor() );
  266           }
  267   
  268           installListeners();
  269           installComponents();
  270   
  271           comboBox.setLayout( createLayoutManager() );
  272   
  273           comboBox.setRequestFocusEnabled( true );
  274   
  275           installKeyboardActions();
  276   
  277           comboBox.putClientProperty("doNotCancelPopup", HIDE_POPUP_KEY);
  278   
  279           if (keySelectionManager == null || keySelectionManager instanceof UIResource) {
  280               keySelectionManager = new DefaultKeySelectionManager();
  281           }
  282           comboBox.setKeySelectionManager(keySelectionManager);
  283       }
  284   
  285       @Override
  286       public void uninstallUI( JComponent c ) {
  287           setPopupVisible( comboBox, false);
  288           popup.uninstallingUI();
  289   
  290           uninstallKeyboardActions();
  291   
  292           comboBox.setLayout( null );
  293   
  294           uninstallComponents();
  295           uninstallListeners();
  296           uninstallDefaults();
  297   
  298           if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
  299               comboBox.setRenderer( null );
  300           }
  301   
  302           ComboBoxEditor comboBoxEditor = comboBox.getEditor();
  303           if (comboBoxEditor instanceof UIResource ) {
  304               if (comboBoxEditor.getEditorComponent().hasFocus()) {
  305                   // Leave focus in JComboBox.
  306                   comboBox.requestFocusInWindow();
  307               }
  308               comboBox.setEditor( null );
  309           }
  310   
  311           if (keySelectionManager instanceof UIResource) {
  312               comboBox.setKeySelectionManager(null);
  313           }
  314   
  315           handler = null;
  316           keyListener = null;
  317           focusListener = null;
  318           listDataListener = null;
  319           propertyChangeListener = null;
  320           popup = null;
  321           listBox = null;
  322           comboBox = null;
  323       }
  324   
  325       /**
  326        * Installs the default colors, default font, default renderer, and default
  327        * editor into the JComboBox.
  328        */
  329       protected void installDefaults() {
  330           LookAndFeel.installColorsAndFont( comboBox,
  331                                             "ComboBox.background",
  332                                             "ComboBox.foreground",
  333                                             "ComboBox.font" );
  334           LookAndFeel.installBorder( comboBox, "ComboBox.border" );
  335           LookAndFeel.installProperty( comboBox, "opaque", Boolean.TRUE);
  336   
  337           Long l = (Long)UIManager.get("ComboBox.timeFactor");
  338           timeFactor = l == null ? 1000L : l.longValue();
  339   
  340           //NOTE: this needs to default to true if not specified
  341           Boolean b = (Boolean)UIManager.get("ComboBox.squareButton");
  342           squareButton = b == null ? true : b;
  343   
  344           padding = UIManager.getInsets("ComboBox.padding");
  345       }
  346   
  347       /**
  348        * Creates and installs listeners for the combo box and its model.
  349        * This method is called when the UI is installed.
  350        */
  351       protected void installListeners() {
  352           if ( (itemListener = createItemListener()) != null) {
  353               comboBox.addItemListener( itemListener );
  354           }
  355           if ( (propertyChangeListener = createPropertyChangeListener()) != null ) {
  356               comboBox.addPropertyChangeListener( propertyChangeListener );
  357           }
  358           if ( (keyListener = createKeyListener()) != null ) {
  359               comboBox.addKeyListener( keyListener );
  360           }
  361           if ( (focusListener = createFocusListener()) != null ) {
  362               comboBox.addFocusListener( focusListener );
  363           }
  364           if ((popupMouseListener = popup.getMouseListener()) != null) {
  365               comboBox.addMouseListener( popupMouseListener );
  366           }
  367           if ((popupMouseMotionListener = popup.getMouseMotionListener()) != null) {
  368               comboBox.addMouseMotionListener( popupMouseMotionListener );
  369           }
  370           if ((popupKeyListener = popup.getKeyListener()) != null) {
  371               comboBox.addKeyListener(popupKeyListener);
  372           }
  373   
  374           if ( comboBox.getModel() != null ) {
  375               if ( (listDataListener = createListDataListener()) != null ) {
  376                   comboBox.getModel().addListDataListener( listDataListener );
  377               }
  378           }
  379       }
  380   
  381       /**
  382        * Uninstalls the default colors, default font, default renderer,
  383        * and default editor from the combo box.
  384        */
  385       protected void uninstallDefaults() {
  386           LookAndFeel.installColorsAndFont( comboBox,
  387                                             "ComboBox.background",
  388                                             "ComboBox.foreground",
  389                                             "ComboBox.font" );
  390           LookAndFeel.uninstallBorder( comboBox );
  391       }
  392   
  393       /**
  394        * Removes the installed listeners from the combo box and its model.
  395        * The number and types of listeners removed and in this method should be
  396        * the same that was added in <code>installListeners</code>
  397        */
  398       protected void uninstallListeners() {
  399           if ( keyListener != null ) {
  400               comboBox.removeKeyListener( keyListener );
  401           }
  402           if ( itemListener != null) {
  403               comboBox.removeItemListener( itemListener );
  404           }
  405           if ( propertyChangeListener != null ) {
  406               comboBox.removePropertyChangeListener( propertyChangeListener );
  407           }
  408           if ( focusListener != null) {
  409               comboBox.removeFocusListener( focusListener );
  410           }
  411           if ( popupMouseListener != null) {
  412               comboBox.removeMouseListener( popupMouseListener );
  413           }
  414           if ( popupMouseMotionListener != null) {
  415               comboBox.removeMouseMotionListener( popupMouseMotionListener );
  416           }
  417           if (popupKeyListener != null) {
  418               comboBox.removeKeyListener(popupKeyListener);
  419           }
  420           if ( comboBox.getModel() != null ) {
  421               if ( listDataListener != null ) {
  422                   comboBox.getModel().removeListDataListener( listDataListener );
  423               }
  424           }
  425       }
  426   
  427       /**
  428        * Creates the popup portion of the combo box.
  429        *
  430        * @return an instance of <code>ComboPopup</code>
  431        * @see ComboPopup
  432        */
  433       protected ComboPopup createPopup() {
  434           return new BasicComboPopup( comboBox );
  435       }
  436   
  437       /**
  438        * Creates a <code>KeyListener</code> which will be added to the
  439        * combo box. If this method returns null then it will not be added
  440        * to the combo box.
  441        *
  442        * @return an instance <code>KeyListener</code> or null
  443        */
  444       protected KeyListener createKeyListener() {
  445           return getHandler();
  446       }
  447   
  448       /**
  449        * Creates a <code>FocusListener</code> which will be added to the combo box.
  450        * If this method returns null then it will not be added to the combo box.
  451        *
  452        * @return an instance of a <code>FocusListener</code> or null
  453        */
  454       protected FocusListener createFocusListener() {
  455           return getHandler();
  456       }
  457   
  458       /**
  459        * Creates a list data listener which will be added to the
  460        * <code>ComboBoxModel</code>. If this method returns null then
  461        * it will not be added to the combo box model.
  462        *
  463        * @return an instance of a <code>ListDataListener</code> or null
  464        */
  465       protected ListDataListener createListDataListener() {
  466           return getHandler();
  467       }
  468   
  469       /**
  470        * Creates an <code>ItemListener</code> which will be added to the
  471        * combo box. If this method returns null then it will not
  472        * be added to the combo box.
  473        * <p>
  474        * Subclasses may override this method to return instances of their own
  475        * ItemEvent handlers.
  476        *
  477        * @return an instance of an <code>ItemListener</code> or null
  478        */
  479       protected ItemListener createItemListener() {
  480           return null;
  481       }
  482   
  483       /**
  484        * Creates a <code>PropertyChangeListener</code> which will be added to
  485        * the combo box. If this method returns null then it will not
  486        * be added to the combo box.
  487        *
  488        * @return an instance of a <code>PropertyChangeListener</code> or null
  489        */
  490       protected PropertyChangeListener createPropertyChangeListener() {
  491           return getHandler();
  492       }
  493   
  494       /**
  495        * Creates a layout manager for managing the components which make up the
  496        * combo box.
  497        *
  498        * @return an instance of a layout manager
  499        */
  500       protected LayoutManager createLayoutManager() {
  501           return getHandler();
  502       }
  503   
  504       /**
  505        * Creates the default renderer that will be used in a non-editiable combo
  506        * box. A default renderer will used only if a renderer has not been
  507        * explicitly set with <code>setRenderer</code>.
  508        *
  509        * @return a <code>ListCellRender</code> used for the combo box
  510        * @see javax.swing.JComboBox#setRenderer
  511        */
  512       protected ListCellRenderer createRenderer() {
  513           return new BasicComboBoxRenderer.UIResource();
  514       }
  515   
  516       /**
  517        * Creates the default editor that will be used in editable combo boxes.
  518        * A default editor will be used only if an editor has not been
  519        * explicitly set with <code>setEditor</code>.
  520        *
  521        * @return a <code>ComboBoxEditor</code> used for the combo box
  522        * @see javax.swing.JComboBox#setEditor
  523        */
  524       protected ComboBoxEditor createEditor() {
  525           return new BasicComboBoxEditor.UIResource();
  526       }
  527   
  528       /**
  529        * Returns the shared listener.
  530        */
  531       private Handler getHandler() {
  532           if (handler == null) {
  533               handler = new Handler();
  534           }
  535           return handler;
  536       }
  537   
  538       //
  539       // end UI Initialization
  540       //======================
  541   
  542   
  543       //======================
  544       // begin Inner classes
  545       //
  546   
  547       /**
  548        * This listener checks to see if the key event isn't a navigation key.  If
  549        * it finds a key event that wasn't a navigation key it dispatches it to
  550        * JComboBox.selectWithKeyChar() so that it can do type-ahead.
  551        *
  552        * This public inner class should be treated as protected.
  553        * Instantiate it only within subclasses of
  554        * <code>BasicComboBoxUI</code>.
  555        */
  556       public class KeyHandler extends KeyAdapter {
  557           @Override
  558           public void keyPressed( KeyEvent e ) {
  559               getHandler().keyPressed(e);
  560           }
  561       }
  562   
  563       /**
  564        * This listener hides the popup when the focus is lost.  It also repaints
  565        * when focus is gained or lost.
  566        *
  567        * This public inner class should be treated as protected.
  568        * Instantiate it only within subclasses of
  569        * <code>BasicComboBoxUI</code>.
  570        */
  571       public class FocusHandler implements FocusListener {
  572           public void focusGained( FocusEvent e ) {
  573               getHandler().focusGained(e);
  574           }
  575   
  576           public void focusLost( FocusEvent e ) {
  577               getHandler().focusLost(e);
  578           }
  579       }
  580   
  581       /**
  582        * This listener watches for changes in the
  583        * <code>ComboBoxModel</code>.
  584        * <p>
  585        * This public inner class should be treated as protected.
  586        * Instantiate it only within subclasses of
  587        * <code>BasicComboBoxUI</code>.
  588        *
  589        * @see #createListDataListener
  590        */
  591       public class ListDataHandler implements ListDataListener {
  592           public void contentsChanged( ListDataEvent e ) {
  593               getHandler().contentsChanged(e);
  594           }
  595   
  596           public void intervalAdded( ListDataEvent e ) {
  597               getHandler().intervalAdded(e);
  598           }
  599   
  600           public void intervalRemoved( ListDataEvent e ) {
  601               getHandler().intervalRemoved(e);
  602           }
  603       }
  604   
  605       /**
  606        * This listener watches for changes to the selection in the
  607        * combo box.
  608        * <p>
  609        * This public inner class should be treated as protected.
  610        * Instantiate it only within subclasses of
  611        * <code>BasicComboBoxUI</code>.
  612        *
  613        * @see #createItemListener
  614        */
  615       public class ItemHandler implements ItemListener {
  616           // This class used to implement behavior which is now redundant.
  617           public void itemStateChanged(ItemEvent e) {}
  618       }
  619   
  620       /**
  621        * This listener watches for bound properties that have changed in the
  622        * combo box.
  623        * <p>
  624        * Subclasses which wish to listen to combo box property changes should
  625        * call the superclass methods to ensure that the combo box ui correctly
  626        * handles property changes.
  627        * <p>
  628        * This public inner class should be treated as protected.
  629        * Instantiate it only within subclasses of
  630        * <code>BasicComboBoxUI</code>.
  631        *
  632        * @see #createPropertyChangeListener
  633        */
  634       public class PropertyChangeHandler implements PropertyChangeListener {
  635           public void propertyChange(PropertyChangeEvent e) {
  636               getHandler().propertyChange(e);
  637           }
  638       }
  639   
  640   
  641       // Syncronizes the ToolTip text for the components within the combo box to be the
  642       // same value as the combo box ToolTip text.
  643       private void updateToolTipTextForChildren() {
  644           Component[] children = comboBox.getComponents();
  645           for ( int i = 0; i < children.length; ++i ) {
  646               if ( children[i] instanceof JComponent ) {
  647                   ((JComponent)children[i]).setToolTipText( comboBox.getToolTipText() );
  648               }
  649           }
  650       }
  651   
  652       /**
  653        * This layout manager handles the 'standard' layout of combo boxes.  It puts
  654        * the arrow button to the right and the editor to the left.  If there is no
  655        * editor it still keeps the arrow button to the right.
  656        *
  657        * This public inner class should be treated as protected.
  658        * Instantiate it only within subclasses of
  659        * <code>BasicComboBoxUI</code>.
  660        */
  661       public class ComboBoxLayoutManager implements LayoutManager {
  662           public void addLayoutComponent(String name, Component comp) {}
  663   
  664           public void removeLayoutComponent(Component comp) {}
  665   
  666           public Dimension preferredLayoutSize(Container parent) {
  667               return getHandler().preferredLayoutSize(parent);
  668           }
  669   
  670           public Dimension minimumLayoutSize(Container parent) {
  671               return getHandler().minimumLayoutSize(parent);
  672           }
  673   
  674           public void layoutContainer(Container parent) {
  675               getHandler().layoutContainer(parent);
  676           }
  677       }
  678   
  679       //
  680       // end Inner classes
  681       //====================
  682   
  683   
  684       //===============================
  685       // begin Sub-Component Management
  686       //
  687   
  688       /**
  689        * Creates and initializes the components which make up the
  690        * aggregate combo box. This method is called as part of the UI
  691        * installation process.
  692        */
  693       protected void installComponents() {
  694           arrowButton = createArrowButton();
  695           comboBox.add( arrowButton );
  696   
  697           if (arrowButton != null)  {
  698               configureArrowButton();
  699           }
  700   
  701           if ( comboBox.isEditable() ) {
  702               addEditor();
  703           }
  704   
  705           comboBox.add( currentValuePane );
  706       }
  707   
  708       /**
  709        * The aggregate components which compise the combo box are
  710        * unregistered and uninitialized. This method is called as part of the
  711        * UI uninstallation process.
  712        */
  713       protected void uninstallComponents() {
  714           if ( arrowButton != null ) {
  715               unconfigureArrowButton();
  716           }
  717           if ( editor != null ) {
  718               unconfigureEditor();
  719           }
  720           comboBox.removeAll(); // Just to be safe.
  721           arrowButton = null;
  722       }
  723   
  724       /**
  725        * This public method is implementation specific and should be private.
  726        * do not call or override. To implement a specific editor create a
  727        * custom <code>ComboBoxEditor</code>
  728        *
  729        * @see #createEditor
  730        * @see javax.swing.JComboBox#setEditor
  731        * @see javax.swing.ComboBoxEditor
  732        */
  733       public void addEditor() {
  734           removeEditor();
  735           editor = comboBox.getEditor().getEditorComponent();
  736           if ( editor != null ) {
  737               configureEditor();
  738               comboBox.add(editor);
  739               if(comboBox.isFocusOwner()) {
  740                   // Switch focus to the editor component
  741                   editor.requestFocusInWindow();
  742               }
  743           }
  744       }
  745   
  746       /**
  747        * This public method is implementation specific and should be private.
  748        * do not call or override.
  749        *
  750        * @see #addEditor
  751        */
  752       public void removeEditor() {
  753           if ( editor != null ) {
  754               unconfigureEditor();
  755               comboBox.remove( editor );
  756               editor = null;
  757           }
  758       }
  759   
  760       /**
  761        * This protected method is implementation specific and should be private.
  762        * do not call or override.
  763        *
  764        * @see #addEditor
  765        */
  766       protected void configureEditor() {
  767           // Should be in the same state as the combobox
  768           editor.setEnabled(comboBox.isEnabled());
  769   
  770           editor.setFocusable(comboBox.isFocusable());
  771   
  772           editor.setFont( comboBox.getFont() );
  773   
  774           if (focusListener != null) {
  775               editor.addFocusListener(focusListener);
  776           }
  777   
  778           editor.addFocusListener( getHandler() );
  779   
  780           comboBox.getEditor().addActionListener(getHandler());
  781   
  782           if(editor instanceof JComponent) {
  783               ((JComponent)editor).putClientProperty("doNotCancelPopup",
  784                                                      HIDE_POPUP_KEY);
  785               ((JComponent)editor).setInheritsPopupMenu(true);
  786           }
  787   
  788           comboBox.configureEditor(comboBox.getEditor(),comboBox.getSelectedItem());
  789   
  790           editor.addPropertyChangeListener(propertyChangeListener);
  791       }
  792   
  793       /**
  794        * This protected method is implementation specific and should be private.
  795        * Do not call or override.
  796        *
  797        * @see #addEditor
  798        */
  799       protected void unconfigureEditor() {
  800           if (focusListener != null) {
  801               editor.removeFocusListener(focusListener);
  802           }
  803   
  804           editor.removePropertyChangeListener(propertyChangeListener);
  805           editor.removeFocusListener(getHandler());
  806           comboBox.getEditor().removeActionListener(getHandler());
  807       }
  808   
  809       /**
  810        * This public method is implementation specific and should be private. Do
  811        * not call or override.
  812        *
  813        * @see #createArrowButton
  814        */
  815       public void configureArrowButton() {
  816           if ( arrowButton != null ) {
  817               arrowButton.setEnabled( comboBox.isEnabled() );
  818               arrowButton.setFocusable(comboBox.isFocusable());
  819               arrowButton.setRequestFocusEnabled(false);
  820               arrowButton.addMouseListener( popup.getMouseListener() );
  821               arrowButton.addMouseMotionListener( popup.getMouseMotionListener() );
  822               arrowButton.resetKeyboardActions();
  823               arrowButton.putClientProperty("doNotCancelPopup", HIDE_POPUP_KEY);
  824               arrowButton.setInheritsPopupMenu(true);
  825           }
  826       }
  827   
  828       /**
  829        * This public method is implementation specific and should be private. Do
  830        * not call or override.
  831        *
  832        * @see #createArrowButton
  833        */
  834       public void unconfigureArrowButton() {
  835           if ( arrowButton != null ) {
  836               arrowButton.removeMouseListener( popup.getMouseListener() );
  837               arrowButton.removeMouseMotionListener( popup.getMouseMotionListener() );
  838           }
  839       }
  840   
  841       /**
  842        * Creates a button which will be used as the control to show or hide
  843        * the popup portion of the combo box.
  844        *
  845        * @return a button which represents the popup control
  846        */
  847       protected JButton createArrowButton() {
  848           JButton button = new BasicArrowButton(BasicArrowButton.SOUTH,
  849                                       UIManager.getColor("ComboBox.buttonBackground"),
  850                                       UIManager.getColor("ComboBox.buttonShadow"),
  851                                       UIManager.getColor("ComboBox.buttonDarkShadow"),
  852                                       UIManager.getColor("ComboBox.buttonHighlight"));
  853           button.setName("ComboBox.arrowButton");
  854           return button;
  855       }
  856   
  857       //
  858       // end Sub-Component Management
  859       //===============================
  860   
  861   
  862       //================================
  863       // begin ComboBoxUI Implementation
  864       //
  865   
  866       /**
  867        * Tells if the popup is visible or not.
  868        */
  869       public boolean isPopupVisible( JComboBox c ) {
  870           return popup.isVisible();
  871       }
  872   
  873       /**
  874        * Hides the popup.
  875        */
  876       public void setPopupVisible( JComboBox c, boolean v ) {
  877           if ( v ) {
  878               popup.show();
  879           } else {
  880               popup.hide();
  881           }
  882       }
  883   
  884       /**
  885        * Determines if the JComboBox is focus traversable.  If the JComboBox is editable
  886        * this returns false, otherwise it returns true.
  887        */
  888       public boolean isFocusTraversable( JComboBox c ) {
  889           return !comboBox.isEditable();
  890       }
  891   
  892       //
  893       // end ComboBoxUI Implementation
  894       //==============================
  895   
  896   
  897       //=================================
  898       // begin ComponentUI Implementation
  899       @Override
  900       public void paint( Graphics g, JComponent c ) {
  901           hasFocus = comboBox.hasFocus();
  902           if ( !comboBox.isEditable() ) {
  903               Rectangle r = rectangleForCurrentValue();
  904               paintCurrentValueBackground(g,r,hasFocus);
  905               paintCurrentValue(g,r,hasFocus);
  906           }
  907       }
  908   
  909       @Override
  910       public Dimension getPreferredSize( JComponent c ) {
  911           return getMinimumSize(c);
  912       }
  913   
  914       /**
  915        * The minumum size is the size of the display area plus insets plus the button.
  916        */
  917       @Override
  918       public Dimension getMinimumSize( JComponent c ) {
  919           if ( !isMinimumSizeDirty ) {
  920               return new Dimension(cachedMinimumSize);
  921           }
  922           Dimension size = getDisplaySize();
  923           Insets insets = getInsets();
  924           //calculate the width and height of the button
  925           int buttonHeight = size.height;
  926           int buttonWidth = squareButton ? buttonHeight : arrowButton.getPreferredSize().width;
  927           //adjust the size based on the button width
  928           size.height += insets.top + insets.bottom;
  929           size.width +=  insets.left + insets.right + buttonWidth;
  930   
  931           cachedMinimumSize.setSize( size.width, size.height );
  932           isMinimumSizeDirty = false;
  933   
  934           return new Dimension(size);
  935       }
  936   
  937       @Override
  938       public Dimension getMaximumSize( JComponent c ) {
  939           return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
  940       }
  941   
  942       /**
  943        * Returns the baseline.
  944        *
  945        * @throws NullPointerException {@inheritDoc}
  946        * @throws IllegalArgumentException {@inheritDoc}
  947        * @see javax.swing.JComponent#getBaseline(int, int)
  948        * @since 1.6
  949        */
  950       @Override
  951       public int getBaseline(JComponent c, int width, int height) {
  952           super.getBaseline(c, width, height);
  953           int baseline = -1;
  954           // force sameBaseline to be updated.
  955           getDisplaySize();
  956           if (sameBaseline) {
  957               Insets insets = c.getInsets();
  958               height = height - insets.top - insets.bottom;
  959               if (!comboBox.isEditable()) {
  960                   ListCellRenderer renderer = comboBox.getRenderer();
  961                   if (renderer == null)  {
  962                       renderer = new DefaultListCellRenderer();
  963                   }
  964                   Object value = null;
  965                   Object prototypeValue = comboBox.getPrototypeDisplayValue();
  966                   if (prototypeValue != null)  {
  967                       value = prototypeValue;
  968                   }
  969                   else if (comboBox.getModel().getSize() > 0) {
  970                       // Note, we're assuming the baseline is the same for all
  971                       // cells, if not, this needs to loop through all.
  972                       value = comboBox.getModel().getElementAt(0);
  973                   }
  974                   if (value == null) {
  975                       value = " ";
  976                   } else if (value instanceof String && "".equals(value)) {
  977                       value = " ";
  978                   }
  979                   Component component = renderer.
  980                           getListCellRendererComponent(listBox, value, -1,
  981                                                        false, false);
  982                   if (component instanceof JComponent) {
  983                       component.setFont(comboBox.getFont());
  984                   }
  985                   baseline = component.getBaseline(width, height);
  986               }
  987               else {
  988                   baseline = editor.getBaseline(width, height);
  989               }
  990               if (baseline > 0) {
  991                   baseline += insets.top;
  992               }
  993           }
  994           return baseline;
  995       }
  996   
  997       /**
  998        * Returns an enum indicating how the baseline of the component
  999        * changes as the size changes.
 1000        *
 1001        * @throws NullPointerException {@inheritDoc}
 1002        * @see javax.swing.JComponent#getBaseline(int, int)
 1003        * @since 1.6
 1004        */
 1005       @Override
 1006       public Component.BaselineResizeBehavior getBaselineResizeBehavior(
 1007               JComponent c) {
 1008           super.getBaselineResizeBehavior(c);
 1009           // Force sameBaseline to be updated.
 1010           getDisplaySize();
 1011           if (comboBox.isEditable()) {
 1012               return editor.getBaselineResizeBehavior();
 1013           }
 1014           else if (sameBaseline) {
 1015               ListCellRenderer renderer = comboBox.getRenderer();
 1016               if (renderer == null)  {
 1017                   renderer = new DefaultListCellRenderer();
 1018               }
 1019               Object value = null;
 1020               Object prototypeValue = comboBox.getPrototypeDisplayValue();
 1021               if (prototypeValue != null)  {
 1022                   value = prototypeValue;
 1023               }
 1024               else if (comboBox.getModel().getSize() > 0) {
 1025                   // Note, we're assuming the baseline is the same for all
 1026                   // cells, if not, this needs to loop through all.
 1027                   value = comboBox.getModel().getElementAt(0);
 1028               }
 1029               if (value != null) {
 1030                   Component component = renderer.
 1031                           getListCellRendererComponent(listBox, value, -1,
 1032                                                        false, false);
 1033                   return component.getBaselineResizeBehavior();
 1034               }
 1035           }
 1036           return Component.BaselineResizeBehavior.OTHER;
 1037       }
 1038   
 1039       // This is currently hacky...
 1040       @Override
 1041       public int getAccessibleChildrenCount(JComponent c) {
 1042           if ( comboBox.isEditable() ) {
 1043               return 2;
 1044           }
 1045           else {
 1046               return 1;
 1047           }
 1048       }
 1049   
 1050       // This is currently hacky...
 1051       @Override
 1052       public Accessible getAccessibleChild(JComponent c, int i) {
 1053           // 0 = the popup
 1054           // 1 = the editor
 1055           switch ( i ) {
 1056           case 0:
 1057               if ( popup instanceof Accessible ) {
 1058                   AccessibleContext ac = ((Accessible) popup).getAccessibleContext();
 1059                   ac.setAccessibleParent(comboBox);
 1060                   return(Accessible) popup;
 1061               }
 1062               break;
 1063           case 1:
 1064               if ( comboBox.isEditable()
 1065                    && (editor instanceof Accessible) ) {
 1066                   AccessibleContext ac = ((Accessible) editor).getAccessibleContext();
 1067                   ac.setAccessibleParent(comboBox);
 1068                   return(Accessible) editor;
 1069               }
 1070               break;
 1071           }
 1072           return null;
 1073       }
 1074   
 1075       //
 1076       // end ComponentUI Implementation
 1077       //===============================
 1078   
 1079   
 1080       //======================
 1081       // begin Utility Methods
 1082       //
 1083   
 1084       /**
 1085        * Returns whether or not the supplied keyCode maps to a key that is used for
 1086        * navigation.  This is used for optimizing key input by only passing non-
 1087        * navigation keys to the type-ahead mechanism.  Subclasses should override this
 1088        * if they change the navigation keys.
 1089        */
 1090       protected boolean isNavigationKey( int keyCode ) {
 1091           return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN ||
 1092                  keyCode == KeyEvent.VK_KP_UP || keyCode == KeyEvent.VK_KP_DOWN;
 1093       }
 1094   
 1095       private boolean isNavigationKey(int keyCode, int modifiers) {
 1096           InputMap inputMap = comboBox.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 1097           KeyStroke key = KeyStroke.getKeyStroke(keyCode, modifiers);
 1098   
 1099           if (inputMap != null && inputMap.get(key) != null) {
 1100               return true;
 1101           }
 1102           return false;
 1103       }
 1104   
 1105       /**
 1106        * Selects the next item in the list.  It won't change the selection if the
 1107        * currently selected item is already the last item.
 1108        */
 1109       protected void selectNextPossibleValue() {
 1110           int si;
 1111   
 1112           if ( comboBox.isPopupVisible() ) {
 1113               si = listBox.getSelectedIndex();
 1114           }
 1115           else {
 1116               si = comboBox.getSelectedIndex();
 1117           }
 1118   
 1119           if ( si < comboBox.getModel().getSize() - 1 ) {
 1120               listBox.setSelectedIndex( si + 1 );
 1121               listBox.ensureIndexIsVisible( si + 1 );
 1122               if ( !isTableCellEditor ) {
 1123                   comboBox.setSelectedIndex(si+1);
 1124               }
 1125               comboBox.repaint();
 1126           }
 1127       }
 1128   
 1129       /**
 1130        * Selects the previous item in the list.  It won't change the selection if the
 1131        * currently selected item is already the first item.
 1132        */
 1133       protected void selectPreviousPossibleValue() {
 1134           int si;
 1135   
 1136           if ( comboBox.isPopupVisible() ) {
 1137               si = listBox.getSelectedIndex();
 1138           }
 1139           else {
 1140               si = comboBox.getSelectedIndex();
 1141           }
 1142   
 1143           if ( si > 0 ) {
 1144               listBox.setSelectedIndex( si - 1 );
 1145               listBox.ensureIndexIsVisible( si - 1 );
 1146               if ( !isTableCellEditor ) {
 1147                   comboBox.setSelectedIndex(si-1);
 1148               }
 1149               comboBox.repaint();
 1150           }
 1151       }
 1152   
 1153       /**
 1154        * Hides the popup if it is showing and shows the popup if it is hidden.
 1155        */
 1156       protected void toggleOpenClose() {
 1157           setPopupVisible(comboBox, !isPopupVisible(comboBox));
 1158       }
 1159   
 1160       /**
 1161        * Returns the area that is reserved for drawing the currently selected item.
 1162        */
 1163       protected Rectangle rectangleForCurrentValue() {
 1164           int width = comboBox.getWidth();
 1165           int height = comboBox.getHeight();
 1166           Insets insets = getInsets();
 1167           int buttonSize = height - (insets.top + insets.bottom);
 1168           if ( arrowButton != null ) {
 1169               buttonSize = arrowButton.getWidth();
 1170           }
 1171           if(BasicGraphicsUtils.isLeftToRight(comboBox)) {
 1172               return new Rectangle(insets.left, insets.top,
 1173                                width - (insets.left + insets.right + buttonSize),
 1174                                height - (insets.top + insets.bottom));
 1175           }
 1176           else {
 1177               return new Rectangle(insets.left + buttonSize, insets.top,
 1178                                width - (insets.left + insets.right + buttonSize),
 1179                                height - (insets.top + insets.bottom));
 1180           }
 1181       }
 1182   
 1183       /**
 1184        * Gets the insets from the JComboBox.
 1185        */
 1186       protected Insets getInsets() {
 1187           return comboBox.getInsets();
 1188       }
 1189   
 1190       //
 1191       // end Utility Methods
 1192       //====================
 1193   
 1194   
 1195       //===============================
 1196       // begin Painting Utility Methods
 1197       //
 1198   
 1199       /**
 1200        * Paints the currently selected item.
 1201        */
 1202       public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
 1203           ListCellRenderer renderer = comboBox.getRenderer();
 1204           Component c;
 1205   
 1206           if ( hasFocus && !isPopupVisible(comboBox) ) {
 1207               c = renderer.getListCellRendererComponent( listBox,
 1208                                                          comboBox.getSelectedItem(),
 1209                                                          -1,
 1210                                                          true,
 1211                                                          false );
 1212           }
 1213           else {
 1214               c = renderer.getListCellRendererComponent( listBox,
 1215                                                          comboBox.getSelectedItem(),
 1216                                                          -1,
 1217                                                          false,
 1218                                                          false );
 1219               c.setBackground(UIManager.getColor("ComboBox.background"));
 1220           }
 1221           c.setFont(comboBox.getFont());
 1222           if ( hasFocus && !isPopupVisible(comboBox) ) {
 1223               c.setForeground(listBox.getSelectionForeground());
 1224               c.setBackground(listBox.getSelectionBackground());
 1225           }
 1226           else {
 1227               if ( comboBox.isEnabled() ) {
 1228                   c.setForeground(comboBox.getForeground());
 1229                   c.setBackground(comboBox.getBackground());
 1230               }
 1231               else {
 1232                   c.setForeground(DefaultLookup.getColor(
 1233                            comboBox, this, "ComboBox.disabledForeground", null));
 1234                   c.setBackground(DefaultLookup.getColor(
 1235                            comboBox, this, "ComboBox.disabledBackground", null));
 1236               }
 1237           }
 1238   
 1239           // Fix for 4238829: should lay out the JPanel.
 1240           boolean shouldValidate = false;
 1241           if (c instanceof JPanel)  {
 1242               shouldValidate = true;
 1243           }
 1244   
 1245           int x = bounds.x, y = bounds.y, w = bounds.width, h = bounds.height;
 1246           if (padding != null) {
 1247               x = bounds.x + padding.left;
 1248               y = bounds.y + padding.top;
 1249               w = bounds.width - (padding.left + padding.right);
 1250               h = bounds.height - (padding.top + padding.bottom);
 1251           }
 1252   
 1253           currentValuePane.paintComponent(g,c,comboBox,x,y,w,h,shouldValidate);
 1254       }
 1255   
 1256       /**
 1257        * Paints the background of the currently selected item.
 1258        */
 1259       public void paintCurrentValueBackground(Graphics g,Rectangle bounds,boolean hasFocus) {
 1260           Color t = g.getColor();
 1261           if ( comboBox.isEnabled() )
 1262               g.setColor(DefaultLookup.getColor(comboBox, this,
 1263                                                 "ComboBox.background", null));
 1264           else
 1265               g.setColor(DefaultLookup.getColor(comboBox, this,
 1266                                        "ComboBox.disabledBackground", null));
 1267           g.fillRect(bounds.x,bounds.y,bounds.width,bounds.height);
 1268           g.setColor(t);
 1269       }
 1270   
 1271       /**
 1272        * Repaint the currently selected item.
 1273        */
 1274       void repaintCurrentValue() {
 1275           Rectangle r = rectangleForCurrentValue();
 1276           comboBox.repaint(r.x,r.y,r.width,r.height);
 1277       }
 1278   
 1279       //
 1280       // end Painting Utility Methods
 1281       //=============================
 1282   
 1283   
 1284       //===============================
 1285       // begin Size Utility Methods
 1286       //
 1287   
 1288       /**
 1289        * Return the default size of an empty display area of the combo box using
 1290        * the current renderer and font.
 1291        *
 1292        * @return the size of an empty display area
 1293        * @see #getDisplaySize
 1294        */
 1295       protected Dimension getDefaultSize() {
 1296           // Calculates the height and width using the default text renderer
 1297           Dimension d = getSizeForComponent(getDefaultListCellRenderer().getListCellRendererComponent(listBox, " ", -1, false, false));
 1298   
 1299           return new Dimension(d.width, d.height);
 1300       }
 1301   
 1302       /**
 1303        * Returns the calculated size of the display area. The display area is the
 1304        * portion of the combo box in which the selected item is displayed. This
 1305        * method will use the prototype display value if it has been set.
 1306        * <p>
 1307        * For combo boxes with a non trivial number of items, it is recommended to
 1308        * use a prototype display value to significantly speed up the display
 1309        * size calculation.
 1310        *
 1311        * @return the size of the display area calculated from the combo box items
 1312        * @see javax.swing.JComboBox#setPrototypeDisplayValue
 1313        */
 1314       protected Dimension getDisplaySize() {
 1315           if (!isDisplaySizeDirty)  {
 1316               return new Dimension(cachedDisplaySize);
 1317           }
 1318           Dimension result = new Dimension();
 1319   
 1320           ListCellRenderer renderer = comboBox.getRenderer();
 1321           if (renderer == null)  {
 1322               renderer = new DefaultListCellRenderer();
 1323           }
 1324   
 1325           sameBaseline = true;
 1326   
 1327           Object prototypeValue = comboBox.getPrototypeDisplayValue();
 1328           if (prototypeValue != null)  {
 1329               // Calculates the dimension based on the prototype value
 1330               result = getSizeForComponent(renderer.getListCellRendererComponent(listBox,
 1331                                                                                  prototypeValue,
 1332                                                                                  -1, false, false));
 1333           } else {
 1334               // Calculate the dimension by iterating over all the elements in the combo
 1335               // box list.
 1336               ComboBoxModel model = comboBox.getModel();
 1337               int modelSize = model.getSize();
 1338               int baseline = -1;
 1339               Dimension d;
 1340   
 1341               Component cpn;
 1342   
 1343               if (modelSize > 0 ) {
 1344                   for (int i = 0; i < modelSize ; i++ ) {
 1345                       // Calculates the maximum height and width based on the largest
 1346                       // element
 1347                       Object value = model.getElementAt(i);
 1348                       Component c = renderer.getListCellRendererComponent(
 1349                               listBox, value, -1, false, false);
 1350                       d = getSizeForComponent(c);
 1351                       if (sameBaseline && value != null &&
 1352                               (!(value instanceof String) || !"".equals(value))) {
 1353                           int newBaseline = c.getBaseline(d.width, d.height);
 1354                           if (newBaseline == -1) {
 1355                               sameBaseline = false;
 1356                           }
 1357                           else if (baseline == -1) {
 1358                               baseline = newBaseline;
 1359                           }
 1360                           else if (baseline != newBaseline) {
 1361                               sameBaseline = false;
 1362                           }
 1363                       }
 1364                       result.width = Math.max(result.width,d.width);
 1365                       result.height = Math.max(result.height,d.height);
 1366                   }
 1367               } else {
 1368                   result = getDefaultSize();
 1369                   if (comboBox.isEditable()) {
 1370                       result.width = 100;
 1371                   }
 1372               }
 1373           }
 1374   
 1375           if ( comboBox.isEditable() ) {
 1376               Dimension d = editor.getPreferredSize();
 1377               result.width = Math.max(result.width,d.width);
 1378               result.height = Math.max(result.height,d.height);
 1379           }
 1380   
 1381           // calculate in the padding
 1382           if (padding != null) {
 1383               result.width += padding.left + padding.right;
 1384               result.height += padding.top + padding.bottom;
 1385           }
 1386   
 1387           // Set the cached value
 1388           cachedDisplaySize.setSize(result.width, result.height);
 1389           isDisplaySizeDirty = false;
 1390   
 1391           return result;
 1392       }
 1393   
 1394       /**
 1395        * Returns the size a component would have if used as a cell renderer.
 1396        *
 1397        * @param comp a {@code Component} to check
 1398        * @return size of the component
 1399        * @since 1.7
 1400        */
 1401       protected Dimension getSizeForComponent(Component comp) {
 1402           // This has been refactored out in hopes that it may be investigated and
 1403           // simplified for the next major release. adding/removing
 1404           // the component to the currentValuePane and changing the font may be
 1405           // redundant operations.
 1406           currentValuePane.add(comp);
 1407           comp.setFont(comboBox.getFont());
 1408           Dimension d = comp.getPreferredSize();
 1409           currentValuePane.remove(comp);
 1410           return d;
 1411       }
 1412   
 1413   
 1414       //
 1415       // end Size Utility Methods
 1416       //=============================
 1417   
 1418   
 1419       //=================================
 1420       // begin Keyboard Action Management
 1421       //
 1422   
 1423       /**
 1424        * Adds keyboard actions to the JComboBox.  Actions on enter and esc are already
 1425        * supplied.  Add more actions as you need them.
 1426        */
 1427       protected void installKeyboardActions() {
 1428           InputMap km = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 1429           SwingUtilities.replaceUIInputMap(comboBox, JComponent.
 1430                                WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, km);
 1431   
 1432   
 1433           LazyActionMap.installLazyActionMap(comboBox, BasicComboBoxUI.class,
 1434                                              "ComboBox.actionMap");
 1435       }
 1436   
 1437       InputMap getInputMap(int condition) {
 1438           if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
 1439               return (InputMap)DefaultLookup.get(comboBox, this,
 1440                                                  "ComboBox.ancestorInputMap");
 1441           }
 1442           return null;
 1443       }
 1444   
 1445       boolean isTableCellEditor() {
 1446           return isTableCellEditor;
 1447       }
 1448   
 1449       /**
 1450        * Removes the focus InputMap and ActionMap.
 1451        */
 1452       protected void uninstallKeyboardActions() {
 1453           SwingUtilities.replaceUIInputMap(comboBox, JComponent.
 1454                                    WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
 1455           SwingUtilities.replaceUIActionMap(comboBox, null);
 1456       }
 1457   
 1458   
 1459       //
 1460       // Actions
 1461       //
 1462       private static class Actions extends UIAction {
 1463           private static final String HIDE = "hidePopup";
 1464           private static final String DOWN = "selectNext";
 1465           private static final String DOWN_2 = "selectNext2";
 1466           private static final String TOGGLE = "togglePopup";
 1467           private static final String TOGGLE_2 = "spacePopup";
 1468           private static final String UP = "selectPrevious";
 1469           private static final String UP_2 = "selectPrevious2";
 1470           private static final String ENTER = "enterPressed";
 1471           private static final String PAGE_DOWN = "pageDownPassThrough";
 1472           private static final String PAGE_UP = "pageUpPassThrough";
 1473           private static final String HOME = "homePassThrough";
 1474           private static final String END = "endPassThrough";
 1475   
 1476           Actions(String name) {
 1477               super(name);
 1478           }
 1479   
 1480           public void actionPerformed( ActionEvent e ) {
 1481               String key = getName();
 1482               JComboBox comboBox = (JComboBox)e.getSource();
 1483               BasicComboBoxUI ui = (BasicComboBoxUI)BasicLookAndFeel.getUIOfType(
 1484                                     comboBox.getUI(), BasicComboBoxUI.class);
 1485               if (key == HIDE) {
 1486                   comboBox.firePopupMenuCanceled();
 1487                   comboBox.setPopupVisible(false);
 1488               }
 1489               else if (key == PAGE_DOWN || key == PAGE_UP ||
 1490                        key == HOME || key == END) {
 1491                   int index = getNextIndex(comboBox, key);
 1492                   if (index >= 0 && index < comboBox.getItemCount()) {
 1493                       comboBox.setSelectedIndex(index);
 1494                   }
 1495               }
 1496               else if (key == DOWN) {
 1497                   if (comboBox.isShowing() ) {
 1498                       if ( comboBox.isPopupVisible() ) {
 1499                           if (ui != null) {
 1500                               ui.selectNextPossibleValue();
 1501                           }
 1502                       } else {
 1503                           comboBox.setPopupVisible(true);
 1504                       }
 1505                   }
 1506               }
 1507               else if (key == DOWN_2) {
 1508                   // Special case in which pressing the arrow keys will not
 1509                   // make the popup appear - except for editable combo boxes
 1510                   // and combo boxes inside a table.
 1511                   if (comboBox.isShowing() ) {
 1512                       if ( (comboBox.isEditable() ||
 1513                               (ui != null && ui.isTableCellEditor()))
 1514                            && !comboBox.isPopupVisible() ) {
 1515                           comboBox.setPopupVisible(true);
 1516                       } else {
 1517                           if (ui != null) {
 1518                               ui.selectNextPossibleValue();
 1519                           }
 1520                       }
 1521                   }
 1522               }
 1523               else if (key == TOGGLE || key == TOGGLE_2) {
 1524                   if (ui != null && (key == TOGGLE || !comboBox.isEditable())) {
 1525                       if ( ui.isTableCellEditor() ) {
 1526                           // Forces the selection of the list item if the
 1527                           // combo box is in a JTable.
 1528                           comboBox.setSelectedIndex(ui.popup.getList().
 1529                                                     getSelectedIndex());
 1530                       }
 1531                       else {
 1532                           comboBox.setPopupVisible(!comboBox.isPopupVisible());
 1533                       }
 1534                   }
 1535               }
 1536               else if (key == UP) {
 1537                   if (ui != null) {
 1538                       if (ui.isPopupVisible(comboBox)) {
 1539                           ui.selectPreviousPossibleValue();
 1540                       }
 1541                       else if (DefaultLookup.getBoolean(comboBox, ui,
 1542                                       "ComboBox.showPopupOnNavigation", false)) {
 1543                           ui.setPopupVisible(comboBox, true);
 1544                       }
 1545                   }
 1546               }
 1547               else if (key == UP_2) {
 1548                    // Special case in which pressing the arrow keys will not
 1549                    // make the popup appear - except for editable combo boxes.
 1550                    if (comboBox.isShowing() && ui != null) {
 1551                        if ( comboBox.isEditable() && !comboBox.isPopupVisible()) {
 1552                            comboBox.setPopupVisible(true);
 1553                        } else {
 1554                            ui.selectPreviousPossibleValue();
 1555                        }
 1556                    }
 1557                }
 1558   
 1559               else if (key == ENTER) {
 1560                   if (comboBox.isPopupVisible()) {
 1561                       // Forces the selection of the list item
 1562                       boolean isEnterSelectablePopup =
 1563                               UIManager.getBoolean("ComboBox.isEnterSelectablePopup");
 1564                       if (!comboBox.isEditable() || isEnterSelectablePopup
 1565                               || ui.isTableCellEditor) {
 1566                           Object listItem = ui.popup.getList().getSelectedValue();
 1567                           if (listItem != null) {
 1568                               // Use the selected value from popup
 1569                               // to set the selected item in combo box,
 1570                               // but ensure before that JComboBox.actionPerformed()
 1571                               // won't use editor's value to set the selected item
 1572                               comboBox.getEditor().setItem(listItem);
 1573                               comboBox.setSelectedItem(listItem);
 1574                           }
 1575                       }
 1576                       comboBox.setPopupVisible(false);
 1577                   }
 1578                   else {
 1579                       // Hide combo box if it is a table cell editor
 1580                       if (ui.isTableCellEditor && !comboBox.isEditable()) {
 1581                           comboBox.setSelectedItem(comboBox.getSelectedItem());
 1582                       }
 1583                       // Call the default button binding.
 1584                       // This is a pretty messy way of passing an event through
 1585                       // to the root pane.
 1586                       JRootPane root = SwingUtilities.getRootPane(comboBox);
 1587                       if (root != null) {
 1588                           InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
 1589                           ActionMap am = root.getActionMap();
 1590                           if (im != null && am != null) {
 1591                               Object obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
 1592                               if (obj != null) {
 1593                                   Action action = am.get(obj);
 1594                                   if (action != null) {
 1595                                       action.actionPerformed(new ActionEvent(
 1596                                        root, e.getID(), e.getActionCommand(),
 1597                                        e.getWhen(), e.getModifiers()));
 1598                                   }
 1599                               }
 1600                           }
 1601                       }
 1602                   }
 1603               }
 1604           }
 1605   
 1606           private int getNextIndex(JComboBox comboBox, String key) {
 1607               if (key == PAGE_UP) {
 1608                   int listHeight = comboBox.getMaximumRowCount();
 1609                   int index = comboBox.getSelectedIndex() - listHeight;
 1610                   return (index < 0 ? 0: index);
 1611               }
 1612               else if (key == PAGE_DOWN) {
 1613                   int listHeight = comboBox.getMaximumRowCount();
 1614                   int index = comboBox.getSelectedIndex() + listHeight;
 1615                   int max = comboBox.getItemCount();
 1616                   return (index < max ? index: max-1);
 1617               }
 1618               else if (key == HOME) {
 1619                   return 0;
 1620               }
 1621               else if (key == END) {
 1622                   return comboBox.getItemCount() - 1;
 1623               }
 1624               return comboBox.getSelectedIndex();
 1625           }
 1626   
 1627           public boolean isEnabled(Object c) {
 1628               if (getName() == HIDE) {
 1629                   return (c != null && ((JComboBox)c).isPopupVisible());
 1630               }
 1631               return true;
 1632           }
 1633       }
 1634       //
 1635       // end Keyboard Action Management
 1636       //===============================
 1637   
 1638   
 1639       //
 1640       // Shared Handler, implements all listeners
 1641       //
 1642       private class Handler implements ActionListener, FocusListener,
 1643                                        KeyListener, LayoutManager,
 1644                                        ListDataListener, PropertyChangeListener {
 1645           //
 1646           // PropertyChangeListener
 1647           //
 1648           public void propertyChange(PropertyChangeEvent e) {
 1649               String propertyName = e.getPropertyName();
 1650               if (e.getSource() == editor){
 1651                   // If the border of the editor changes then this can effect
 1652                   // the size of the editor which can cause the combo's size to
 1653                   // become invalid so we need to clear size caches
 1654                   if ("border".equals(propertyName)){
 1655                       isMinimumSizeDirty = true;
 1656                       isDisplaySizeDirty = true;
 1657                       comboBox.revalidate();
 1658                   }
 1659               } else {
 1660                   JComboBox comboBox = (JComboBox)e.getSource();
 1661                   if ( propertyName == "model" ) {
 1662                       ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
 1663                       ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();
 1664   
 1665                       if ( oldModel != null && listDataListener != null ) {
 1666                           oldModel.removeListDataListener( listDataListener );
 1667                       }
 1668   
 1669                       if ( newModel != null && listDataListener != null ) {
 1670                           newModel.addListDataListener( listDataListener );
 1671                       }
 1672   
 1673                       if ( editor != null ) {
 1674                           comboBox.configureEditor( comboBox.getEditor(), comboBox.getSelectedItem() );
 1675                       }
 1676                       isMinimumSizeDirty = true;
 1677                       isDisplaySizeDirty = true;
 1678                       comboBox.revalidate();
 1679                       comboBox.repaint();
 1680                   }
 1681                   else if ( propertyName == "editor" && comboBox.isEditable() ) {
 1682                       addEditor();
 1683                       comboBox.revalidate();
 1684                   }
 1685                   else if ( propertyName == "editable" ) {
 1686                       if ( comboBox.isEditable() ) {
 1687                           comboBox.setRequestFocusEnabled( false );
 1688                           addEditor();
 1689                       } else {
 1690                           comboBox.setRequestFocusEnabled( true );
 1691                           removeEditor();
 1692                       }
 1693                       updateToolTipTextForChildren();
 1694                       comboBox.revalidate();
 1695                   }
 1696                   else if ( propertyName == "enabled" ) {
 1697                       boolean enabled = comboBox.isEnabled();
 1698                       if ( editor != null )
 1699                           editor.setEnabled(enabled);
 1700                       if ( arrowButton != null )
 1701                           arrowButton.setEnabled(enabled);
 1702                       comboBox.repaint();
 1703                   }
 1704                   else if ( propertyName == "focusable" ) {
 1705                       boolean focusable = comboBox.isFocusable();
 1706                       if ( editor != null )
 1707                           editor.setFocusable(focusable);
 1708                       if ( arrowButton != null )
 1709                           arrowButton.setFocusable(focusable);
 1710                       comboBox.repaint();
 1711                   }
 1712                   else if ( propertyName == "maximumRowCount" ) {
 1713                       if ( isPopupVisible( comboBox ) ) {
 1714                           setPopupVisible(comboBox, false);
 1715                           setPopupVisible(comboBox, true);
 1716                       }
 1717                   }
 1718                   else if ( propertyName == "font" ) {
 1719                       listBox.setFont( comboBox.getFont() );
 1720                       if ( editor != null ) {
 1721                           editor.setFont( comboBox.getFont() );
 1722                       }
 1723                       isMinimumSizeDirty = true;
 1724                       isDisplaySizeDirty = true;
 1725                       comboBox.validate();
 1726                   }
 1727                   else if ( propertyName == JComponent.TOOL_TIP_TEXT_KEY ) {
 1728                       updateToolTipTextForChildren();
 1729                   }
 1730                   else if ( propertyName == BasicComboBoxUI.IS_TABLE_CELL_EDITOR ) {
 1731                       Boolean inTable = (Boolean)e.getNewValue();
 1732                       isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
 1733                   }
 1734                   else if (propertyName == "prototypeDisplayValue") {
 1735                       isMinimumSizeDirty = true;
 1736                       isDisplaySizeDirty = true;
 1737                       comboBox.revalidate();
 1738                   }
 1739                   else if (propertyName == "renderer") {
 1740                       isMinimumSizeDirty = true;
 1741                       isDisplaySizeDirty = true;
 1742                       comboBox.revalidate();
 1743                   }
 1744               }
 1745           }
 1746   
 1747   
 1748           //
 1749           // KeyListener
 1750           //
 1751   
 1752           // This listener checks to see if the key event isn't a navigation
 1753           // key.  If it finds a key event that wasn't a navigation key it
 1754           // dispatches it to JComboBox.selectWithKeyChar() so that it can do
 1755           // type-ahead.
 1756           public void keyPressed( KeyEvent e ) {
 1757               if ( isNavigationKey(e.getKeyCode(), e.getModifiers()) ) {
 1758                   lastTime = 0L;
 1759               } else if ( comboBox.isEnabled() && comboBox.getModel().getSize()!=0 &&
 1760                           isTypeAheadKey( e ) && e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
 1761                   time = e.getWhen();
 1762                   if ( comboBox.selectWithKeyChar(e.getKeyChar()) ) {
 1763                       e.consume();
 1764                   }
 1765               }
 1766           }
 1767   
 1768           public void keyTyped(KeyEvent e) {
 1769           }
 1770   
 1771           public void keyReleased(KeyEvent e) {
 1772           }
 1773   
 1774           private boolean isTypeAheadKey( KeyEvent e ) {
 1775               return !e.isAltDown() && !BasicGraphicsUtils.isMenuShortcutKeyDown(e);
 1776           }
 1777   
 1778           //
 1779           // FocusListener
 1780           //
 1781           // NOTE: The class is added to both the Editor and ComboBox.
 1782           // The combo box listener hides the popup when the focus is lost.
 1783           // It also repaints when focus is gained or lost.
 1784   
 1785           public void focusGained( FocusEvent e ) {
 1786               ComboBoxEditor comboBoxEditor = comboBox.getEditor();
 1787   
 1788               if ( (comboBoxEditor != null) &&
 1789                    (e.getSource() == comboBoxEditor.getEditorComponent()) ) {
 1790                   return;
 1791               }
 1792               hasFocus = true;
 1793               comboBox.repaint();
 1794   
 1795               if (comboBox.isEditable() && editor != null) {
 1796                   editor.requestFocus();
 1797               }
 1798           }
 1799   
 1800           public void focusLost( FocusEvent e ) {
 1801               ComboBoxEditor editor = comboBox.getEditor();
 1802               if ( (editor != null) &&
 1803                    (e.getSource() == editor.getEditorComponent()) ) {
 1804                   Object item = editor.getItem();
 1805   
 1806                   Object selectedItem = comboBox.getSelectedItem();
 1807                   if (!e.isTemporary() && item != null &&
 1808                       !item.equals((selectedItem == null) ? "" : selectedItem )) {
 1809                       comboBox.actionPerformed
 1810                           (new ActionEvent(editor, 0, "",
 1811                                         EventQueue.getMostRecentEventTime(), 0));
 1812                   }
 1813               }
 1814   
 1815               hasFocus = false;
 1816               if (!e.isTemporary()) {
 1817                   setPopupVisible(comboBox, false);
 1818               }
 1819               comboBox.repaint();
 1820           }
 1821   
 1822           //
 1823           // ListDataListener
 1824           //
 1825   
 1826           // This listener watches for changes in the ComboBoxModel
 1827           public void contentsChanged( ListDataEvent e ) {
 1828               if ( !(e.getIndex0() == -1 && e.getIndex1() == -1) ) {
 1829                   isMinimumSizeDirty = true;
 1830                   comboBox.revalidate();
 1831               }
 1832   
 1833               // set the editor with the selected item since this
 1834               // is the event handler for a selected item change.
 1835               if (comboBox.isEditable() && editor != null) {
 1836                   comboBox.configureEditor( comboBox.getEditor(),
 1837                                             comboBox.getSelectedItem() );
 1838               }
 1839   
 1840               isDisplaySizeDirty = true;
 1841               comboBox.repaint();
 1842           }
 1843   
 1844           public void intervalAdded( ListDataEvent e ) {
 1845               contentsChanged( e );
 1846           }
 1847   
 1848           public void intervalRemoved( ListDataEvent e ) {
 1849               contentsChanged( e );
 1850           }
 1851   
 1852           //
 1853           // LayoutManager
 1854           //
 1855   
 1856           // This layout manager handles the 'standard' layout of combo boxes.
 1857           // It puts the arrow button to the right and the editor to the left.
 1858           // If there is no editor it still keeps the arrow button to the right.
 1859           public void addLayoutComponent(String name, Component comp) {}
 1860   
 1861           public void removeLayoutComponent(Component comp) {}
 1862   
 1863           public Dimension preferredLayoutSize(Container parent) {
 1864               return parent.getPreferredSize();
 1865           }
 1866   
 1867           public Dimension minimumLayoutSize(Container parent) {
 1868               return parent.getMinimumSize();
 1869           }
 1870   
 1871           public void layoutContainer(Container parent) {
 1872               JComboBox cb = (JComboBox)parent;
 1873               int width = cb.getWidth();
 1874               int height = cb.getHeight();
 1875   
 1876               Insets insets = getInsets();
 1877               int buttonHeight = height - (insets.top + insets.bottom);
 1878               int buttonWidth = buttonHeight;
 1879               if (arrowButton != null) {
 1880                   Insets arrowInsets = arrowButton.getInsets();
 1881                   buttonWidth = squareButton ?
 1882                       buttonHeight :
 1883                       arrowButton.getPreferredSize().width + arrowInsets.left + arrowInsets.right;
 1884               }
 1885               Rectangle cvb;
 1886   
 1887               if (arrowButton != null) {
 1888                   if (BasicGraphicsUtils.isLeftToRight(cb)) {
 1889                       arrowButton.setBounds(width - (insets.right + buttonWidth),
 1890                               insets.top, buttonWidth, buttonHeight);
 1891                   } else {
 1892                       arrowButton.setBounds(insets.left, insets.top,
 1893                               buttonWidth, buttonHeight);
 1894                   }
 1895               }
 1896               if ( editor != null ) {
 1897                   cvb = rectangleForCurrentValue();
 1898                   editor.setBounds(cvb);
 1899               }
 1900           }
 1901   
 1902           //
 1903           // ActionListener
 1904           //
 1905           // Fix for 4515752: Forward the Enter pressed on the
 1906           // editable combo box to the default button
 1907   
 1908           // Note: This could depend on event ordering. The first ActionEvent
 1909           // from the editor may be handled by the JComboBox in which case, the
 1910           // enterPressed action will always be invoked.
 1911           public void actionPerformed(ActionEvent evt) {
 1912               Object item = comboBox.getEditor().getItem();
 1913               if (item != null) {
 1914                if(!comboBox.isPopupVisible() && !item.equals(comboBox.getSelectedItem())) {
 1915                 comboBox.setSelectedItem(comboBox.getEditor().getItem());
 1916                }
 1917                ActionMap am = comboBox.getActionMap();
 1918                if (am != null) {
 1919                   Action action = am.get("enterPressed");
 1920                   if (action != null) {
 1921                       action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
 1922                                              evt.getActionCommand(),
 1923                                              evt.getModifiers()));
 1924                   }
 1925               }
 1926          }
 1927      }
 1928     }
 1929   
 1930       class DefaultKeySelectionManager implements JComboBox.KeySelectionManager, UIResource {
 1931           private String prefix = "";
 1932           private String typedString = "";
 1933   
 1934           public int selectionForKey(char aKey,ComboBoxModel aModel) {
 1935               if (lastTime == 0L) {
 1936                   prefix = "";
 1937                   typedString = "";
 1938               }
 1939               boolean startingFromSelection = true;
 1940   
 1941               int startIndex = comboBox.getSelectedIndex();
 1942               if (time - lastTime < timeFactor) {
 1943                   typedString += aKey;
 1944                   if((prefix.length() == 1) && (aKey == prefix.charAt(0))) {
 1945                       // Subsequent same key presses move the keyboard focus to the next
 1946                       // object that starts with the same letter.
 1947                       startIndex++;
 1948                   } else {
 1949                       prefix = typedString;
 1950                   }
 1951               } else {
 1952                   startIndex++;
 1953                   typedString = "" + aKey;
 1954                   prefix = typedString;
 1955               }
 1956               lastTime = time;
 1957   
 1958               if (startIndex < 0 || startIndex >= aModel.getSize()) {
 1959                   startingFromSelection = false;
 1960                   startIndex = 0;
 1961               }
 1962               int index = listBox.getNextMatch(prefix, startIndex,
 1963                                                Position.Bias.Forward);
 1964               if (index < 0 && startingFromSelection) { // wrap
 1965                   index = listBox.getNextMatch(prefix, 0,
 1966                                                Position.Bias.Forward);
 1967               }
 1968               return index;
 1969           }
 1970       }
 1971   
 1972   }

Home » openjdk-7 » javax » swing » plaf » basic » [javadoc | source]