src/share/classes/javax/swing/plaf/basic/BasicComboBoxUI.java

Print this page


   1 /*
   2  * Copyright (c) 1997, 2013, 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


  44  * The combo box is a compound component which means that it is an aggregate 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://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html">How to Use Key Bindings</a>
  58  *
  59  * @author Arnaud Weber
  60  * @author Tom Santos
  61  * @author Mark Davidson
  62  */
  63 public class BasicComboBoxUI extends ComboBoxUI {
  64     protected JComboBox comboBox;
  65     /**
  66      * This protected field is implementation specific. Do not access directly
  67      * or override.
  68      */
  69     protected boolean   hasFocus = false;
  70 
  71     // Control the selection behavior of the JComboBox when it is used
  72     // in the JTable DefaultCellEditor.
  73     private boolean isTableCellEditor = false;
  74     private static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor";
  75 
  76     // This list is for drawing the current item in the combo box.
  77     protected JList   listBox;
  78 
  79     // Used to render the currently selected item in the combo box.
  80     // It doesn't have anything to do with the popup's rendering.
  81     protected CellRendererPane currentValuePane = new CellRendererPane();
  82 
  83     // The implementation of ComboPopup that is used to show the popup.
  84     protected ComboPopup popup;
  85 
  86     // The Component that the ComboBoxEditor uses for editing
  87     protected Component editor;
  88 
  89     // The arrow button that invokes the popup.
  90     protected JButton   arrowButton;
  91 
  92     // Listeners that are attached to the JComboBox
  93     /**
  94      * This protected field is implementation specific. Do not access directly
  95      * or override. Override the listener construction method instead.
  96      *
  97      * @see #createKeyListener


 186 
 187     /**
 188      * Indicates whether or not the combo box button should be square.
 189      * If square, then the width and height are equal, and are both set to
 190      * the height of the combo minus appropriate insets.
 191      *
 192      * @since 1.7
 193      */
 194     protected boolean squareButton = true;
 195 
 196     /**
 197      * If specified, these insets act as padding around the cell renderer when
 198      * laying out and painting the "selected" item in the combo box. These
 199      * insets add to those specified by the cell renderer.
 200      *
 201      * @since 1.7
 202      */
 203     protected Insets padding;
 204 
 205     // Used for calculating the default size.
 206     private static ListCellRenderer getDefaultListCellRenderer() {
 207         ListCellRenderer renderer = (ListCellRenderer)AppContext.

 208                          getAppContext().get(COMBO_UI_LIST_CELL_RENDERER_KEY);
 209 
 210         if (renderer == null) {
 211             renderer = new DefaultListCellRenderer();
 212             AppContext.getAppContext().put(COMBO_UI_LIST_CELL_RENDERER_KEY,
 213                                            new DefaultListCellRenderer());
 214         }
 215         return renderer;
 216     }
 217 
 218     /**
 219      * Populates ComboBox's actions.
 220      */
 221     static void loadActionMap(LazyActionMap map) {
 222         map.put(new Actions(Actions.HIDE));
 223         map.put(new Actions(Actions.PAGE_DOWN));
 224         map.put(new Actions(Actions.PAGE_UP));
 225         map.put(new Actions(Actions.HOME));
 226         map.put(new Actions(Actions.END));
 227         map.put(new Actions(Actions.DOWN));
 228         map.put(new Actions(Actions.DOWN_2));
 229         map.put(new Actions(Actions.TOGGLE));
 230         map.put(new Actions(Actions.TOGGLE_2));
 231         map.put(new Actions(Actions.UP));
 232         map.put(new Actions(Actions.UP_2));
 233         map.put(new Actions(Actions.ENTER));
 234     }
 235 
 236     //========================
 237     // begin UI Initialization
 238     //
 239 
 240     public static ComponentUI createUI(JComponent c) {
 241         return new BasicComboBoxUI();
 242     }
 243 
 244     @Override
 245     public void installUI( JComponent c ) {
 246         isMinimumSizeDirty = true;
 247 
 248         comboBox = (JComboBox)c;


 249         installDefaults();
 250         popup = createPopup();
 251         listBox = popup.getList();
 252 
 253         // Is this combo box a cell editor?
 254         Boolean inTable = (Boolean)c.getClientProperty(IS_TABLE_CELL_EDITOR );
 255         if (inTable != null) {
 256             isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
 257         }
 258 
 259         if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
 260             comboBox.setRenderer( createRenderer() );
 261         }
 262 
 263         if ( comboBox.getEditor() == null || comboBox.getEditor() instanceof UIResource ) {
 264             comboBox.setEditor( createEditor() );
 265         }
 266 
 267         installListeners();
 268         installComponents();


 491     }
 492 
 493     /**
 494      * Creates a layout manager for managing the components which make up the
 495      * combo box.
 496      *
 497      * @return an instance of a layout manager
 498      */
 499     protected LayoutManager createLayoutManager() {
 500         return getHandler();
 501     }
 502 
 503     /**
 504      * Creates the default renderer that will be used in a non-editiable combo
 505      * box. A default renderer will used only if a renderer has not been
 506      * explicitly set with <code>setRenderer</code>.
 507      *
 508      * @return a <code>ListCellRender</code> used for the combo box
 509      * @see javax.swing.JComboBox#setRenderer
 510      */
 511     protected ListCellRenderer createRenderer() {
 512         return new BasicComboBoxRenderer.UIResource();
 513     }
 514 
 515     /**
 516      * Creates the default editor that will be used in editable combo boxes.
 517      * A default editor will be used only if an editor has not been
 518      * explicitly set with <code>setEditor</code>.
 519      *
 520      * @return a <code>ComboBoxEditor</code> used for the combo box
 521      * @see javax.swing.JComboBox#setEditor
 522      */
 523     protected ComboBoxEditor createEditor() {
 524         return new BasicComboBoxEditor.UIResource();
 525     }
 526 
 527     /**
 528      * Returns the shared listener.
 529      */
 530     private Handler getHandler() {
 531         if (handler == null) {


 848                                     UIManager.getColor("ComboBox.buttonBackground"),
 849                                     UIManager.getColor("ComboBox.buttonShadow"),
 850                                     UIManager.getColor("ComboBox.buttonDarkShadow"),
 851                                     UIManager.getColor("ComboBox.buttonHighlight"));
 852         button.setName("ComboBox.arrowButton");
 853         return button;
 854     }
 855 
 856     //
 857     // end Sub-Component Management
 858     //===============================
 859 
 860 
 861     //================================
 862     // begin ComboBoxUI Implementation
 863     //
 864 
 865     /**
 866      * Tells if the popup is visible or not.
 867      */
 868     public boolean isPopupVisible( JComboBox c ) {
 869         return popup.isVisible();
 870     }
 871 
 872     /**
 873      * Hides the popup.
 874      */
 875     public void setPopupVisible( JComboBox c, boolean v ) {
 876         if ( v ) {
 877             popup.show();
 878         } else {
 879             popup.hide();
 880         }
 881     }
 882 
 883     /**
 884      * Determines if the JComboBox is focus traversable.  If the JComboBox is editable
 885      * this returns false, otherwise it returns true.
 886      */
 887     public boolean isFocusTraversable( JComboBox c ) {
 888         return !comboBox.isEditable();
 889     }
 890 
 891     //
 892     // end ComboBoxUI Implementation
 893     //==============================
 894 
 895 
 896     //=================================
 897     // begin ComponentUI Implementation
 898     @Override
 899     public void paint( Graphics g, JComponent c ) {
 900         hasFocus = comboBox.hasFocus();
 901         if ( !comboBox.isEditable() ) {
 902             Rectangle r = rectangleForCurrentValue();
 903             paintCurrentValueBackground(g,r,hasFocus);
 904             paintCurrentValue(g,r,hasFocus);
 905         }
 906     }
 907 


 939     }
 940 
 941     /**
 942      * Returns the baseline.
 943      *
 944      * @throws NullPointerException {@inheritDoc}
 945      * @throws IllegalArgumentException {@inheritDoc}
 946      * @see javax.swing.JComponent#getBaseline(int, int)
 947      * @since 1.6
 948      */
 949     @Override
 950     public int getBaseline(JComponent c, int width, int height) {
 951         super.getBaseline(c, width, height);
 952         int baseline = -1;
 953         // force sameBaseline to be updated.
 954         getDisplaySize();
 955         if (sameBaseline) {
 956             Insets insets = c.getInsets();
 957             height = height - insets.top - insets.bottom;
 958             if (!comboBox.isEditable()) {
 959                 ListCellRenderer renderer = comboBox.getRenderer();
 960                 if (renderer == null)  {
 961                     renderer = new DefaultListCellRenderer();
 962                 }
 963                 Object value = null;
 964                 Object prototypeValue = comboBox.getPrototypeDisplayValue();
 965                 if (prototypeValue != null)  {
 966                     value = prototypeValue;
 967                 }
 968                 else if (comboBox.getModel().getSize() > 0) {
 969                     // Note, we're assuming the baseline is the same for all
 970                     // cells, if not, this needs to loop through all.
 971                     value = comboBox.getModel().getElementAt(0);
 972                 }
 973                 Component component = renderer.
 974                         getListCellRendererComponent(listBox, value, -1,
 975                                                      false, false);
 976                 if (component instanceof JLabel) {
 977                     JLabel label = (JLabel) component;
 978                     String text = label.getText();
 979                     if ((text == null) || text.isEmpty()) {


 996     }
 997 
 998     /**
 999      * Returns an enum indicating how the baseline of the component
1000      * changes as the size changes.
1001      *
1002      * @throws NullPointerException {@inheritDoc}
1003      * @see javax.swing.JComponent#getBaseline(int, int)
1004      * @since 1.6
1005      */
1006     @Override
1007     public Component.BaselineResizeBehavior getBaselineResizeBehavior(
1008             JComponent c) {
1009         super.getBaselineResizeBehavior(c);
1010         // Force sameBaseline to be updated.
1011         getDisplaySize();
1012         if (comboBox.isEditable()) {
1013             return editor.getBaselineResizeBehavior();
1014         }
1015         else if (sameBaseline) {
1016             ListCellRenderer renderer = comboBox.getRenderer();
1017             if (renderer == null)  {
1018                 renderer = new DefaultListCellRenderer();
1019             }
1020             Object value = null;
1021             Object prototypeValue = comboBox.getPrototypeDisplayValue();
1022             if (prototypeValue != null)  {
1023                 value = prototypeValue;
1024             }
1025             else if (comboBox.getModel().getSize() > 0) {
1026                 // Note, we're assuming the baseline is the same for all
1027                 // cells, if not, this needs to loop through all.
1028                 value = comboBox.getModel().getElementAt(0);
1029             }
1030             if (value != null) {
1031                 Component component = renderer.
1032                         getListCellRendererComponent(listBox, value, -1,
1033                                                      false, false);
1034                 return component.getBaselineResizeBehavior();
1035             }
1036         }


1188     /**
1189      * Gets the insets from the JComboBox.
1190      */
1191     protected Insets getInsets() {
1192         return comboBox.getInsets();
1193     }
1194 
1195     //
1196     // end Utility Methods
1197     //====================
1198 
1199 
1200     //===============================
1201     // begin Painting Utility Methods
1202     //
1203 
1204     /**
1205      * Paints the currently selected item.
1206      */
1207     public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
1208         ListCellRenderer renderer = comboBox.getRenderer();
1209         Component c;
1210 
1211         if ( hasFocus && !isPopupVisible(comboBox) ) {
1212             c = renderer.getListCellRendererComponent( listBox,
1213                                                        comboBox.getSelectedItem(),
1214                                                        -1,
1215                                                        true,
1216                                                        false );
1217         }
1218         else {
1219             c = renderer.getListCellRendererComponent( listBox,
1220                                                        comboBox.getSelectedItem(),
1221                                                        -1,
1222                                                        false,
1223                                                        false );
1224             c.setBackground(UIManager.getColor("ComboBox.background"));
1225         }
1226         c.setFont(comboBox.getFont());
1227         if ( hasFocus && !isPopupVisible(comboBox) ) {
1228             c.setForeground(listBox.getSelectionForeground());


1305     }
1306 
1307     /**
1308      * Returns the calculated size of the display area. The display area is the
1309      * portion of the combo box in which the selected item is displayed. This
1310      * method will use the prototype display value if it has been set.
1311      * <p>
1312      * For combo boxes with a non trivial number of items, it is recommended to
1313      * use a prototype display value to significantly speed up the display
1314      * size calculation.
1315      *
1316      * @return the size of the display area calculated from the combo box items
1317      * @see javax.swing.JComboBox#setPrototypeDisplayValue
1318      */
1319     protected Dimension getDisplaySize() {
1320         if (!isDisplaySizeDirty)  {
1321             return new Dimension(cachedDisplaySize);
1322         }
1323         Dimension result = new Dimension();
1324 
1325         ListCellRenderer renderer = comboBox.getRenderer();
1326         if (renderer == null)  {
1327             renderer = new DefaultListCellRenderer();
1328         }
1329 
1330         sameBaseline = true;
1331 
1332         Object prototypeValue = comboBox.getPrototypeDisplayValue();
1333         if (prototypeValue != null)  {
1334             // Calculates the dimension based on the prototype value
1335             result = getSizeForComponent(renderer.getListCellRendererComponent(listBox,
1336                                                                                prototypeValue,
1337                                                                                -1, false, false));
1338         } else {
1339             // Calculate the dimension by iterating over all the elements in the combo
1340             // box list.
1341             ComboBoxModel model = comboBox.getModel();
1342             int modelSize = model.getSize();
1343             int baseline = -1;
1344             Dimension d;
1345 
1346             Component cpn;
1347 
1348             if (modelSize > 0 ) {
1349                 for (int i = 0; i < modelSize ; i++ ) {
1350                     // Calculates the maximum height and width based on the largest
1351                     // element
1352                     Object value = model.getElementAt(i);
1353                     Component c = renderer.getListCellRendererComponent(
1354                             listBox, value, -1, false, false);
1355                     d = getSizeForComponent(c);
1356                     if (sameBaseline && value != null &&
1357                             (!(value instanceof String) || !"".equals(value))) {
1358                         int newBaseline = c.getBaseline(d.width, d.height);
1359                         if (newBaseline == -1) {
1360                             sameBaseline = false;
1361                         }


1467     private static class Actions extends UIAction {
1468         private static final String HIDE = "hidePopup";
1469         private static final String DOWN = "selectNext";
1470         private static final String DOWN_2 = "selectNext2";
1471         private static final String TOGGLE = "togglePopup";
1472         private static final String TOGGLE_2 = "spacePopup";
1473         private static final String UP = "selectPrevious";
1474         private static final String UP_2 = "selectPrevious2";
1475         private static final String ENTER = "enterPressed";
1476         private static final String PAGE_DOWN = "pageDownPassThrough";
1477         private static final String PAGE_UP = "pageUpPassThrough";
1478         private static final String HOME = "homePassThrough";
1479         private static final String END = "endPassThrough";
1480 
1481         Actions(String name) {
1482             super(name);
1483         }
1484 
1485         public void actionPerformed( ActionEvent e ) {
1486             String key = getName();
1487             JComboBox comboBox = (JComboBox)e.getSource();

1488             BasicComboBoxUI ui = (BasicComboBoxUI)BasicLookAndFeel.getUIOfType(
1489                                   comboBox.getUI(), BasicComboBoxUI.class);
1490             if (key == HIDE) {
1491                 comboBox.firePopupMenuCanceled();
1492                 comboBox.setPopupVisible(false);
1493             }
1494             else if (key == PAGE_DOWN || key == PAGE_UP ||
1495                      key == HOME || key == END) {
1496                 int index = getNextIndex(comboBox, key);
1497                 if (index >= 0 && index < comboBox.getItemCount()) {
1498                     if (UIManager.getBoolean("ComboBox.noActionOnKeyNavigation") && comboBox.isPopupVisible()) {
1499                         ui.listBox.setSelectedIndex(index);
1500                         ui.listBox.ensureIndexIsVisible(index);
1501                         comboBox.repaint();
1502                     } else {
1503                         comboBox.setSelectedIndex(index);
1504                     }
1505                 }
1506             }
1507             else if (key == DOWN) {


1608                     JRootPane root = SwingUtilities.getRootPane(comboBox);
1609                     if (root != null) {
1610                         InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1611                         ActionMap am = root.getActionMap();
1612                         if (im != null && am != null) {
1613                             Object obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
1614                             if (obj != null) {
1615                                 Action action = am.get(obj);
1616                                 if (action != null) {
1617                                     action.actionPerformed(new ActionEvent(
1618                                      root, e.getID(), e.getActionCommand(),
1619                                      e.getWhen(), e.getModifiers()));
1620                                 }
1621                             }
1622                         }
1623                     }
1624                 }
1625             }
1626         }
1627 
1628         private int getNextIndex(JComboBox comboBox, String key) {
1629             int listHeight = comboBox.getMaximumRowCount();
1630 
1631             int selectedIndex = comboBox.getSelectedIndex();
1632             if (UIManager.getBoolean("ComboBox.noActionOnKeyNavigation")
1633                     && (comboBox.getUI() instanceof BasicComboBoxUI)) {
1634                 selectedIndex = ((BasicComboBoxUI) comboBox.getUI()).listBox.getSelectedIndex();
1635             }
1636 
1637             if (key == PAGE_UP) {
1638                 int index = selectedIndex - listHeight;
1639                 return (index < 0 ? 0: index);
1640             }
1641             else if (key == PAGE_DOWN) {
1642                 int index = selectedIndex + listHeight;
1643                 int max = comboBox.getItemCount();
1644                 return (index < max ? index: max-1);
1645             }
1646             else if (key == HOME) {
1647                 return 0;
1648             }


1668     // Shared Handler, implements all listeners
1669     //
1670     private class Handler implements ActionListener, FocusListener,
1671                                      KeyListener, LayoutManager,
1672                                      ListDataListener, PropertyChangeListener {
1673         //
1674         // PropertyChangeListener
1675         //
1676         public void propertyChange(PropertyChangeEvent e) {
1677             String propertyName = e.getPropertyName();
1678             if (e.getSource() == editor){
1679                 // If the border of the editor changes then this can effect
1680                 // the size of the editor which can cause the combo's size to
1681                 // become invalid so we need to clear size caches
1682                 if ("border".equals(propertyName)){
1683                     isMinimumSizeDirty = true;
1684                     isDisplaySizeDirty = true;
1685                     comboBox.revalidate();
1686                 }
1687             } else {
1688                 JComboBox comboBox = (JComboBox)e.getSource();

1689                 if ( propertyName == "model" ) {
1690                     ComboBoxModel newModel = (ComboBoxModel)e.getNewValue();
1691                     ComboBoxModel oldModel = (ComboBoxModel)e.getOldValue();


1692 
1693                     if ( oldModel != null && listDataListener != null ) {
1694                         oldModel.removeListDataListener( listDataListener );
1695                     }
1696 
1697                     if ( newModel != null && listDataListener != null ) {
1698                         newModel.addListDataListener( listDataListener );
1699                     }
1700 
1701                     if ( editor != null ) {
1702                         comboBox.configureEditor( comboBox.getEditor(), comboBox.getSelectedItem() );
1703                     }
1704                     isMinimumSizeDirty = true;
1705                     isDisplaySizeDirty = true;
1706                     comboBox.revalidate();
1707                     comboBox.repaint();
1708                 }
1709                 else if ( propertyName == "editor" && comboBox.isEditable() ) {
1710                     addEditor();
1711                     comboBox.revalidate();


1880         //
1881         // LayoutManager
1882         //
1883 
1884         // This layout manager handles the 'standard' layout of combo boxes.
1885         // It puts the arrow button to the right and the editor to the left.
1886         // If there is no editor it still keeps the arrow button to the right.
1887         public void addLayoutComponent(String name, Component comp) {}
1888 
1889         public void removeLayoutComponent(Component comp) {}
1890 
1891         public Dimension preferredLayoutSize(Container parent) {
1892             return parent.getPreferredSize();
1893         }
1894 
1895         public Dimension minimumLayoutSize(Container parent) {
1896             return parent.getMinimumSize();
1897         }
1898 
1899         public void layoutContainer(Container parent) {
1900             JComboBox cb = (JComboBox)parent;

1901             int width = cb.getWidth();
1902             int height = cb.getHeight();
1903 
1904             Insets insets = getInsets();
1905             int buttonHeight = height - (insets.top + insets.bottom);
1906             int buttonWidth = buttonHeight;
1907             if (arrowButton != null) {
1908                 Insets arrowInsets = arrowButton.getInsets();
1909                 buttonWidth = squareButton ?
1910                     buttonHeight :
1911                     arrowButton.getPreferredSize().width + arrowInsets.left + arrowInsets.right;
1912             }
1913             Rectangle cvb;
1914 
1915             if (arrowButton != null) {
1916                 if (BasicGraphicsUtils.isLeftToRight(cb)) {
1917                     arrowButton.setBounds(width - (insets.right + buttonWidth),
1918                             insets.top, buttonWidth, buttonHeight);
1919                 } else {
1920                     arrowButton.setBounds(insets.left, insets.top,


1942              if(!comboBox.isPopupVisible() && !item.equals(comboBox.getSelectedItem())) {
1943               comboBox.setSelectedItem(comboBox.getEditor().getItem());
1944              }
1945              ActionMap am = comboBox.getActionMap();
1946              if (am != null) {
1947                 Action action = am.get("enterPressed");
1948                 if (action != null) {
1949                     action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
1950                                            evt.getActionCommand(),
1951                                            evt.getModifiers()));
1952                 }
1953             }
1954        }
1955    }
1956   }
1957 
1958     class DefaultKeySelectionManager implements JComboBox.KeySelectionManager, UIResource {
1959         private String prefix = "";
1960         private String typedString = "";
1961 
1962         public int selectionForKey(char aKey,ComboBoxModel aModel) {
1963             if (lastTime == 0L) {
1964                 prefix = "";
1965                 typedString = "";
1966             }
1967             boolean startingFromSelection = true;
1968 
1969             int startIndex = comboBox.getSelectedIndex();
1970             if (time - lastTime < timeFactor) {
1971                 typedString += aKey;
1972                 if((prefix.length() == 1) && (aKey == prefix.charAt(0))) {
1973                     // Subsequent same key presses move the keyboard focus to the next
1974                     // object that starts with the same letter.
1975                     startIndex++;
1976                 } else {
1977                     prefix = typedString;
1978                 }
1979             } else {
1980                 startIndex++;
1981                 typedString = "" + aKey;
1982                 prefix = typedString;
   1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any


  44  * The combo box is a compound component which means that it is an aggregate 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://docs.oracle.com/javase/tutorial/uiswing/misc/keybinding.html">How to Use Key Bindings</a>
  58  *
  59  * @author Arnaud Weber
  60  * @author Tom Santos
  61  * @author Mark Davidson
  62  */
  63 public class BasicComboBoxUI extends ComboBoxUI {
  64     protected JComboBox<Object> comboBox;
  65     /**
  66      * This protected field is implementation specific. Do not access directly
  67      * or override.
  68      */
  69     protected boolean   hasFocus = false;
  70 
  71     // Control the selection behavior of the JComboBox when it is used
  72     // in the JTable DefaultCellEditor.
  73     private boolean isTableCellEditor = false;
  74     private static final String IS_TABLE_CELL_EDITOR = "JComboBox.isTableCellEditor";
  75 
  76     // This list is for drawing the current item in the combo box.
  77     protected JList<Object>   listBox;
  78 
  79     // Used to render the currently selected item in the combo box.
  80     // It doesn't have anything to do with the popup's rendering.
  81     protected CellRendererPane currentValuePane = new CellRendererPane();
  82 
  83     // The implementation of ComboPopup that is used to show the popup.
  84     protected ComboPopup popup;
  85 
  86     // The Component that the ComboBoxEditor uses for editing
  87     protected Component editor;
  88 
  89     // The arrow button that invokes the popup.
  90     protected JButton   arrowButton;
  91 
  92     // Listeners that are attached to the JComboBox
  93     /**
  94      * This protected field is implementation specific. Do not access directly
  95      * or override. Override the listener construction method instead.
  96      *
  97      * @see #createKeyListener


 186 
 187     /**
 188      * Indicates whether or not the combo box button should be square.
 189      * If square, then the width and height are equal, and are both set to
 190      * the height of the combo minus appropriate insets.
 191      *
 192      * @since 1.7
 193      */
 194     protected boolean squareButton = true;
 195 
 196     /**
 197      * If specified, these insets act as padding around the cell renderer when
 198      * laying out and painting the "selected" item in the combo box. These
 199      * insets add to those specified by the cell renderer.
 200      *
 201      * @since 1.7
 202      */
 203     protected Insets padding;
 204 
 205     // Used for calculating the default size.
 206     private static ListCellRenderer<Object> getDefaultListCellRenderer() {
 207         @SuppressWarnings("unchecked")
 208         ListCellRenderer<Object> 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         @SuppressWarnings("unchecked")
 250         JComboBox<Object> tmp = (JComboBox)c;
 251         comboBox = tmp;
 252         installDefaults();
 253         popup = createPopup();
 254         listBox = popup.getList();
 255 
 256         // Is this combo box a cell editor?
 257         Boolean inTable = (Boolean)c.getClientProperty(IS_TABLE_CELL_EDITOR );
 258         if (inTable != null) {
 259             isTableCellEditor = inTable.equals(Boolean.TRUE) ? true : false;
 260         }
 261 
 262         if ( comboBox.getRenderer() == null || comboBox.getRenderer() instanceof UIResource ) {
 263             comboBox.setRenderer( createRenderer() );
 264         }
 265 
 266         if ( comboBox.getEditor() == null || comboBox.getEditor() instanceof UIResource ) {
 267             comboBox.setEditor( createEditor() );
 268         }
 269 
 270         installListeners();
 271         installComponents();


 494     }
 495 
 496     /**
 497      * Creates a layout manager for managing the components which make up the
 498      * combo box.
 499      *
 500      * @return an instance of a layout manager
 501      */
 502     protected LayoutManager createLayoutManager() {
 503         return getHandler();
 504     }
 505 
 506     /**
 507      * Creates the default renderer that will be used in a non-editiable combo
 508      * box. A default renderer will used only if a renderer has not been
 509      * explicitly set with <code>setRenderer</code>.
 510      *
 511      * @return a <code>ListCellRender</code> used for the combo box
 512      * @see javax.swing.JComboBox#setRenderer
 513      */
 514     protected ListCellRenderer<Object> createRenderer() {
 515         return new BasicComboBoxRenderer.UIResource();
 516     }
 517 
 518     /**
 519      * Creates the default editor that will be used in editable combo boxes.
 520      * A default editor will be used only if an editor has not been
 521      * explicitly set with <code>setEditor</code>.
 522      *
 523      * @return a <code>ComboBoxEditor</code> used for the combo box
 524      * @see javax.swing.JComboBox#setEditor
 525      */
 526     protected ComboBoxEditor createEditor() {
 527         return new BasicComboBoxEditor.UIResource();
 528     }
 529 
 530     /**
 531      * Returns the shared listener.
 532      */
 533     private Handler getHandler() {
 534         if (handler == null) {


 851                                     UIManager.getColor("ComboBox.buttonBackground"),
 852                                     UIManager.getColor("ComboBox.buttonShadow"),
 853                                     UIManager.getColor("ComboBox.buttonDarkShadow"),
 854                                     UIManager.getColor("ComboBox.buttonHighlight"));
 855         button.setName("ComboBox.arrowButton");
 856         return button;
 857     }
 858 
 859     //
 860     // end Sub-Component Management
 861     //===============================
 862 
 863 
 864     //================================
 865     // begin ComboBoxUI Implementation
 866     //
 867 
 868     /**
 869      * Tells if the popup is visible or not.
 870      */
 871     public boolean isPopupVisible( JComboBox<?> c ) {
 872         return popup.isVisible();
 873     }
 874 
 875     /**
 876      * Hides the popup.
 877      */
 878     public void setPopupVisible( JComboBox<?> c, boolean v ) {
 879         if ( v ) {
 880             popup.show();
 881         } else {
 882             popup.hide();
 883         }
 884     }
 885 
 886     /**
 887      * Determines if the JComboBox is focus traversable.  If the JComboBox is editable
 888      * this returns false, otherwise it returns true.
 889      */
 890     public boolean isFocusTraversable( JComboBox<?> c ) {
 891         return !comboBox.isEditable();
 892     }
 893 
 894     //
 895     // end ComboBoxUI Implementation
 896     //==============================
 897 
 898 
 899     //=================================
 900     // begin ComponentUI Implementation
 901     @Override
 902     public void paint( Graphics g, JComponent c ) {
 903         hasFocus = comboBox.hasFocus();
 904         if ( !comboBox.isEditable() ) {
 905             Rectangle r = rectangleForCurrentValue();
 906             paintCurrentValueBackground(g,r,hasFocus);
 907             paintCurrentValue(g,r,hasFocus);
 908         }
 909     }
 910 


 942     }
 943 
 944     /**
 945      * Returns the baseline.
 946      *
 947      * @throws NullPointerException {@inheritDoc}
 948      * @throws IllegalArgumentException {@inheritDoc}
 949      * @see javax.swing.JComponent#getBaseline(int, int)
 950      * @since 1.6
 951      */
 952     @Override
 953     public int getBaseline(JComponent c, int width, int height) {
 954         super.getBaseline(c, width, height);
 955         int baseline = -1;
 956         // force sameBaseline to be updated.
 957         getDisplaySize();
 958         if (sameBaseline) {
 959             Insets insets = c.getInsets();
 960             height = height - insets.top - insets.bottom;
 961             if (!comboBox.isEditable()) {
 962                 ListCellRenderer<Object> renderer = comboBox.getRenderer();
 963                 if (renderer == null)  {
 964                     renderer = new DefaultListCellRenderer();
 965                 }
 966                 Object value = null;
 967                 Object prototypeValue = comboBox.getPrototypeDisplayValue();
 968                 if (prototypeValue != null)  {
 969                     value = prototypeValue;
 970                 }
 971                 else if (comboBox.getModel().getSize() > 0) {
 972                     // Note, we're assuming the baseline is the same for all
 973                     // cells, if not, this needs to loop through all.
 974                     value = comboBox.getModel().getElementAt(0);
 975                 }
 976                 Component component = renderer.
 977                         getListCellRendererComponent(listBox, value, -1,
 978                                                      false, false);
 979                 if (component instanceof JLabel) {
 980                     JLabel label = (JLabel) component;
 981                     String text = label.getText();
 982                     if ((text == null) || text.isEmpty()) {


 999     }
1000 
1001     /**
1002      * Returns an enum indicating how the baseline of the component
1003      * changes as the size changes.
1004      *
1005      * @throws NullPointerException {@inheritDoc}
1006      * @see javax.swing.JComponent#getBaseline(int, int)
1007      * @since 1.6
1008      */
1009     @Override
1010     public Component.BaselineResizeBehavior getBaselineResizeBehavior(
1011             JComponent c) {
1012         super.getBaselineResizeBehavior(c);
1013         // Force sameBaseline to be updated.
1014         getDisplaySize();
1015         if (comboBox.isEditable()) {
1016             return editor.getBaselineResizeBehavior();
1017         }
1018         else if (sameBaseline) {
1019             ListCellRenderer<Object> renderer = comboBox.getRenderer();
1020             if (renderer == null)  {
1021                 renderer = new DefaultListCellRenderer();
1022             }
1023             Object value = null;
1024             Object prototypeValue = comboBox.getPrototypeDisplayValue();
1025             if (prototypeValue != null)  {
1026                 value = prototypeValue;
1027             }
1028             else if (comboBox.getModel().getSize() > 0) {
1029                 // Note, we're assuming the baseline is the same for all
1030                 // cells, if not, this needs to loop through all.
1031                 value = comboBox.getModel().getElementAt(0);
1032             }
1033             if (value != null) {
1034                 Component component = renderer.
1035                         getListCellRendererComponent(listBox, value, -1,
1036                                                      false, false);
1037                 return component.getBaselineResizeBehavior();
1038             }
1039         }


1191     /**
1192      * Gets the insets from the JComboBox.
1193      */
1194     protected Insets getInsets() {
1195         return comboBox.getInsets();
1196     }
1197 
1198     //
1199     // end Utility Methods
1200     //====================
1201 
1202 
1203     //===============================
1204     // begin Painting Utility Methods
1205     //
1206 
1207     /**
1208      * Paints the currently selected item.
1209      */
1210     public void paintCurrentValue(Graphics g,Rectangle bounds,boolean hasFocus) {
1211         ListCellRenderer<Object> renderer = comboBox.getRenderer();
1212         Component c;
1213 
1214         if ( hasFocus && !isPopupVisible(comboBox) ) {
1215             c = renderer.getListCellRendererComponent( listBox,
1216                                                        comboBox.getSelectedItem(),
1217                                                        -1,
1218                                                        true,
1219                                                        false );
1220         }
1221         else {
1222             c = renderer.getListCellRendererComponent( listBox,
1223                                                        comboBox.getSelectedItem(),
1224                                                        -1,
1225                                                        false,
1226                                                        false );
1227             c.setBackground(UIManager.getColor("ComboBox.background"));
1228         }
1229         c.setFont(comboBox.getFont());
1230         if ( hasFocus && !isPopupVisible(comboBox) ) {
1231             c.setForeground(listBox.getSelectionForeground());


1308     }
1309 
1310     /**
1311      * Returns the calculated size of the display area. The display area is the
1312      * portion of the combo box in which the selected item is displayed. This
1313      * method will use the prototype display value if it has been set.
1314      * <p>
1315      * For combo boxes with a non trivial number of items, it is recommended to
1316      * use a prototype display value to significantly speed up the display
1317      * size calculation.
1318      *
1319      * @return the size of the display area calculated from the combo box items
1320      * @see javax.swing.JComboBox#setPrototypeDisplayValue
1321      */
1322     protected Dimension getDisplaySize() {
1323         if (!isDisplaySizeDirty)  {
1324             return new Dimension(cachedDisplaySize);
1325         }
1326         Dimension result = new Dimension();
1327 
1328         ListCellRenderer<Object> renderer = comboBox.getRenderer();
1329         if (renderer == null)  {
1330             renderer = new DefaultListCellRenderer();
1331         }
1332 
1333         sameBaseline = true;
1334 
1335         Object prototypeValue = comboBox.getPrototypeDisplayValue();
1336         if (prototypeValue != null)  {
1337             // Calculates the dimension based on the prototype value
1338             result = getSizeForComponent(renderer.getListCellRendererComponent(listBox,
1339                                                                                prototypeValue,
1340                                                                                -1, false, false));
1341         } else {
1342             // Calculate the dimension by iterating over all the elements in the combo
1343             // box list.
1344             ComboBoxModel<Object> model = comboBox.getModel();
1345             int modelSize = model.getSize();
1346             int baseline = -1;
1347             Dimension d;
1348 
1349             Component cpn;
1350 
1351             if (modelSize > 0 ) {
1352                 for (int i = 0; i < modelSize ; i++ ) {
1353                     // Calculates the maximum height and width based on the largest
1354                     // element
1355                     Object value = model.getElementAt(i);
1356                     Component c = renderer.getListCellRendererComponent(
1357                             listBox, value, -1, false, false);
1358                     d = getSizeForComponent(c);
1359                     if (sameBaseline && value != null &&
1360                             (!(value instanceof String) || !"".equals(value))) {
1361                         int newBaseline = c.getBaseline(d.width, d.height);
1362                         if (newBaseline == -1) {
1363                             sameBaseline = false;
1364                         }


1470     private static class Actions extends UIAction {
1471         private static final String HIDE = "hidePopup";
1472         private static final String DOWN = "selectNext";
1473         private static final String DOWN_2 = "selectNext2";
1474         private static final String TOGGLE = "togglePopup";
1475         private static final String TOGGLE_2 = "spacePopup";
1476         private static final String UP = "selectPrevious";
1477         private static final String UP_2 = "selectPrevious2";
1478         private static final String ENTER = "enterPressed";
1479         private static final String PAGE_DOWN = "pageDownPassThrough";
1480         private static final String PAGE_UP = "pageUpPassThrough";
1481         private static final String HOME = "homePassThrough";
1482         private static final String END = "endPassThrough";
1483 
1484         Actions(String name) {
1485             super(name);
1486         }
1487 
1488         public void actionPerformed( ActionEvent e ) {
1489             String key = getName();
1490             @SuppressWarnings("unchecked")
1491             JComboBox<Object> comboBox = (JComboBox)e.getSource();
1492             BasicComboBoxUI ui = (BasicComboBoxUI)BasicLookAndFeel.getUIOfType(
1493                                   comboBox.getUI(), BasicComboBoxUI.class);
1494             if (key == HIDE) {
1495                 comboBox.firePopupMenuCanceled();
1496                 comboBox.setPopupVisible(false);
1497             }
1498             else if (key == PAGE_DOWN || key == PAGE_UP ||
1499                      key == HOME || key == END) {
1500                 int index = getNextIndex(comboBox, key);
1501                 if (index >= 0 && index < comboBox.getItemCount()) {
1502                     if (UIManager.getBoolean("ComboBox.noActionOnKeyNavigation") && comboBox.isPopupVisible()) {
1503                         ui.listBox.setSelectedIndex(index);
1504                         ui.listBox.ensureIndexIsVisible(index);
1505                         comboBox.repaint();
1506                     } else {
1507                         comboBox.setSelectedIndex(index);
1508                     }
1509                 }
1510             }
1511             else if (key == DOWN) {


1612                     JRootPane root = SwingUtilities.getRootPane(comboBox);
1613                     if (root != null) {
1614                         InputMap im = root.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1615                         ActionMap am = root.getActionMap();
1616                         if (im != null && am != null) {
1617                             Object obj = im.get(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0));
1618                             if (obj != null) {
1619                                 Action action = am.get(obj);
1620                                 if (action != null) {
1621                                     action.actionPerformed(new ActionEvent(
1622                                      root, e.getID(), e.getActionCommand(),
1623                                      e.getWhen(), e.getModifiers()));
1624                                 }
1625                             }
1626                         }
1627                     }
1628                 }
1629             }
1630         }
1631 
1632         private int getNextIndex(JComboBox<?> comboBox, String key) {
1633             int listHeight = comboBox.getMaximumRowCount();
1634 
1635             int selectedIndex = comboBox.getSelectedIndex();
1636             if (UIManager.getBoolean("ComboBox.noActionOnKeyNavigation")
1637                     && (comboBox.getUI() instanceof BasicComboBoxUI)) {
1638                 selectedIndex = ((BasicComboBoxUI) comboBox.getUI()).listBox.getSelectedIndex();
1639             }
1640 
1641             if (key == PAGE_UP) {
1642                 int index = selectedIndex - listHeight;
1643                 return (index < 0 ? 0: index);
1644             }
1645             else if (key == PAGE_DOWN) {
1646                 int index = selectedIndex + listHeight;
1647                 int max = comboBox.getItemCount();
1648                 return (index < max ? index: max-1);
1649             }
1650             else if (key == HOME) {
1651                 return 0;
1652             }


1672     // Shared Handler, implements all listeners
1673     //
1674     private class Handler implements ActionListener, FocusListener,
1675                                      KeyListener, LayoutManager,
1676                                      ListDataListener, PropertyChangeListener {
1677         //
1678         // PropertyChangeListener
1679         //
1680         public void propertyChange(PropertyChangeEvent e) {
1681             String propertyName = e.getPropertyName();
1682             if (e.getSource() == editor){
1683                 // If the border of the editor changes then this can effect
1684                 // the size of the editor which can cause the combo's size to
1685                 // become invalid so we need to clear size caches
1686                 if ("border".equals(propertyName)){
1687                     isMinimumSizeDirty = true;
1688                     isDisplaySizeDirty = true;
1689                     comboBox.revalidate();
1690                 }
1691             } else {
1692                 @SuppressWarnings("unchecked")
1693                 JComboBox<?> comboBox = (JComboBox)e.getSource();
1694                 if ( propertyName == "model" ) {
1695                     @SuppressWarnings("unchecked")
1696                     ComboBoxModel<?> newModel = (ComboBoxModel)e.getNewValue();
1697                     @SuppressWarnings("unchecked")
1698                     ComboBoxModel<?> oldModel = (ComboBoxModel)e.getOldValue();
1699 
1700                     if ( oldModel != null && listDataListener != null ) {
1701                         oldModel.removeListDataListener( listDataListener );
1702                     }
1703 
1704                     if ( newModel != null && listDataListener != null ) {
1705                         newModel.addListDataListener( listDataListener );
1706                     }
1707 
1708                     if ( editor != null ) {
1709                         comboBox.configureEditor( comboBox.getEditor(), comboBox.getSelectedItem() );
1710                     }
1711                     isMinimumSizeDirty = true;
1712                     isDisplaySizeDirty = true;
1713                     comboBox.revalidate();
1714                     comboBox.repaint();
1715                 }
1716                 else if ( propertyName == "editor" && comboBox.isEditable() ) {
1717                     addEditor();
1718                     comboBox.revalidate();


1887         //
1888         // LayoutManager
1889         //
1890 
1891         // This layout manager handles the 'standard' layout of combo boxes.
1892         // It puts the arrow button to the right and the editor to the left.
1893         // If there is no editor it still keeps the arrow button to the right.
1894         public void addLayoutComponent(String name, Component comp) {}
1895 
1896         public void removeLayoutComponent(Component comp) {}
1897 
1898         public Dimension preferredLayoutSize(Container parent) {
1899             return parent.getPreferredSize();
1900         }
1901 
1902         public Dimension minimumLayoutSize(Container parent) {
1903             return parent.getMinimumSize();
1904         }
1905 
1906         public void layoutContainer(Container parent) {
1907             @SuppressWarnings("unchecked")
1908             JComboBox<?> cb = (JComboBox)parent;
1909             int width = cb.getWidth();
1910             int height = cb.getHeight();
1911 
1912             Insets insets = getInsets();
1913             int buttonHeight = height - (insets.top + insets.bottom);
1914             int buttonWidth = buttonHeight;
1915             if (arrowButton != null) {
1916                 Insets arrowInsets = arrowButton.getInsets();
1917                 buttonWidth = squareButton ?
1918                     buttonHeight :
1919                     arrowButton.getPreferredSize().width + arrowInsets.left + arrowInsets.right;
1920             }
1921             Rectangle cvb;
1922 
1923             if (arrowButton != null) {
1924                 if (BasicGraphicsUtils.isLeftToRight(cb)) {
1925                     arrowButton.setBounds(width - (insets.right + buttonWidth),
1926                             insets.top, buttonWidth, buttonHeight);
1927                 } else {
1928                     arrowButton.setBounds(insets.left, insets.top,


1950              if(!comboBox.isPopupVisible() && !item.equals(comboBox.getSelectedItem())) {
1951               comboBox.setSelectedItem(comboBox.getEditor().getItem());
1952              }
1953              ActionMap am = comboBox.getActionMap();
1954              if (am != null) {
1955                 Action action = am.get("enterPressed");
1956                 if (action != null) {
1957                     action.actionPerformed(new ActionEvent(comboBox, evt.getID(),
1958                                            evt.getActionCommand(),
1959                                            evt.getModifiers()));
1960                 }
1961             }
1962        }
1963    }
1964   }
1965 
1966     class DefaultKeySelectionManager implements JComboBox.KeySelectionManager, UIResource {
1967         private String prefix = "";
1968         private String typedString = "";
1969 
1970         public int selectionForKey(char aKey,ComboBoxModel<?> aModel) {
1971             if (lastTime == 0L) {
1972                 prefix = "";
1973                 typedString = "";
1974             }
1975             boolean startingFromSelection = true;
1976 
1977             int startIndex = comboBox.getSelectedIndex();
1978             if (time - lastTime < timeFactor) {
1979                 typedString += aKey;
1980                 if((prefix.length() == 1) && (aKey == prefix.charAt(0))) {
1981                     // Subsequent same key presses move the keyboard focus to the next
1982                     // object that starts with the same letter.
1983                     startIndex++;
1984                 } else {
1985                     prefix = typedString;
1986                 }
1987             } else {
1988                 startIndex++;
1989                 typedString = "" + aKey;
1990                 prefix = typedString;