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

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