1 /*
   2  * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing;
  26 
  27 import java.beans.*;
  28 import java.util.*;
  29 
  30 import java.awt.*;
  31 import java.awt.event.*;
  32 
  33 import java.io.Serializable;
  34 import java.io.ObjectOutputStream;
  35 import java.io.IOException;
  36 
  37 import javax.swing.event.*;
  38 import javax.swing.plaf.*;
  39 
  40 import javax.accessibility.*;
  41 
  42 /**
  43  * A component that combines a button or editable field and a drop-down list.
  44  * The user can select a value from the drop-down list, which appears at the
  45  * user's request. If you make the combo box editable, then the combo box
  46  * includes an editable field into which the user can type a value.
  47  * <p>
  48  * <strong>Warning:</strong> Swing is not thread safe. For more
  49  * information see <a
  50  * href="package-summary.html#threading">Swing's Threading
  51  * Policy</a>.
  52  * <p>
  53  * <strong>Warning:</strong>
  54  * Serialized objects of this class will not be compatible with
  55  * future Swing releases. The current serialization support is
  56  * appropriate for short term storage or RMI between applications running
  57  * the same version of Swing.  As of 1.4, support for long term storage
  58  * of all JavaBeans<sup><font size="-2">TM</font></sup>
  59  * has been added to the <code>java.beans</code> package.
  60  * Please see {@link java.beans.XMLEncoder}.
  61  *
  62  * <p>
  63  * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/combobox.html">How to Use Combo Boxes</a>
  64  * in <a href="http://java.sun.com/Series/Tutorial/index.html"><em>The Java Tutorial</em></a>
  65  * for further information.
  66  * <p>
  67  * @see ComboBoxModel
  68  * @see DefaultComboBoxModel
  69  *
  70  * @beaninfo
  71  *   attribute: isContainer false
  72  * description: A combination of a text field and a drop-down list.
  73  *
  74  * @author Arnaud Weber
  75  * @author Mark Davidson
  76  */
  77 public class JComboBox extends JComponent
  78 implements ItemSelectable,ListDataListener,ActionListener, Accessible {
  79     /**
  80      * @see #getUIClassID
  81      * @see #readObject
  82      */
  83     private static final String uiClassID = "ComboBoxUI";
  84 
  85     /**
  86      * This protected field is implementation specific. Do not access directly
  87      * or override. Use the accessor methods instead.
  88      *
  89      * @see #getModel
  90      * @see #setModel
  91      */
  92     protected ComboBoxModel    dataModel;
  93     /**
  94      * This protected field is implementation specific. Do not access directly
  95      * or override. Use the accessor methods instead.
  96      *
  97      * @see #getRenderer
  98      * @see #setRenderer
  99      */
 100     protected ListCellRenderer renderer;
 101     /**
 102      * This protected field is implementation specific. Do not access directly
 103      * or override. Use the accessor methods instead.
 104      *
 105      * @see #getEditor
 106      * @see #setEditor
 107      */
 108     protected ComboBoxEditor       editor;
 109     /**
 110      * This protected field is implementation specific. Do not access directly
 111      * or override. Use the accessor methods instead.
 112      *
 113      * @see #getMaximumRowCount
 114      * @see #setMaximumRowCount
 115      */
 116     protected int maximumRowCount = 8;
 117 
 118     /**
 119      * This protected field is implementation specific. Do not access directly
 120      * or override. Use the accessor methods instead.
 121      *
 122      * @see #isEditable
 123      * @see #setEditable
 124      */
 125     protected boolean isEditable  = false;
 126     /**
 127      * This protected field is implementation specific. Do not access directly
 128      * or override. Use the accessor methods instead.
 129      *
 130      * @see #setKeySelectionManager
 131      * @see #getKeySelectionManager
 132      */
 133     protected KeySelectionManager keySelectionManager = null;
 134     /**
 135      * This protected field is implementation specific. Do not access directly
 136      * or override. Use the accessor methods instead.
 137      *
 138      * @see #setActionCommand
 139      * @see #getActionCommand
 140      */
 141     protected String actionCommand = "comboBoxChanged";
 142     /**
 143      * This protected field is implementation specific. Do not access directly
 144      * or override. Use the accessor methods instead.
 145      *
 146      * @see #setLightWeightPopupEnabled
 147      * @see #isLightWeightPopupEnabled
 148      */
 149     protected boolean lightWeightPopupEnabled = JPopupMenu.getDefaultLightWeightPopupEnabled();
 150 
 151     /**
 152      * This protected field is implementation specific. Do not access directly
 153      * or override.
 154      */
 155     protected Object selectedItemReminder = null;
 156 
 157     private Object prototypeDisplayValue;
 158 
 159     // Flag to ensure that infinite loops do not occur with ActionEvents.
 160     private boolean firingActionEvent = false;
 161 
 162     // Flag to ensure the we don't get multiple ActionEvents on item selection.
 163     private boolean selectingItem = false;
 164 
 165     /**
 166      * Creates a <code>JComboBox</code> that takes its items from an
 167      * existing <code>ComboBoxModel</code>.  Since the
 168      * <code>ComboBoxModel</code> is provided, a combo box created using
 169      * this constructor does not create a default combo box model and
 170      * may impact how the insert, remove and add methods behave.
 171      *
 172      * @param aModel the <code>ComboBoxModel</code> that provides the
 173      *          displayed list of items
 174      * @see DefaultComboBoxModel
 175      */
 176     public JComboBox(ComboBoxModel aModel) {
 177         super();
 178         setModel(aModel);
 179         init();
 180     }
 181 
 182     /**
 183      * Creates a <code>JComboBox</code> that contains the elements
 184      * in the specified array.  By default the first item in the array
 185      * (and therefore the data model) becomes selected.
 186      *
 187      * @param items  an array of objects to insert into the combo box
 188      * @see DefaultComboBoxModel
 189      */
 190     public JComboBox(final Object items[]) {
 191         super();
 192         setModel(new DefaultComboBoxModel(items));
 193         init();
 194     }
 195 
 196     /**
 197      * Creates a <code>JComboBox</code> that contains the elements
 198      * in the specified Vector.  By default the first item in the vector
 199      * (and therefore the data model) becomes selected.
 200      *
 201      * @param items  an array of vectors to insert into the combo box
 202      * @see DefaultComboBoxModel
 203      */
 204     public JComboBox(Vector<?> items) {
 205         super();
 206         setModel(new DefaultComboBoxModel(items));
 207         init();
 208     }
 209 
 210     /**
 211      * Creates a <code>JComboBox</code> with a default data model.
 212      * The default data model is an empty list of objects.
 213      * Use <code>addItem</code> to add items.  By default the first item
 214      * in the data model becomes selected.
 215      *
 216      * @see DefaultComboBoxModel
 217      */
 218     public JComboBox() {
 219         super();
 220         setModel(new DefaultComboBoxModel());
 221         init();
 222     }
 223 
 224     private void init() {
 225         installAncestorListener();
 226         setUIProperty("opaque",true);
 227         updateUI();
 228     }
 229 
 230     protected void installAncestorListener() {
 231         addAncestorListener(new AncestorListener(){
 232                                 public void ancestorAdded(AncestorEvent event){ hidePopup();}
 233                                 public void ancestorRemoved(AncestorEvent event){ hidePopup();}
 234                                 public void ancestorMoved(AncestorEvent event){
 235                                     if (event.getSource() != JComboBox.this)
 236                                         hidePopup();
 237                                 }});
 238     }
 239 
 240     /**
 241      * Sets the L&F object that renders this component.
 242      *
 243      * @param ui  the <code>ComboBoxUI</code> L&F object
 244      * @see UIDefaults#getUI
 245      *
 246      * @beaninfo
 247      *        bound: true
 248      *       hidden: true
 249      *    attribute: visualUpdate true
 250      *  description: The UI object that implements the Component's LookAndFeel.
 251      */
 252     public void setUI(ComboBoxUI ui) {
 253         super.setUI(ui);
 254     }
 255 
 256     /**
 257      * Resets the UI property to a value from the current look and feel.
 258      *
 259      * @see JComponent#updateUI
 260      */
 261     public void updateUI() {
 262         setUI((ComboBoxUI)UIManager.getUI(this));
 263 
 264         ListCellRenderer renderer = getRenderer();
 265         if (renderer instanceof Component) {
 266             SwingUtilities.updateComponentTreeUI((Component)renderer);
 267         }
 268     }
 269 
 270 
 271     /**
 272      * Returns the name of the L&F class that renders this component.
 273      *
 274      * @return the string "ComboBoxUI"
 275      * @see JComponent#getUIClassID
 276      * @see UIDefaults#getUI
 277      */
 278     public String getUIClassID() {
 279         return uiClassID;
 280     }
 281 
 282 
 283     /**
 284      * Returns the L&F object that renders this component.
 285      *
 286      * @return the ComboBoxUI object that renders this component
 287      */
 288     public ComboBoxUI getUI() {
 289         return(ComboBoxUI)ui;
 290     }
 291 
 292     /**
 293      * Sets the data model that the <code>JComboBox</code> uses to obtain
 294      * the list of items.
 295      *
 296      * @param aModel the <code>ComboBoxModel</code> that provides the
 297      *  displayed list of items
 298      *
 299      * @beaninfo
 300      *        bound: true
 301      *  description: Model that the combo box uses to get data to display.
 302      */
 303     public void setModel(ComboBoxModel aModel) {
 304         ComboBoxModel oldModel = dataModel;
 305         if (oldModel != null) {
 306             oldModel.removeListDataListener(this);
 307         }
 308         dataModel = aModel;
 309         dataModel.addListDataListener(this);
 310 
 311         // set the current selected item.
 312         selectedItemReminder = dataModel.getSelectedItem();
 313 
 314         firePropertyChange( "model", oldModel, dataModel);
 315     }
 316 
 317     /**
 318      * Returns the data model currently used by the <code>JComboBox</code>.
 319      *
 320      * @return the <code>ComboBoxModel</code> that provides the displayed
 321      *                  list of items
 322      */
 323     public ComboBoxModel getModel() {
 324         return dataModel;
 325     }
 326 
 327     /*
 328      * Properties
 329      */
 330 
 331     /**
 332      * Sets the <code>lightWeightPopupEnabled</code> property, which
 333      * provides a hint as to whether or not a lightweight
 334      * <code>Component</code> should be used to contain the
 335      * <code>JComboBox</code>, versus a heavyweight
 336      * <code>Component</code> such as a <code>Panel</code>
 337      * or a <code>Window</code>.  The decision of lightweight
 338      * versus heavyweight is ultimately up to the
 339      * <code>JComboBox</code>.  Lightweight windows are more
 340      * efficient than heavyweight windows, but lightweight
 341      * and heavyweight components do not mix well in a GUI.
 342      * If your application mixes lightweight and heavyweight
 343      * components, you should disable lightweight popups.
 344      * The default value for the <code>lightWeightPopupEnabled</code>
 345      * property is <code>true</code>, unless otherwise specified
 346      * by the look and feel.  Some look and feels always use
 347      * heavyweight popups, no matter what the value of this property.
 348      * <p>
 349      * See the article <a href="http://java.sun.com/products/jfc/tsc/articles/mixing/index.html">Mixing Heavy and Light Components</a>
 350      * on <a href="http://java.sun.com/products/jfc/tsc">
 351      * <em>The Swing Connection</em></a>
 352      * This method fires a property changed event.
 353      *
 354      * @param aFlag if <code>true</code>, lightweight popups are desired
 355      *
 356      * @beaninfo
 357      *        bound: true
 358      *       expert: true
 359      *  description: Set to <code>false</code> to require heavyweight popups.
 360      */
 361     public void setLightWeightPopupEnabled(boolean aFlag) {
 362         boolean oldFlag = lightWeightPopupEnabled;
 363         lightWeightPopupEnabled = aFlag;
 364         firePropertyChange("lightWeightPopupEnabled", oldFlag, lightWeightPopupEnabled);
 365     }
 366 
 367     /**
 368      * Gets the value of the <code>lightWeightPopupEnabled</code>
 369      * property.
 370      *
 371      * @return the value of the <code>lightWeightPopupEnabled</code>
 372      *    property
 373      * @see #setLightWeightPopupEnabled
 374      */
 375     public boolean isLightWeightPopupEnabled() {
 376         return lightWeightPopupEnabled;
 377     }
 378 
 379     /**
 380      * Determines whether the <code>JComboBox</code> field is editable.
 381      * An editable <code>JComboBox</code> allows the user to type into the
 382      * field or selected an item from the list to initialize the field,
 383      * after which it can be edited. (The editing affects only the field,
 384      * the list item remains intact.) A non editable <code>JComboBox</code>
 385      * displays the selected item in the field,
 386      * but the selection cannot be modified.
 387      *
 388      * @param aFlag a boolean value, where true indicates that the
 389      *                  field is editable
 390      *
 391      * @beaninfo
 392      *        bound: true
 393      *    preferred: true
 394      *  description: If true, the user can type a new value in the combo box.
 395      */
 396     public void setEditable(boolean aFlag) {
 397         boolean oldFlag = isEditable;
 398         isEditable = aFlag;
 399         firePropertyChange( "editable", oldFlag, isEditable );
 400     }
 401 
 402     /**
 403      * Returns true if the <code>JComboBox</code> is editable.
 404      * By default, a combo box is not editable.
 405      *
 406      * @return true if the <code>JComboBox</code> is editable, else false
 407      */
 408     public boolean isEditable() {
 409         return isEditable;
 410     }
 411 
 412     /**
 413      * Sets the maximum number of rows the <code>JComboBox</code> displays.
 414      * If the number of objects in the model is greater than count,
 415      * the combo box uses a scrollbar.
 416      *
 417      * @param count an integer specifying the maximum number of items to
 418      *              display in the list before using a scrollbar
 419      * @beaninfo
 420      *        bound: true
 421      *    preferred: true
 422      *  description: The maximum number of rows the popup should have
 423      */
 424     public void setMaximumRowCount(int count) {
 425         int oldCount = maximumRowCount;
 426         maximumRowCount = count;
 427         firePropertyChange( "maximumRowCount", oldCount, maximumRowCount );
 428     }
 429 
 430     /**
 431      * Returns the maximum number of items the combo box can display
 432      * without a scrollbar
 433      *
 434      * @return an integer specifying the maximum number of items that are
 435      *         displayed in the list before using a scrollbar
 436      */
 437     public int getMaximumRowCount() {
 438         return maximumRowCount;
 439     }
 440 
 441     /**
 442      * Sets the renderer that paints the list items and the item selected from the list in
 443      * the JComboBox field. The renderer is used if the JComboBox is not
 444      * editable. If it is editable, the editor is used to render and edit
 445      * the selected item.
 446      * <p>
 447      * The default renderer displays a string or an icon.
 448      * Other renderers can handle graphic images and composite items.
 449      * <p>
 450      * To display the selected item,
 451      * <code>aRenderer.getListCellRendererComponent</code>
 452      * is called, passing the list object and an index of -1.
 453      *
 454      * @param aRenderer  the <code>ListCellRenderer</code> that
 455      *                  displays the selected item
 456      * @see #setEditor
 457      * @beaninfo
 458      *      bound: true
 459      *     expert: true
 460      *  description: The renderer that paints the item selected in the list.
 461      */
 462     public void setRenderer(ListCellRenderer aRenderer) {
 463         ListCellRenderer oldRenderer = renderer;
 464         renderer = aRenderer;
 465         firePropertyChange( "renderer", oldRenderer, renderer );
 466         invalidate();
 467     }
 468 
 469     /**
 470      * Returns the renderer used to display the selected item in the
 471      * <code>JComboBox</code> field.
 472      *
 473      * @return  the <code>ListCellRenderer</code> that displays
 474      *                  the selected item.
 475      */
 476     public ListCellRenderer getRenderer() {
 477         return renderer;
 478     }
 479 
 480     /**
 481      * Sets the editor used to paint and edit the selected item in the
 482      * <code>JComboBox</code> field.  The editor is used only if the
 483      * receiving <code>JComboBox</code> is editable. If not editable,
 484      * the combo box uses the renderer to paint the selected item.
 485      *
 486      * @param anEditor  the <code>ComboBoxEditor</code> that
 487      *                  displays the selected item
 488      * @see #setRenderer
 489      * @beaninfo
 490      *     bound: true
 491      *    expert: true
 492      *  description: The editor that combo box uses to edit the current value
 493      */
 494     public void setEditor(ComboBoxEditor anEditor) {
 495         ComboBoxEditor oldEditor = editor;
 496 
 497         if ( editor != null ) {
 498             editor.removeActionListener(this);
 499         }
 500         editor = anEditor;
 501         if ( editor != null ) {
 502             editor.addActionListener(this);
 503         }
 504         firePropertyChange( "editor", oldEditor, editor );
 505     }
 506 
 507     /**
 508      * Returns the editor used to paint and edit the selected item in the
 509      * <code>JComboBox</code> field.
 510      *
 511      * @return the <code>ComboBoxEditor</code> that displays the selected item
 512      */
 513     public ComboBoxEditor getEditor() {
 514         return editor;
 515     }
 516 
 517     //
 518     // Selection
 519     //
 520 
 521     /**
 522      * Sets the selected item in the combo box display area to the object in
 523      * the argument.
 524      * If <code>anObject</code> is in the list, the display area shows
 525      * <code>anObject</code> selected.
 526      * <p>
 527      * If <code>anObject</code> is <i>not</i> in the list and the combo box is
 528      * uneditable, it will not change the current selection. For editable
 529      * combo boxes, the selection will change to <code>anObject</code>.
 530      * <p>
 531      * If this constitutes a change in the selected item,
 532      * <code>ItemListener</code>s added to the combo box will be notified with
 533      * one or two <code>ItemEvent</code>s.
 534      * If there is a current selected item, an <code>ItemEvent</code> will be
 535      * fired and the state change will be <code>ItemEvent.DESELECTED</code>.
 536      * If <code>anObject</code> is in the list and is not currently selected
 537      * then an <code>ItemEvent</code> will be fired and the state change will
 538      * be <code>ItemEvent.SELECTED</code>.
 539      * <p>
 540      * <code>ActionListener</code>s added to the combo box will be notified
 541      * with an <code>ActionEvent</code> when this method is called.
 542      *
 543      * @param anObject  the list object to select; use <code>null</code> to
 544                         clear the selection
 545      * @beaninfo
 546      *    preferred:   true
 547      *    description: Sets the selected item in the JComboBox.
 548      */
 549     public void setSelectedItem(Object anObject) {
 550         Object oldSelection = selectedItemReminder;
 551         Object objectToSelect = anObject;
 552         if (oldSelection == null || !oldSelection.equals(anObject)) {
 553 
 554             if (anObject != null && !isEditable()) {
 555                 // For non editable combo boxes, an invalid selection
 556                 // will be rejected.
 557                 boolean found = false;
 558                 for (int i = 0; i < dataModel.getSize(); i++) {
 559                     Object element = dataModel.getElementAt(i);
 560                     if (anObject.equals(element)) {
 561                         found = true;
 562                         objectToSelect = element;
 563                         break;
 564                     }
 565                 }
 566                 if (!found) {
 567                     return;
 568                 }
 569             }
 570 
 571             // Must toggle the state of this flag since this method
 572             // call may result in ListDataEvents being fired.
 573             selectingItem = true;
 574             dataModel.setSelectedItem(objectToSelect);
 575             selectingItem = false;
 576 
 577             if (selectedItemReminder != dataModel.getSelectedItem()) {
 578                 // in case a users implementation of ComboBoxModel
 579                 // doesn't fire a ListDataEvent when the selection
 580                 // changes.
 581                 selectedItemChanged();
 582             }
 583         }
 584         fireActionEvent();
 585     }
 586 
 587     /**
 588      * Returns the current selected item.
 589      * <p>
 590      * If the combo box is editable, then this value may not have been added
 591      * to the combo box with <code>addItem</code>, <code>insertItemAt</code>
 592      * or the data constructors.
 593      *
 594      * @return the current selected Object
 595      * @see #setSelectedItem
 596      */
 597     public Object getSelectedItem() {
 598         return dataModel.getSelectedItem();
 599     }
 600 
 601     /**
 602      * Selects the item at index <code>anIndex</code>.
 603      *
 604      * @param anIndex an integer specifying the list item to select,
 605      *                  where 0 specifies the first item in the list and -1 indicates no selection
 606      * @exception IllegalArgumentException if <code>anIndex</code> < -1 or
 607      *                  <code>anIndex</code> is greater than or equal to size
 608      * @beaninfo
 609      *   preferred: true
 610      *  description: The item at index is selected.
 611      */
 612     public void setSelectedIndex(int anIndex) {
 613         int size = dataModel.getSize();
 614 
 615         if ( anIndex == -1 ) {
 616             setSelectedItem( null );
 617         } else if ( anIndex < -1 || anIndex >= size ) {
 618             throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
 619         } else {
 620             setSelectedItem(dataModel.getElementAt(anIndex));
 621         }
 622     }
 623 
 624     /**
 625      * Returns the first item in the list that matches the given item.
 626      * The result is not always defined if the <code>JComboBox</code>
 627      * allows selected items that are not in the list.
 628      * Returns -1 if there is no selected item or if the user specified
 629      * an item which is not in the list.
 630 
 631      * @return an integer specifying the currently selected list item,
 632      *                  where 0 specifies
 633      *                  the first item in the list;
 634      *                  or -1 if no item is selected or if
 635      *                  the currently selected item is not in the list
 636      */
 637     public int getSelectedIndex() {
 638         Object sObject = dataModel.getSelectedItem();
 639         int i,c;
 640         Object obj;
 641 
 642         for ( i=0,c=dataModel.getSize();i<c;i++ ) {
 643             obj = dataModel.getElementAt(i);
 644             if ( obj != null && obj.equals(sObject) )
 645                 return i;
 646         }
 647         return -1;
 648     }
 649 
 650     /**
 651      * Returns the "prototypical display" value - an Object used
 652      * for the calculation of the display height and width.
 653      *
 654      * @return the value of the <code>prototypeDisplayValue</code> property
 655      * @see #setPrototypeDisplayValue
 656      * @since 1.4
 657      */
 658     public Object getPrototypeDisplayValue() {
 659         return prototypeDisplayValue;
 660     }
 661 
 662     /**
 663      * Sets the prototype display value used to calculate the size of the display
 664      * for the UI portion.
 665      * <p>
 666      * If a prototype display value is specified, the preferred size of
 667      * the combo box is calculated by configuring the renderer with the
 668      * prototype display value and obtaining its preferred size. Specifying
 669      * the preferred display value is often useful when the combo box will be
 670      * displaying large amounts of data. If no prototype display value has
 671      * been specified, the renderer must be configured for each value from
 672      * the model and its preferred size obtained, which can be
 673      * relatively expensive.
 674      *
 675      * @param prototypeDisplayValue
 676      * @see #getPrototypeDisplayValue
 677      * @since 1.4
 678      * @beaninfo
 679      *       bound: true
 680      *   attribute: visualUpdate true
 681      * description: The display prototype value, used to compute display width and height.
 682      */
 683     public void setPrototypeDisplayValue(Object prototypeDisplayValue) {
 684         Object oldValue = this.prototypeDisplayValue;
 685         this.prototypeDisplayValue = prototypeDisplayValue;
 686         firePropertyChange("prototypeDisplayValue", oldValue, prototypeDisplayValue);
 687     }
 688 
 689     /**
 690      * Adds an item to the item list.
 691      * This method works only if the <code>JComboBox</code> uses a
 692      * mutable data model.
 693      * <p>
 694      * <strong>Warning:</strong>
 695      * Focus and keyboard navigation problems may arise if you add duplicate
 696      * String objects. A workaround is to add new objects instead of String
 697      * objects and make sure that the toString() method is defined.
 698      * For example:
 699      * <pre>
 700      *   comboBox.addItem(makeObj("Item 1"));
 701      *   comboBox.addItem(makeObj("Item 1"));
 702      *   ...
 703      *   private Object makeObj(final String item)  {
 704      *     return new Object() { public String toString() { return item; } };
 705      *   }
 706      * </pre>
 707      *
 708      * @param anObject the Object to add to the list
 709      * @see MutableComboBoxModel
 710      */
 711     public void addItem(Object anObject) {
 712         checkMutableComboBoxModel();
 713         ((MutableComboBoxModel)dataModel).addElement(anObject);
 714     }
 715 
 716     /**
 717      * Inserts an item into the item list at a given index.
 718      * This method works only if the <code>JComboBox</code> uses a
 719      * mutable data model.
 720      *
 721      * @param anObject the <code>Object</code> to add to the list
 722      * @param index    an integer specifying the position at which
 723      *                  to add the item
 724      * @see MutableComboBoxModel
 725      */
 726     public void insertItemAt(Object anObject, int index) {
 727         checkMutableComboBoxModel();
 728         ((MutableComboBoxModel)dataModel).insertElementAt(anObject,index);
 729     }
 730 
 731     /**
 732      * Removes an item from the item list.
 733      * This method works only if the <code>JComboBox</code> uses a
 734      * mutable data model.
 735      *
 736      * @param anObject  the object to remove from the item list
 737      * @see MutableComboBoxModel
 738      */
 739     public void removeItem(Object anObject) {
 740         checkMutableComboBoxModel();
 741         ((MutableComboBoxModel)dataModel).removeElement(anObject);
 742     }
 743 
 744     /**
 745      * Removes the item at <code>anIndex</code>
 746      * This method works only if the <code>JComboBox</code> uses a
 747      * mutable data model.
 748      *
 749      * @param anIndex  an int specifying the index of the item to remove,
 750      *                  where 0
 751      *                  indicates the first item in the list
 752      * @see MutableComboBoxModel
 753      */
 754     public void removeItemAt(int anIndex) {
 755         checkMutableComboBoxModel();
 756         ((MutableComboBoxModel)dataModel).removeElementAt( anIndex );
 757     }
 758 
 759     /**
 760      * Removes all items from the item list.
 761      */
 762     public void removeAllItems() {
 763         checkMutableComboBoxModel();
 764         MutableComboBoxModel model = (MutableComboBoxModel)dataModel;
 765         int size = model.getSize();
 766 
 767         if ( model instanceof DefaultComboBoxModel ) {
 768             ((DefaultComboBoxModel)model).removeAllElements();
 769         }
 770         else {
 771             for ( int i = 0; i < size; ++i ) {
 772                 Object element = model.getElementAt( 0 );
 773                 model.removeElement( element );
 774             }
 775         }
 776         selectedItemReminder = null;
 777         if (isEditable()) {
 778             editor.setItem(null);
 779         }
 780     }
 781 
 782     /**
 783      * Checks that the <code>dataModel</code> is an instance of
 784      * <code>MutableComboBoxModel</code>.  If not, it throws an exception.
 785      * @exception RuntimeException if <code>dataModel</code> is not an
 786      *          instance of <code>MutableComboBoxModel</code>.
 787      */
 788     void checkMutableComboBoxModel() {
 789         if ( !(dataModel instanceof MutableComboBoxModel) )
 790             throw new RuntimeException("Cannot use this method with a non-Mutable data model.");
 791     }
 792 
 793     /**
 794      * Causes the combo box to display its popup window.
 795      * @see #setPopupVisible
 796      */
 797     public void showPopup() {
 798         setPopupVisible(true);
 799     }
 800 
 801     /**
 802      * Causes the combo box to close its popup window.
 803      * @see #setPopupVisible
 804      */
 805     public void hidePopup() {
 806         setPopupVisible(false);
 807     }
 808 
 809     /**
 810      * Sets the visibility of the popup.
 811      */
 812     public void setPopupVisible(boolean v) {
 813         getUI().setPopupVisible(this, v);
 814     }
 815 
 816     /**
 817      * Determines the visibility of the popup.
 818      *
 819      * @return true if the popup is visible, otherwise returns false
 820      */
 821     public boolean isPopupVisible() {
 822         return getUI().isPopupVisible(this);
 823     }
 824 
 825     /** Selection **/
 826 
 827     /**
 828      * Adds an <code>ItemListener</code>.
 829      * <p>
 830      * <code>aListener</code> will receive one or two <code>ItemEvent</code>s when
 831      * the selected item changes.
 832      *
 833      * @param aListener the <code>ItemListener</code> that is to be notified
 834      * @see #setSelectedItem
 835      */
 836     public void addItemListener(ItemListener aListener) {
 837         listenerList.add(ItemListener.class,aListener);
 838     }
 839 
 840     /** Removes an <code>ItemListener</code>.
 841      *
 842      * @param aListener  the <code>ItemListener</code> to remove
 843      */
 844     public void removeItemListener(ItemListener aListener) {
 845         listenerList.remove(ItemListener.class,aListener);
 846     }
 847 
 848     /**
 849      * Returns an array of all the <code>ItemListener</code>s added
 850      * to this JComboBox with addItemListener().
 851      *
 852      * @return all of the <code>ItemListener</code>s added or an empty
 853      *         array if no listeners have been added
 854      * @since 1.4
 855      */
 856     public ItemListener[] getItemListeners() {
 857         return (ItemListener[])listenerList.getListeners(ItemListener.class);
 858     }
 859 
 860     /**
 861      * Adds an <code>ActionListener</code>.
 862      * <p>
 863      * The <code>ActionListener</code> will receive an <code>ActionEvent</code>
 864      * when a selection has been made. If the combo box is editable, then
 865      * an <code>ActionEvent</code> will be fired when editing has stopped.
 866      *
 867      * @param l  the <code>ActionListener</code> that is to be notified
 868      * @see #setSelectedItem
 869      */
 870     public void addActionListener(ActionListener l) {
 871         listenerList.add(ActionListener.class,l);
 872     }
 873 
 874     /** Removes an <code>ActionListener</code>.
 875      *
 876      * @param l  the <code>ActionListener</code> to remove
 877      */
 878     public void removeActionListener(ActionListener l) {
 879         if ((l != null) && (getAction() == l)) {
 880             setAction(null);
 881         } else {
 882             listenerList.remove(ActionListener.class, l);
 883         }
 884     }
 885 
 886     /**
 887      * Returns an array of all the <code>ActionListener</code>s added
 888      * to this JComboBox with addActionListener().
 889      *
 890      * @return all of the <code>ActionListener</code>s added or an empty
 891      *         array if no listeners have been added
 892      * @since 1.4
 893      */
 894     public ActionListener[] getActionListeners() {
 895         return (ActionListener[])listenerList.getListeners(
 896                 ActionListener.class);
 897     }
 898 
 899     /**
 900      * Adds a <code>PopupMenu</code> listener which will listen to notification
 901      * messages from the popup portion of the combo box.
 902      * <p>
 903      * For all standard look and feels shipped with Java, the popup list
 904      * portion of combo box is implemented as a <code>JPopupMenu</code>.
 905      * A custom look and feel may not implement it this way and will
 906      * therefore not receive the notification.
 907      *
 908      * @param l  the <code>PopupMenuListener</code> to add
 909      * @since 1.4
 910      */
 911     public void addPopupMenuListener(PopupMenuListener l) {
 912         listenerList.add(PopupMenuListener.class,l);
 913     }
 914 
 915     /**
 916      * Removes a <code>PopupMenuListener</code>.
 917      *
 918      * @param l  the <code>PopupMenuListener</code> to remove
 919      * @see #addPopupMenuListener
 920      * @since 1.4
 921      */
 922     public void removePopupMenuListener(PopupMenuListener l) {
 923         listenerList.remove(PopupMenuListener.class,l);
 924     }
 925 
 926     /**
 927      * Returns an array of all the <code>PopupMenuListener</code>s added
 928      * to this JComboBox with addPopupMenuListener().
 929      *
 930      * @return all of the <code>PopupMenuListener</code>s added or an empty
 931      *         array if no listeners have been added
 932      * @since 1.4
 933      */
 934     public PopupMenuListener[] getPopupMenuListeners() {
 935         return (PopupMenuListener[])listenerList.getListeners(
 936                 PopupMenuListener.class);
 937     }
 938 
 939     /**
 940      * Notifies <code>PopupMenuListener</code>s that the popup portion of the
 941      * combo box will become visible.
 942      * <p>
 943      * This method is public but should not be called by anything other than
 944      * the UI delegate.
 945      * @see #addPopupMenuListener
 946      * @since 1.4
 947      */
 948     public void firePopupMenuWillBecomeVisible() {
 949         Object[] listeners = listenerList.getListenerList();
 950         PopupMenuEvent e=null;
 951         for (int i = listeners.length-2; i>=0; i-=2) {
 952             if (listeners[i]==PopupMenuListener.class) {
 953                 if (e == null)
 954                     e = new PopupMenuEvent(this);
 955                 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
 956             }
 957         }
 958     }
 959 
 960     /**
 961      * Notifies <code>PopupMenuListener</code>s that the popup portion of the
 962      * combo box has become invisible.
 963      * <p>
 964      * This method is public but should not be called by anything other than
 965      * the UI delegate.
 966      * @see #addPopupMenuListener
 967      * @since 1.4
 968      */
 969     public void firePopupMenuWillBecomeInvisible() {
 970         Object[] listeners = listenerList.getListenerList();
 971         PopupMenuEvent e=null;
 972         for (int i = listeners.length-2; i>=0; i-=2) {
 973             if (listeners[i]==PopupMenuListener.class) {
 974                 if (e == null)
 975                     e = new PopupMenuEvent(this);
 976                 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
 977             }
 978         }
 979     }
 980 
 981     /**
 982      * Notifies <code>PopupMenuListener</code>s that the popup portion of the
 983      * combo box has been canceled.
 984      * <p>
 985      * This method is public but should not be called by anything other than
 986      * the UI delegate.
 987      * @see #addPopupMenuListener
 988      * @since 1.4
 989      */
 990     public void firePopupMenuCanceled() {
 991         Object[] listeners = listenerList.getListenerList();
 992         PopupMenuEvent e=null;
 993         for (int i = listeners.length-2; i>=0; i-=2) {
 994             if (listeners[i]==PopupMenuListener.class) {
 995                 if (e == null)
 996                     e = new PopupMenuEvent(this);
 997                 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
 998             }
 999         }
1000     }
1001 
1002     /**
1003      * Sets the action command that should be included in the event
1004      * sent to action listeners.
1005      *
1006      * @param aCommand  a string containing the "command" that is sent
1007      *                  to action listeners; the same listener can then
1008      *                  do different things depending on the command it
1009      *                  receives
1010      */
1011     public void setActionCommand(String aCommand) {
1012         actionCommand = aCommand;
1013     }
1014 
1015     /**
1016      * Returns the action command that is included in the event sent to
1017      * action listeners.
1018      *
1019      * @return  the string containing the "command" that is sent
1020      *          to action listeners.
1021      */
1022     public String getActionCommand() {
1023         return actionCommand;
1024     }
1025 
1026     private Action action;
1027     private PropertyChangeListener actionPropertyChangeListener;
1028 
1029     /**
1030      * Sets the <code>Action</code> for the <code>ActionEvent</code> source.
1031      * The new <code>Action</code> replaces any previously set
1032      * <code>Action</code> but does not affect <code>ActionListeners</code>
1033      * independently added with <code>addActionListener</code>.
1034      * If the <code>Action</code> is already a registered
1035      * <code>ActionListener</code> for the <code>ActionEvent</code> source,
1036      * it is not re-registered.
1037      * <p>
1038      * Setting the <code>Action</code> results in immediately changing
1039      * all the properties described in <a href="Action.html#buttonActions">
1040      * Swing Components Supporting <code>Action</code></a>.
1041      * Subsequently, the combobox's properties are automatically updated
1042      * as the <code>Action</code>'s properties change.
1043      * <p>
1044      * This method uses three other methods to set
1045      * and help track the <code>Action</code>'s property values.
1046      * It uses the <code>configurePropertiesFromAction</code> method
1047      * to immediately change the combobox's properties.
1048      * To track changes in the <code>Action</code>'s property values,
1049      * this method registers the <code>PropertyChangeListener</code>
1050      * returned by <code>createActionPropertyChangeListener</code>. The
1051      * default {@code PropertyChangeListener} invokes the
1052      * {@code actionPropertyChanged} method when a property in the
1053      * {@code Action} changes.
1054      *
1055      * @param a the <code>Action</code> for the <code>JComboBox</code>,
1056      *                  or <code>null</code>.
1057      * @since 1.3
1058      * @see Action
1059      * @see #getAction
1060      * @see #configurePropertiesFromAction
1061      * @see #createActionPropertyChangeListener
1062      * @see #actionPropertyChanged
1063      * @beaninfo
1064      *        bound: true
1065      *    attribute: visualUpdate true
1066      *  description: the Action instance connected with this ActionEvent source
1067      */
1068     public void setAction(Action a) {
1069         Action oldValue = getAction();
1070         if (action==null || !action.equals(a)) {
1071             action = a;
1072             if (oldValue!=null) {
1073                 removeActionListener(oldValue);
1074                 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1075                 actionPropertyChangeListener = null;
1076             }
1077             configurePropertiesFromAction(action);
1078             if (action!=null) {
1079                 // Don't add if it is already a listener
1080                 if (!isListener(ActionListener.class, action)) {
1081                     addActionListener(action);
1082                 }
1083                 // Reverse linkage:
1084                 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1085                 action.addPropertyChangeListener(actionPropertyChangeListener);
1086             }
1087             firePropertyChange("action", oldValue, action);
1088         }
1089     }
1090 
1091     private boolean isListener(Class c, ActionListener a) {
1092         boolean isListener = false;
1093         Object[] listeners = listenerList.getListenerList();
1094         for (int i = listeners.length-2; i>=0; i-=2) {
1095             if (listeners[i]==c && listeners[i+1]==a) {
1096                     isListener=true;
1097             }
1098         }
1099         return isListener;
1100     }
1101 
1102     /**
1103      * Returns the currently set <code>Action</code> for this
1104      * <code>ActionEvent</code> source, or <code>null</code> if no
1105      * <code>Action</code> is set.
1106      *
1107      * @return the <code>Action</code> for this <code>ActionEvent</code>
1108      *          source; or <code>null</code>
1109      * @since 1.3
1110      * @see Action
1111      * @see #setAction
1112      */
1113     public Action getAction() {
1114         return action;
1115     }
1116 
1117     /**
1118      * Sets the properties on this combobox to match those in the specified
1119      * <code>Action</code>.  Refer to <a href="Action.html#buttonActions">
1120      * Swing Components Supporting <code>Action</code></a> for more
1121      * details as to which properties this sets.
1122      *
1123      * @param a the <code>Action</code> from which to get the properties,
1124      *          or <code>null</code>
1125      * @since 1.3
1126      * @see Action
1127      * @see #setAction
1128      */
1129     protected void configurePropertiesFromAction(Action a) {
1130         AbstractAction.setEnabledFromAction(this, a);
1131         AbstractAction.setToolTipTextFromAction(this, a);
1132         setActionCommandFromAction(a);
1133     }
1134 
1135     /**
1136      * Creates and returns a <code>PropertyChangeListener</code> that is
1137      * responsible for listening for changes from the specified
1138      * <code>Action</code> and updating the appropriate properties.
1139      * <p>
1140      * <b>Warning:</b> If you subclass this do not create an anonymous
1141      * inner class.  If you do the lifetime of the combobox will be tied to
1142      * that of the <code>Action</code>.
1143      *
1144      * @param a the combobox's action
1145      * @since 1.3
1146      * @see Action
1147      * @see #setAction
1148      */
1149     protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1150         return new ComboBoxActionPropertyChangeListener(this, a);
1151     }
1152 
1153     /**
1154      * Updates the combobox's state in response to property changes in
1155      * associated action. This method is invoked from the
1156      * {@code PropertyChangeListener} returned from
1157      * {@code createActionPropertyChangeListener}. Subclasses do not normally
1158      * need to invoke this. Subclasses that support additional {@code Action}
1159      * properties should override this and
1160      * {@code configurePropertiesFromAction}.
1161      * <p>
1162      * Refer to the table at <a href="Action.html#buttonActions">
1163      * Swing Components Supporting <code>Action</code></a> for a list of
1164      * the properties this method sets.
1165      *
1166      * @param action the <code>Action</code> associated with this combobox
1167      * @param propertyName the name of the property that changed
1168      * @since 1.6
1169      * @see Action
1170      * @see #configurePropertiesFromAction
1171      */
1172     protected void actionPropertyChanged(Action action, String propertyName) {
1173         if (propertyName == Action.ACTION_COMMAND_KEY) {
1174             setActionCommandFromAction(action);
1175         } else if (propertyName == "enabled") {
1176             AbstractAction.setEnabledFromAction(this, action);
1177         } else if (Action.SHORT_DESCRIPTION == propertyName) {
1178             AbstractAction.setToolTipTextFromAction(this, action);
1179         }
1180     }
1181 
1182     private void setActionCommandFromAction(Action a) {
1183         setActionCommand((a != null) ?
1184                              (String)a.getValue(Action.ACTION_COMMAND_KEY) :
1185                              null);
1186     }
1187 
1188 
1189     private static class ComboBoxActionPropertyChangeListener
1190                  extends ActionPropertyChangeListener<JComboBox> {
1191         ComboBoxActionPropertyChangeListener(JComboBox b, Action a) {
1192             super(b, a);
1193         }
1194         protected void actionPropertyChanged(JComboBox cb,
1195                                              Action action,
1196                                              PropertyChangeEvent e) {
1197             if (AbstractAction.shouldReconfigure(e)) {
1198                 cb.configurePropertiesFromAction(action);
1199             } else {
1200                 cb.actionPropertyChanged(action, e.getPropertyName());
1201             }
1202         }
1203     }
1204 
1205     /**
1206      * Notifies all listeners that have registered interest for
1207      * notification on this event type.
1208      * @param e  the event of interest
1209      *
1210      * @see EventListenerList
1211      */
1212     protected void fireItemStateChanged(ItemEvent e) {
1213         // Guaranteed to return a non-null array
1214         Object[] listeners = listenerList.getListenerList();
1215         // Process the listeners last to first, notifying
1216         // those that are interested in this event
1217         for ( int i = listeners.length-2; i>=0; i-=2 ) {
1218             if ( listeners[i]==ItemListener.class ) {
1219                 // Lazily create the event:
1220                 // if (changeEvent == null)
1221                 // changeEvent = new ChangeEvent(this);
1222                 ((ItemListener)listeners[i+1]).itemStateChanged(e);
1223             }
1224         }
1225     }
1226 
1227     /**
1228      * Notifies all listeners that have registered interest for
1229      * notification on this event type.
1230      *
1231      * @see EventListenerList
1232      */
1233     protected void fireActionEvent() {
1234         if (!firingActionEvent) {
1235             // Set flag to ensure that an infinite loop is not created
1236             firingActionEvent = true;
1237             ActionEvent e = null;
1238             // Guaranteed to return a non-null array
1239             Object[] listeners = listenerList.getListenerList();
1240             long mostRecentEventTime = EventQueue.getMostRecentEventTime();
1241             int modifiers = 0;
1242             AWTEvent currentEvent = EventQueue.getCurrentEvent();
1243             if (currentEvent instanceof InputEvent) {
1244                 modifiers = ((InputEvent)currentEvent).getModifiers();
1245             } else if (currentEvent instanceof ActionEvent) {
1246                 modifiers = ((ActionEvent)currentEvent).getModifiers();
1247             }
1248             // Process the listeners last to first, notifying
1249             // those that are interested in this event
1250             for ( int i = listeners.length-2; i>=0; i-=2 ) {
1251                 if ( listeners[i]==ActionListener.class ) {
1252                     // Lazily create the event:
1253                     if ( e == null )
1254                         e = new ActionEvent(this,ActionEvent.ACTION_PERFORMED,
1255                                             getActionCommand(),
1256                                             mostRecentEventTime, modifiers);
1257                     ((ActionListener)listeners[i+1]).actionPerformed(e);
1258                 }
1259             }
1260             firingActionEvent = false;
1261         }
1262     }
1263 
1264     /**
1265      * This protected method is implementation specific. Do not access directly
1266      * or override.
1267      */
1268     protected void selectedItemChanged() {
1269         if (selectedItemReminder != null ) {
1270             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1271                                                selectedItemReminder,
1272                                                ItemEvent.DESELECTED));
1273         }
1274 
1275         // set the new selected item.
1276         selectedItemReminder = dataModel.getSelectedItem();
1277 
1278         if (selectedItemReminder != null ) {
1279             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1280                                                selectedItemReminder,
1281                                                ItemEvent.SELECTED));
1282         }
1283     }
1284 
1285     /**
1286      * Returns an array containing the selected item.
1287      * This method is implemented for compatibility with
1288      * <code>ItemSelectable</code>.
1289      *
1290      * @return an array of <code>Objects</code> containing one
1291      *          element -- the selected item
1292      */
1293     public Object[] getSelectedObjects() {
1294         Object selectedObject = getSelectedItem();
1295         if ( selectedObject == null )
1296             return new Object[0];
1297         else {
1298             Object result[] = new Object[1];
1299             result[0] = selectedObject;
1300             return result;
1301         }
1302     }
1303 
1304     /**
1305      * This method is public as an implementation side effect.
1306      * do not call or override.
1307      */
1308     public void actionPerformed(ActionEvent e) {
1309         Object newItem = getEditor().getItem();
1310         setPopupVisible(false);
1311         getModel().setSelectedItem(newItem);
1312         String oldCommand = getActionCommand();
1313         setActionCommand("comboBoxEdited");
1314         fireActionEvent();
1315         setActionCommand(oldCommand);
1316     }
1317 
1318     /**
1319      * This method is public as an implementation side effect.
1320      * do not call or override.
1321      */
1322     public void contentsChanged(ListDataEvent e) {
1323         Object oldSelection = selectedItemReminder;
1324         Object newSelection = dataModel.getSelectedItem();
1325         if (oldSelection == null || !oldSelection.equals(newSelection)) {
1326             selectedItemChanged();
1327             if (!selectingItem) {
1328                 fireActionEvent();
1329             }
1330         }
1331     }
1332 
1333     /**
1334      * This method is public as an implementation side effect.
1335      * do not call or override.
1336      */
1337     public void intervalAdded(ListDataEvent e) {
1338         if (selectedItemReminder != dataModel.getSelectedItem()) {
1339             selectedItemChanged();
1340         }
1341     }
1342 
1343     /**
1344      * This method is public as an implementation side effect.
1345      * do not call or override.
1346      */
1347     public void intervalRemoved(ListDataEvent e) {
1348         contentsChanged(e);
1349     }
1350 
1351     /**
1352      * Selects the list item that corresponds to the specified keyboard
1353      * character and returns true, if there is an item corresponding
1354      * to that character.  Otherwise, returns false.
1355      *
1356      * @param keyChar a char, typically this is a keyboard key
1357      *                  typed by the user
1358      */
1359     public boolean selectWithKeyChar(char keyChar) {
1360         int index;
1361 
1362         if ( keySelectionManager == null )
1363             keySelectionManager = createDefaultKeySelectionManager();
1364 
1365         index = keySelectionManager.selectionForKey(keyChar,getModel());
1366         if ( index != -1 ) {
1367             setSelectedIndex(index);
1368             return true;
1369         }
1370         else
1371             return false;
1372     }
1373 
1374     /**
1375      * Enables the combo box so that items can be selected. When the
1376      * combo box is disabled, items cannot be selected and values
1377      * cannot be typed into its field (if it is editable).
1378      *
1379      * @param b a boolean value, where true enables the component and
1380      *          false disables it
1381      * @beaninfo
1382      *        bound: true
1383      *    preferred: true
1384      *  description: Whether the combo box is enabled.
1385      */
1386     public void setEnabled(boolean b) {
1387         super.setEnabled(b);
1388         firePropertyChange( "enabled", !isEnabled(), isEnabled() );
1389     }
1390 
1391     /**
1392      * Initializes the editor with the specified item.
1393      *
1394      * @param anEditor the <code>ComboBoxEditor</code> that displays
1395      *                  the list item in the
1396      *                  combo box field and allows it to be edited
1397      * @param anItem   the object to display and edit in the field
1398      */
1399     public void configureEditor(ComboBoxEditor anEditor, Object anItem) {
1400         anEditor.setItem(anItem);
1401     }
1402 
1403     /**
1404      * Handles <code>KeyEvent</code>s, looking for the Tab key.
1405      * If the Tab key is found, the popup window is closed.
1406      *
1407      * @param e  the <code>KeyEvent</code> containing the keyboard
1408      *          key that was pressed
1409      */
1410     public void processKeyEvent(KeyEvent e) {
1411         if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
1412             hidePopup();
1413         }
1414         super.processKeyEvent(e);
1415     }
1416 
1417     /**
1418      * Sets the object that translates a keyboard character into a list
1419      * selection. Typically, the first selection with a matching first
1420      * character becomes the selected item.
1421      *
1422      * @beaninfo
1423      *       expert: true
1424      *  description: The objects that changes the selection when a key is pressed.
1425      */
1426     public void setKeySelectionManager(KeySelectionManager aManager) {
1427         keySelectionManager = aManager;
1428     }
1429 
1430     /**
1431      * Returns the list's key-selection manager.
1432      *
1433      * @return the <code>KeySelectionManager</code> currently in use
1434      */
1435     public KeySelectionManager getKeySelectionManager() {
1436         return keySelectionManager;
1437     }
1438 
1439     /* Accessing the model */
1440     /**
1441      * Returns the number of items in the list.
1442      *
1443      * @return an integer equal to the number of items in the list
1444      */
1445     public int getItemCount() {
1446         return dataModel.getSize();
1447     }
1448 
1449     /**
1450      * Returns the list item at the specified index.  If <code>index</code>
1451      * is out of range (less than zero or greater than or equal to size)
1452      * it will return <code>null</code>.
1453      *
1454      * @param index  an integer indicating the list position, where the first
1455      *               item starts at zero
1456      * @return the <code>Object</code> at that list position; or
1457      *                  <code>null</code> if out of range
1458      */
1459     public Object getItemAt(int index) {
1460         return dataModel.getElementAt(index);
1461     }
1462 
1463     /**
1464      * Returns an instance of the default key-selection manager.
1465      *
1466      * @return the <code>KeySelectionManager</code> currently used by the list
1467      * @see #setKeySelectionManager
1468      */
1469     protected KeySelectionManager createDefaultKeySelectionManager() {
1470         return new DefaultKeySelectionManager();
1471     }
1472 
1473 
1474     /**
1475      * The interface that defines a <code>KeySelectionManager</code>.
1476      * To qualify as a <code>KeySelectionManager</code>,
1477      * the class needs to implement the method
1478      * that identifies the list index given a character and the
1479      * combo box data model.
1480      */
1481     public interface KeySelectionManager {
1482         /** Given <code>aKey</code> and the model, returns the row
1483          *  that should become selected. Return -1 if no match was
1484          *  found.
1485          *
1486          * @param  aKey  a char value, usually indicating a keyboard key that
1487          *               was pressed
1488          * @param aModel a ComboBoxModel -- the component's data model, containing
1489          *               the list of selectable items
1490          * @return an int equal to the selected row, where 0 is the
1491          *         first item and -1 is none.
1492          */
1493         int selectionForKey(char aKey,ComboBoxModel aModel);
1494     }
1495 
1496     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
1497         public int selectionForKey(char aKey,ComboBoxModel aModel) {
1498             int i,c;
1499             int currentSelection = -1;
1500             Object selectedItem = aModel.getSelectedItem();
1501             String v;
1502             String pattern;
1503 
1504             if ( selectedItem != null ) {
1505                 for ( i=0,c=aModel.getSize();i<c;i++ ) {
1506                     if ( selectedItem == aModel.getElementAt(i) ) {
1507                         currentSelection  =  i;
1508                         break;
1509                     }
1510                 }
1511             }
1512 
1513             pattern = ("" + aKey).toLowerCase();
1514             aKey = pattern.charAt(0);
1515 
1516             for ( i = ++currentSelection, c = aModel.getSize() ; i < c ; i++ ) {
1517                 Object elem = aModel.getElementAt(i);
1518                 if (elem != null && elem.toString() != null) {
1519                     v = elem.toString().toLowerCase();
1520                     if ( v.length() > 0 && v.charAt(0) == aKey )
1521                         return i;
1522                 }
1523             }
1524 
1525             for ( i = 0 ; i < currentSelection ; i ++ ) {
1526                 Object elem = aModel.getElementAt(i);
1527                 if (elem != null && elem.toString() != null) {
1528                     v = elem.toString().toLowerCase();
1529                     if ( v.length() > 0 && v.charAt(0) == aKey )
1530                         return i;
1531                 }
1532             }
1533             return -1;
1534         }
1535     }
1536 
1537 
1538     /**
1539      * See <code>readObject</code> and <code>writeObject</code> in
1540      * <code>JComponent</code> for more
1541      * information about serialization in Swing.
1542      */
1543     private void writeObject(ObjectOutputStream s) throws IOException {
1544         s.defaultWriteObject();
1545         if (getUIClassID().equals(uiClassID)) {
1546             byte count = JComponent.getWriteObjCounter(this);
1547             JComponent.setWriteObjCounter(this, --count);
1548             if (count == 0 && ui != null) {
1549                 ui.installUI(this);
1550             }
1551         }
1552     }
1553 
1554 
1555     /**
1556      * Returns a string representation of this <code>JComboBox</code>.
1557      * This method is intended to be used only for debugging purposes,
1558      * and the content and format of the returned string may vary between
1559      * implementations. The returned string may be empty but may not
1560      * be <code>null</code>.
1561      *
1562      * @return  a string representation of this <code>JComboBox</code>
1563      */
1564     protected String paramString() {
1565         String selectedItemReminderString = (selectedItemReminder != null ?
1566                                              selectedItemReminder.toString() :
1567                                              "");
1568         String isEditableString = (isEditable ? "true" : "false");
1569         String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
1570                                                 "true" : "false");
1571 
1572         return super.paramString() +
1573         ",isEditable=" + isEditableString +
1574         ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
1575         ",maximumRowCount=" + maximumRowCount +
1576         ",selectedItemReminder=" + selectedItemReminderString;
1577     }
1578 
1579 
1580 ///////////////////
1581 // Accessibility support
1582 ///////////////////
1583 
1584     /**
1585      * Gets the AccessibleContext associated with this JComboBox.
1586      * For combo boxes, the AccessibleContext takes the form of an
1587      * AccessibleJComboBox.
1588      * A new AccessibleJComboBox instance is created if necessary.
1589      *
1590      * @return an AccessibleJComboBox that serves as the
1591      *         AccessibleContext of this JComboBox
1592      */
1593     public AccessibleContext getAccessibleContext() {
1594         if ( accessibleContext == null ) {
1595             accessibleContext = new AccessibleJComboBox();
1596         }
1597         return accessibleContext;
1598     }
1599 
1600     /**
1601      * This class implements accessibility support for the
1602      * <code>JComboBox</code> class.  It provides an implementation of the
1603      * Java Accessibility API appropriate to Combo Box user-interface elements.
1604      * <p>
1605      * <strong>Warning:</strong>
1606      * Serialized objects of this class will not be compatible with
1607      * future Swing releases. The current serialization support is
1608      * appropriate for short term storage or RMI between applications running
1609      * the same version of Swing.  As of 1.4, support for long term storage
1610      * of all JavaBeans<sup><font size="-2">TM</font></sup>
1611      * has been added to the <code>java.beans</code> package.
1612      * Please see {@link java.beans.XMLEncoder}.
1613      */
1614     protected class AccessibleJComboBox extends AccessibleJComponent
1615     implements AccessibleAction, AccessibleSelection {
1616 
1617 
1618         private JList popupList; // combo box popup list
1619         private Accessible previousSelectedAccessible = null;
1620 
1621         /**
1622          * Returns an AccessibleJComboBox instance
1623          * @since 1.4
1624          */
1625         public AccessibleJComboBox() {
1626             // set the combo box editor's accessible name and description
1627             JComboBox.this.addPropertyChangeListener(new AccessibleJComboBoxPropertyChangeListener());
1628             setEditorNameAndDescription();
1629 
1630             // Get the popup list
1631             Accessible a = getUI().getAccessibleChild(JComboBox.this, 0);
1632             if (a instanceof javax.swing.plaf.basic.ComboPopup) {
1633                 // Listen for changes to the popup menu selection.
1634                 popupList = ((javax.swing.plaf.basic.ComboPopup)a).getList();
1635                 popupList.addListSelectionListener(
1636                     new AccessibleJComboBoxListSelectionListener());
1637             }
1638             // Listen for popup menu show/hide events
1639             JComboBox.this.addPopupMenuListener(
1640               new AccessibleJComboBoxPopupMenuListener());
1641         }
1642 
1643         /*
1644          * JComboBox PropertyChangeListener
1645          */
1646         private class AccessibleJComboBoxPropertyChangeListener
1647             implements PropertyChangeListener {
1648 
1649             public void propertyChange(PropertyChangeEvent e) {
1650                 if (e.getPropertyName() == "editor") {
1651                     // set the combo box editor's accessible name
1652                     // and description
1653                     setEditorNameAndDescription();
1654                 }
1655             }
1656         }
1657 
1658         /*
1659          * Sets the combo box editor's accessible name and descripton
1660          */
1661         private void setEditorNameAndDescription() {
1662             ComboBoxEditor editor = JComboBox.this.getEditor();
1663             if (editor != null) {
1664                 Component comp = editor.getEditorComponent();
1665                 if (comp instanceof Accessible) {
1666                     AccessibleContext ac = ((Accessible)comp).getAccessibleContext();
1667                     if (ac != null) { // may be null
1668                         ac.setAccessibleName(getAccessibleName());
1669                         ac.setAccessibleDescription(getAccessibleDescription());
1670                     }
1671                 }
1672             }
1673         }
1674 
1675         /*
1676          * Listener for combo box popup menu
1677          * TIGER - 4669379 4894434
1678          */
1679         private class AccessibleJComboBoxPopupMenuListener
1680             implements PopupMenuListener {
1681 
1682             /**
1683              *  This method is called before the popup menu becomes visible
1684              */
1685             public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
1686                 // save the initial selection
1687                 if (popupList == null) {
1688                     return;
1689                 }
1690                 int selectedIndex = popupList.getSelectedIndex();
1691                 if (selectedIndex < 0) {
1692                     return;
1693                 }
1694                 previousSelectedAccessible =
1695                     popupList.getAccessibleContext().getAccessibleChild(selectedIndex);
1696             }
1697 
1698             /**
1699              * This method is called before the popup menu becomes invisible
1700              * Note that a JPopupMenu can become invisible any time
1701              */
1702             public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
1703                 // ignore
1704             }
1705 
1706             /**
1707              * This method is called when the popup menu is canceled
1708              */
1709             public void popupMenuCanceled(PopupMenuEvent e) {
1710                 // ignore
1711             }
1712         }
1713 
1714         /*
1715          * Handles changes to the popup list selection.
1716          * TIGER - 4669379 4894434 4933143
1717          */
1718         private class AccessibleJComboBoxListSelectionListener
1719             implements ListSelectionListener {
1720 
1721             public void valueChanged(ListSelectionEvent e) {
1722                 if (popupList == null) {
1723                     return;
1724                 }
1725 
1726                 // Get the selected popup list item.
1727                 int selectedIndex = popupList.getSelectedIndex();
1728                 if (selectedIndex < 0) {
1729                     return;
1730                 }
1731                 Accessible selectedAccessible =
1732                     popupList.getAccessibleContext().getAccessibleChild(selectedIndex);
1733                 if (selectedAccessible == null) {
1734                     return;
1735                 }
1736 
1737                 // Fire a FOCUSED lost PropertyChangeEvent for the
1738                 // previously selected list item.
1739                 PropertyChangeEvent pce = null;
1740 
1741                 if (previousSelectedAccessible != null) {
1742                     pce = new PropertyChangeEvent(previousSelectedAccessible,
1743                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1744                         AccessibleState.FOCUSED, null);
1745                     firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1746                                        null, pce);
1747                 }
1748                 // Fire a FOCUSED gained PropertyChangeEvent for the
1749                 // currently selected list item.
1750                 pce = new PropertyChangeEvent(selectedAccessible,
1751                     AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1752                     null, AccessibleState.FOCUSED);
1753                 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1754                                    null, pce);
1755 
1756                 // Fire the ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY event
1757                 // for the combo box.
1758                 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
1759                                    previousSelectedAccessible, selectedAccessible);
1760 
1761                 // Save the previous selection.
1762                 previousSelectedAccessible = selectedAccessible;
1763             }
1764         }
1765 
1766 
1767         /**
1768          * Returns the number of accessible children in the object.  If all
1769          * of the children of this object implement Accessible, than this
1770          * method should return the number of children of this object.
1771          *
1772          * @return the number of accessible children in the object.
1773          */
1774         public int getAccessibleChildrenCount() {
1775             // Always delegate to the UI if it exists
1776             if (ui != null) {
1777                 return ui.getAccessibleChildrenCount(JComboBox.this);
1778             } else {
1779                 return super.getAccessibleChildrenCount();
1780             }
1781         }
1782 
1783         /**
1784          * Returns the nth Accessible child of the object.
1785          * The child at index zero represents the popup.
1786          * If the combo box is editable, the child at index one
1787          * represents the editor.
1788          *
1789          * @param i zero-based index of child
1790          * @return the nth Accessible child of the object
1791          */
1792         public Accessible getAccessibleChild(int i) {
1793             // Always delegate to the UI if it exists
1794             if (ui != null) {
1795                 return ui.getAccessibleChild(JComboBox.this, i);
1796             } else {
1797                return super.getAccessibleChild(i);
1798             }
1799         }
1800 
1801         /**
1802          * Get the role of this object.
1803          *
1804          * @return an instance of AccessibleRole describing the role of the
1805          * object
1806          * @see AccessibleRole
1807          */
1808         public AccessibleRole getAccessibleRole() {
1809             return AccessibleRole.COMBO_BOX;
1810         }
1811 
1812         /**
1813          * Gets the state set of this object.  The AccessibleStateSet of
1814          * an object is composed of a set of unique AccessibleStates.
1815          * A change in the AccessibleStateSet of an object will cause a
1816          * PropertyChangeEvent to be fired for the ACCESSIBLE_STATE_PROPERTY
1817          * property.
1818          *
1819          * @return an instance of AccessibleStateSet containing the
1820          * current state set of the object
1821          * @see AccessibleStateSet
1822          * @see AccessibleState
1823          * @see #addPropertyChangeListener
1824          *
1825          */
1826         public AccessibleStateSet getAccessibleStateSet() {
1827             // TIGER - 4489748
1828             AccessibleStateSet ass = super.getAccessibleStateSet();
1829             if (ass == null) {
1830                 ass = new AccessibleStateSet();
1831             }
1832             if (JComboBox.this.isPopupVisible()) {
1833                 ass.add(AccessibleState.EXPANDED);
1834             } else {
1835                 ass.add(AccessibleState.COLLAPSED);
1836             }
1837             return ass;
1838         }
1839 
1840         /**
1841          * Get the AccessibleAction associated with this object.  In the
1842          * implementation of the Java Accessibility API for this class,
1843          * return this object, which is responsible for implementing the
1844          * AccessibleAction interface on behalf of itself.
1845          *
1846          * @return this object
1847          */
1848         public AccessibleAction getAccessibleAction() {
1849             return this;
1850         }
1851 
1852         /**
1853          * Return a description of the specified action of the object.
1854          *
1855          * @param i zero-based index of the actions
1856          */
1857         public String getAccessibleActionDescription(int i) {
1858             if (i == 0) {
1859                 return UIManager.getString("ComboBox.togglePopupText");
1860             }
1861             else {
1862                 return null;
1863             }
1864         }
1865 
1866         /**
1867          * Returns the number of Actions available in this object.  The
1868          * default behavior of a combo box is to have one action.
1869          *
1870          * @return 1, the number of Actions in this object
1871          */
1872         public int getAccessibleActionCount() {
1873             return 1;
1874         }
1875 
1876         /**
1877          * Perform the specified Action on the object
1878          *
1879          * @param i zero-based index of actions
1880          * @return true if the the action was performed; else false.
1881          */
1882         public boolean doAccessibleAction(int i) {
1883             if (i == 0) {
1884                 setPopupVisible(!isPopupVisible());
1885                 return true;
1886             }
1887             else {
1888                 return false;
1889             }
1890         }
1891 
1892 
1893         /**
1894          * Get the AccessibleSelection associated with this object.  In the
1895          * implementation of the Java Accessibility API for this class,
1896          * return this object, which is responsible for implementing the
1897          * AccessibleSelection interface on behalf of itself.
1898          *
1899          * @return this object
1900          */
1901         public AccessibleSelection getAccessibleSelection() {
1902             return this;
1903         }
1904 
1905         /**
1906          * Returns the number of Accessible children currently selected.
1907          * If no children are selected, the return value will be 0.
1908          *
1909          * @return the number of items currently selected.
1910          * @since 1.3
1911          */
1912         public int getAccessibleSelectionCount() {
1913             Object o = JComboBox.this.getSelectedItem();
1914             if (o != null) {
1915                 return 1;
1916             } else {
1917                 return 0;
1918             }
1919         }
1920 
1921         /**
1922          * Returns an Accessible representing the specified selected child
1923          * in the popup.  If there isn't a selection, or there are
1924          * fewer children selected than the integer passed in, the return
1925          * value will be null.
1926          * <p>Note that the index represents the i-th selected child, which
1927          * is different from the i-th child.
1928          *
1929          * @param i the zero-based index of selected children
1930          * @return the i-th selected child
1931          * @see #getAccessibleSelectionCount
1932          * @since 1.3
1933          */
1934         public Accessible getAccessibleSelection(int i) {
1935             // Get the popup
1936             Accessible a =
1937                 JComboBox.this.getUI().getAccessibleChild(JComboBox.this, 0);
1938             if (a != null &&
1939                 a instanceof javax.swing.plaf.basic.ComboPopup) {
1940 
1941                 // get the popup list
1942                 JList list = ((javax.swing.plaf.basic.ComboPopup)a).getList();
1943 
1944                 // return the i-th selection in the popup list
1945                 AccessibleContext ac = list.getAccessibleContext();
1946                 if (ac != null) {
1947                     AccessibleSelection as = ac.getAccessibleSelection();
1948                     if (as != null) {
1949                         return as.getAccessibleSelection(i);
1950                     }
1951                 }
1952             }
1953             return null;
1954         }
1955 
1956         /**
1957          * Determines if the current child of this object is selected.
1958          *
1959          * @return true if the current child of this object is selected;
1960          *              else false
1961          * @param i the zero-based index of the child in this Accessible
1962          * object.
1963          * @see AccessibleContext#getAccessibleChild
1964          * @since 1.3
1965          */
1966         public boolean isAccessibleChildSelected(int i) {
1967             return JComboBox.this.getSelectedIndex() == i;
1968         }
1969 
1970         /**
1971          * Adds the specified Accessible child of the object to the object's
1972          * selection.  If the object supports multiple selections,
1973          * the specified child is added to any existing selection, otherwise
1974          * it replaces any existing selection in the object.  If the
1975          * specified child is already selected, this method has no effect.
1976          *
1977          * @param i the zero-based index of the child
1978          * @see AccessibleContext#getAccessibleChild
1979          * @since 1.3
1980          */
1981         public void addAccessibleSelection(int i) {
1982             // TIGER - 4856195
1983             clearAccessibleSelection();
1984             JComboBox.this.setSelectedIndex(i);
1985         }
1986 
1987         /**
1988          * Removes the specified child of the object from the object's
1989          * selection.  If the specified item isn't currently selected, this
1990          * method has no effect.
1991          *
1992          * @param i the zero-based index of the child
1993          * @see AccessibleContext#getAccessibleChild
1994          * @since 1.3
1995          */
1996         public void removeAccessibleSelection(int i) {
1997             if (JComboBox.this.getSelectedIndex() == i) {
1998                 clearAccessibleSelection();
1999             }
2000         }
2001 
2002         /**
2003          * Clears the selection in the object, so that no children in the
2004          * object are selected.
2005          * @since 1.3
2006          */
2007         public void clearAccessibleSelection() {
2008             JComboBox.this.setSelectedIndex(-1);
2009         }
2010 
2011         /**
2012          * Causes every child of the object to be selected
2013          * if the object supports multiple selections.
2014          * @since 1.3
2015          */
2016         public void selectAllAccessibleSelection() {
2017             // do nothing since multiple selection is not supported
2018         }
2019 
2020 //        public Accessible getAccessibleAt(Point p) {
2021 //            Accessible a = getAccessibleChild(1);
2022 //            if ( a != null ) {
2023 //                return a; // the editor
2024 //            }
2025 //            else {
2026 //                return getAccessibleChild(0); // the list
2027 //            }
2028 //        }
2029         private EditorAccessibleContext editorAccessibleContext = null;
2030 
2031         private class AccessibleEditor implements Accessible {
2032             public AccessibleContext getAccessibleContext() {
2033                 if (editorAccessibleContext == null) {
2034                     Component c = JComboBox.this.getEditor().getEditorComponent();
2035                     if (c instanceof Accessible) {
2036                         editorAccessibleContext =
2037                             new EditorAccessibleContext((Accessible)c);
2038                     }
2039                 }
2040                 return editorAccessibleContext;
2041             }
2042         }
2043 
2044         /*
2045          * Wrapper class for the AccessibleContext implemented by the
2046          * combo box editor.  Delegates all method calls except
2047          * getAccessibleIndexInParent to the editor.  The
2048          * getAccessibleIndexInParent method returns the selected
2049          * index in the combo box.
2050          */
2051         private class EditorAccessibleContext extends AccessibleContext {
2052 
2053             private AccessibleContext ac;
2054 
2055             private EditorAccessibleContext() {
2056             }
2057 
2058             /*
2059              * @param a the AccessibleContext implemented by the
2060              * combo box editor
2061              */
2062             EditorAccessibleContext(Accessible a) {
2063                 this.ac = a.getAccessibleContext();
2064             }
2065 
2066             /**
2067              * Gets the accessibleName property of this object.  The accessibleName
2068              * property of an object is a localized String that designates the purpose
2069              * of the object.  For example, the accessibleName property of a label
2070              * or button might be the text of the label or button itself.  In the
2071              * case of an object that doesn't display its name, the accessibleName
2072              * should still be set.  For example, in the case of a text field used
2073              * to enter the name of a city, the accessibleName for the en_US locale
2074              * could be 'city.'
2075              *
2076              * @return the localized name of the object; null if this
2077              * object does not have a name
2078              *
2079              * @see #setAccessibleName
2080              */
2081             public String getAccessibleName() {
2082                 return ac.getAccessibleName();
2083             }
2084 
2085             /**
2086              * Sets the localized accessible name of this object.  Changing the
2087              * name will cause a PropertyChangeEvent to be fired for the
2088              * ACCESSIBLE_NAME_PROPERTY property.
2089              *
2090              * @param s the new localized name of the object.
2091              *
2092              * @see #getAccessibleName
2093              * @see #addPropertyChangeListener
2094              *
2095              * @beaninfo
2096              *    preferred:   true
2097              *    description: Sets the accessible name for the component.
2098              */
2099             public void setAccessibleName(String s) {
2100                 ac.setAccessibleName(s);
2101             }
2102 
2103             /**
2104              * Gets the accessibleDescription property of this object.  The
2105              * accessibleDescription property of this object is a short localized
2106              * phrase describing the purpose of the object.  For example, in the
2107              * case of a 'Cancel' button, the accessibleDescription could be
2108              * 'Ignore changes and close dialog box.'
2109              *
2110              * @return the localized description of the object; null if
2111              * this object does not have a description
2112              *
2113              * @see #setAccessibleDescription
2114              */
2115             public String getAccessibleDescription() {
2116                 return ac.getAccessibleDescription();
2117             }
2118 
2119             /**
2120              * Sets the accessible description of this object.  Changing the
2121              * name will cause a PropertyChangeEvent to be fired for the
2122              * ACCESSIBLE_DESCRIPTION_PROPERTY property.
2123              *
2124              * @param s the new localized description of the object
2125              *
2126              * @see #setAccessibleName
2127              * @see #addPropertyChangeListener
2128              *
2129              * @beaninfo
2130              *    preferred:   true
2131              *    description: Sets the accessible description for the component.
2132              */
2133             public void setAccessibleDescription(String s) {
2134                 ac.setAccessibleDescription(s);
2135             }
2136 
2137             /**
2138              * Gets the role of this object.  The role of the object is the generic
2139              * purpose or use of the class of this object.  For example, the role
2140              * of a push button is AccessibleRole.PUSH_BUTTON.  The roles in
2141              * AccessibleRole are provided so component developers can pick from
2142              * a set of predefined roles.  This enables assistive technologies to
2143              * provide a consistent interface to various tweaked subclasses of
2144              * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
2145              * that act like a push button) as well as distinguish between sublasses
2146              * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
2147              * and AccessibleRole.RADIO_BUTTON for radio buttons).
2148              * <p>Note that the AccessibleRole class is also extensible, so
2149              * custom component developers can define their own AccessibleRole's
2150              * if the set of predefined roles is inadequate.
2151              *
2152              * @return an instance of AccessibleRole describing the role of the object
2153              * @see AccessibleRole
2154              */
2155             public AccessibleRole getAccessibleRole() {
2156                 return ac.getAccessibleRole();
2157             }
2158 
2159             /**
2160              * Gets the state set of this object.  The AccessibleStateSet of an object
2161              * is composed of a set of unique AccessibleStates.  A change in the
2162              * AccessibleStateSet of an object will cause a PropertyChangeEvent to
2163              * be fired for the ACCESSIBLE_STATE_PROPERTY property.
2164              *
2165              * @return an instance of AccessibleStateSet containing the
2166              * current state set of the object
2167              * @see AccessibleStateSet
2168              * @see AccessibleState
2169              * @see #addPropertyChangeListener
2170              */
2171             public AccessibleStateSet getAccessibleStateSet() {
2172                 return ac.getAccessibleStateSet();
2173             }
2174 
2175             /**
2176              * Gets the Accessible parent of this object.
2177              *
2178              * @return the Accessible parent of this object; null if this
2179              * object does not have an Accessible parent
2180              */
2181             public Accessible getAccessibleParent() {
2182                 return ac.getAccessibleParent();
2183             }
2184 
2185             /**
2186              * Sets the Accessible parent of this object.  This is meant to be used
2187              * only in the situations where the actual component's parent should
2188              * not be treated as the component's accessible parent and is a method
2189              * that should only be called by the parent of the accessible child.
2190              *
2191              * @param a - Accessible to be set as the parent
2192              */
2193             public void setAccessibleParent(Accessible a) {
2194                 ac.setAccessibleParent(a);
2195             }
2196 
2197             /**
2198              * Gets the 0-based index of this object in its accessible parent.
2199              *
2200              * @return the 0-based index of this object in its parent; -1 if this
2201              * object does not have an accessible parent.
2202              *
2203              * @see #getAccessibleParent
2204              * @see #getAccessibleChildrenCount
2205              * @see #getAccessibleChild
2206              */
2207             public int getAccessibleIndexInParent() {
2208                 return JComboBox.this.getSelectedIndex();
2209             }
2210 
2211             /**
2212              * Returns the number of accessible children of the object.
2213              *
2214              * @return the number of accessible children of the object.
2215              */
2216             public int getAccessibleChildrenCount() {
2217                 return ac.getAccessibleChildrenCount();
2218             }
2219 
2220             /**
2221              * Returns the specified Accessible child of the object.  The Accessible
2222              * children of an Accessible object are zero-based, so the first child
2223              * of an Accessible child is at index 0, the second child is at index 1,
2224              * and so on.
2225              *
2226              * @param i zero-based index of child
2227              * @return the Accessible child of the object
2228              * @see #getAccessibleChildrenCount
2229              */
2230             public Accessible getAccessibleChild(int i) {
2231                 return ac.getAccessibleChild(i);
2232             }
2233 
2234             /**
2235              * Gets the locale of the component. If the component does not have a
2236              * locale, then the locale of its parent is returned.
2237              *
2238              * @return this component's locale.  If this component does not have
2239              * a locale, the locale of its parent is returned.
2240              *
2241              * @exception IllegalComponentStateException
2242              * If the Component does not have its own locale and has not yet been
2243              * added to a containment hierarchy such that the locale can be
2244              * determined from the containing parent.
2245              */
2246             public Locale getLocale() throws IllegalComponentStateException {
2247                 return ac.getLocale();
2248             }
2249 
2250             /**
2251              * Adds a PropertyChangeListener to the listener list.
2252              * The listener is registered for all Accessible properties and will
2253              * be called when those properties change.
2254              *
2255              * @see #ACCESSIBLE_NAME_PROPERTY
2256              * @see #ACCESSIBLE_DESCRIPTION_PROPERTY
2257              * @see #ACCESSIBLE_STATE_PROPERTY
2258              * @see #ACCESSIBLE_VALUE_PROPERTY
2259              * @see #ACCESSIBLE_SELECTION_PROPERTY
2260              * @see #ACCESSIBLE_TEXT_PROPERTY
2261              * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY
2262              *
2263              * @param listener  The PropertyChangeListener to be added
2264              */
2265             public void addPropertyChangeListener(PropertyChangeListener listener) {
2266                 ac.addPropertyChangeListener(listener);
2267             }
2268 
2269             /**
2270              * Removes a PropertyChangeListener from the listener list.
2271              * This removes a PropertyChangeListener that was registered
2272              * for all properties.
2273              *
2274              * @param listener  The PropertyChangeListener to be removed
2275              */
2276             public void removePropertyChangeListener(PropertyChangeListener listener) {
2277                 ac.removePropertyChangeListener(listener);
2278             }
2279 
2280             /**
2281              * Gets the AccessibleAction associated with this object that supports
2282              * one or more actions.
2283              *
2284              * @return AccessibleAction if supported by object; else return null
2285              * @see AccessibleAction
2286              */
2287             public AccessibleAction getAccessibleAction() {
2288                 return ac.getAccessibleAction();
2289             }
2290 
2291             /**
2292              * Gets the AccessibleComponent associated with this object that has a
2293              * graphical representation.
2294              *
2295              * @return AccessibleComponent if supported by object; else return null
2296              * @see AccessibleComponent
2297              */
2298             public AccessibleComponent getAccessibleComponent() {
2299                 return ac.getAccessibleComponent();
2300             }
2301 
2302             /**
2303              * Gets the AccessibleSelection associated with this object which allows its
2304              * Accessible children to be selected.
2305              *
2306              * @return AccessibleSelection if supported by object; else return null
2307              * @see AccessibleSelection
2308              */
2309             public AccessibleSelection getAccessibleSelection() {
2310                 return ac.getAccessibleSelection();
2311             }
2312 
2313             /**
2314              * Gets the AccessibleText associated with this object presenting
2315              * text on the display.
2316              *
2317              * @return AccessibleText if supported by object; else return null
2318              * @see AccessibleText
2319              */
2320             public AccessibleText getAccessibleText() {
2321                 return ac.getAccessibleText();
2322             }
2323 
2324             /**
2325              * Gets the AccessibleEditableText associated with this object
2326              * presenting editable text on the display.
2327              *
2328              * @return AccessibleEditableText if supported by object; else return null
2329              * @see AccessibleEditableText
2330              */
2331             public AccessibleEditableText getAccessibleEditableText() {
2332                 return ac.getAccessibleEditableText();
2333             }
2334 
2335             /**
2336              * Gets the AccessibleValue associated with this object that supports a
2337              * Numerical value.
2338              *
2339              * @return AccessibleValue if supported by object; else return null
2340              * @see AccessibleValue
2341              */
2342             public AccessibleValue getAccessibleValue() {
2343                 return ac.getAccessibleValue();
2344             }
2345 
2346             /**
2347              * Gets the AccessibleIcons associated with an object that has
2348              * one or more associated icons
2349              *
2350              * @return an array of AccessibleIcon if supported by object;
2351              * otherwise return null
2352              * @see AccessibleIcon
2353              */
2354             public AccessibleIcon [] getAccessibleIcon() {
2355                 return ac.getAccessibleIcon();
2356             }
2357 
2358             /**
2359              * Gets the AccessibleRelationSet associated with an object
2360              *
2361              * @return an AccessibleRelationSet if supported by object;
2362              * otherwise return null
2363              * @see AccessibleRelationSet
2364              */
2365             public AccessibleRelationSet getAccessibleRelationSet() {
2366                 return ac.getAccessibleRelationSet();
2367             }
2368 
2369             /**
2370              * Gets the AccessibleTable associated with an object
2371              *
2372              * @return an AccessibleTable if supported by object;
2373              * otherwise return null
2374              * @see AccessibleTable
2375              */
2376             public AccessibleTable getAccessibleTable() {
2377                 return ac.getAccessibleTable();
2378             }
2379 
2380             /**
2381              * Support for reporting bound property changes.  If oldValue and
2382              * newValue are not equal and the PropertyChangeEvent listener list
2383              * is not empty, then fire a PropertyChange event to each listener.
2384              * In general, this is for use by the Accessible objects themselves
2385              * and should not be called by an application program.
2386              * @param propertyName  The programmatic name of the property that
2387              * was changed.
2388              * @param oldValue  The old value of the property.
2389              * @param newValue  The new value of the property.
2390              * @see java.beans.PropertyChangeSupport
2391              * @see #addPropertyChangeListener
2392              * @see #removePropertyChangeListener
2393              * @see #ACCESSIBLE_NAME_PROPERTY
2394              * @see #ACCESSIBLE_DESCRIPTION_PROPERTY
2395              * @see #ACCESSIBLE_STATE_PROPERTY
2396              * @see #ACCESSIBLE_VALUE_PROPERTY
2397              * @see #ACCESSIBLE_SELECTION_PROPERTY
2398              * @see #ACCESSIBLE_TEXT_PROPERTY
2399              * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY
2400              */
2401             public void firePropertyChange(String propertyName,
2402                                            Object oldValue,
2403                                            Object newValue) {
2404                 ac.firePropertyChange(propertyName, oldValue, newValue);
2405             }
2406         }
2407 
2408     } // innerclass AccessibleJComboBox
2409 }