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 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 listenerList.getListeners(ActionListener.class);
 896     }
 897 
 898     /**
 899      * Adds a <code>PopupMenu</code> listener which will listen to notification
 900      * messages from the popup portion of the combo box.
 901      * <p>
 902      * For all standard look and feels shipped with Java, the popup list
 903      * portion of combo box is implemented as a <code>JPopupMenu</code>.
 904      * A custom look and feel may not implement it this way and will
 905      * therefore not receive the notification.
 906      *
 907      * @param l  the <code>PopupMenuListener</code> to add
 908      * @since 1.4
 909      */
 910     public void addPopupMenuListener(PopupMenuListener l) {
 911         listenerList.add(PopupMenuListener.class,l);
 912     }
 913 
 914     /**
 915      * Removes a <code>PopupMenuListener</code>.
 916      *
 917      * @param l  the <code>PopupMenuListener</code> to remove
 918      * @see #addPopupMenuListener
 919      * @since 1.4
 920      */
 921     public void removePopupMenuListener(PopupMenuListener l) {
 922         listenerList.remove(PopupMenuListener.class,l);
 923     }
 924 
 925     /**
 926      * Returns an array of all the <code>PopupMenuListener</code>s added
 927      * to this JComboBox with addPopupMenuListener().
 928      *
 929      * @return all of the <code>PopupMenuListener</code>s added or an empty
 930      *         array if no listeners have been added
 931      * @since 1.4
 932      */
 933     public PopupMenuListener[] getPopupMenuListeners() {
 934         return listenerList.getListeners(PopupMenuListener.class);
 935     }
 936 
 937     /**
 938      * Notifies <code>PopupMenuListener</code>s that the popup portion of the
 939      * combo box will become visible.
 940      * <p>
 941      * This method is public but should not be called by anything other than
 942      * the UI delegate.
 943      * @see #addPopupMenuListener
 944      * @since 1.4
 945      */
 946     public void firePopupMenuWillBecomeVisible() {
 947         Object[] listeners = listenerList.getListenerList();
 948         PopupMenuEvent e=null;
 949         for (int i = listeners.length-2; i>=0; i-=2) {
 950             if (listeners[i]==PopupMenuListener.class) {
 951                 if (e == null)
 952                     e = new PopupMenuEvent(this);
 953                 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
 954             }
 955         }
 956     }
 957 
 958     /**
 959      * Notifies <code>PopupMenuListener</code>s that the popup portion of the
 960      * combo box has become invisible.
 961      * <p>
 962      * This method is public but should not be called by anything other than
 963      * the UI delegate.
 964      * @see #addPopupMenuListener
 965      * @since 1.4
 966      */
 967     public void firePopupMenuWillBecomeInvisible() {
 968         Object[] listeners = listenerList.getListenerList();
 969         PopupMenuEvent e=null;
 970         for (int i = listeners.length-2; i>=0; i-=2) {
 971             if (listeners[i]==PopupMenuListener.class) {
 972                 if (e == null)
 973                     e = new PopupMenuEvent(this);
 974                 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
 975             }
 976         }
 977     }
 978 
 979     /**
 980      * Notifies <code>PopupMenuListener</code>s that the popup portion of the
 981      * combo box has been canceled.
 982      * <p>
 983      * This method is public but should not be called by anything other than
 984      * the UI delegate.
 985      * @see #addPopupMenuListener
 986      * @since 1.4
 987      */
 988     public void firePopupMenuCanceled() {
 989         Object[] listeners = listenerList.getListenerList();
 990         PopupMenuEvent e=null;
 991         for (int i = listeners.length-2; i>=0; i-=2) {
 992             if (listeners[i]==PopupMenuListener.class) {
 993                 if (e == null)
 994                     e = new PopupMenuEvent(this);
 995                 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
 996             }
 997         }
 998     }
 999 
1000     /**
1001      * Sets the action command that should be included in the event
1002      * sent to action listeners.
1003      *
1004      * @param aCommand  a string containing the "command" that is sent
1005      *                  to action listeners; the same listener can then
1006      *                  do different things depending on the command it
1007      *                  receives
1008      */
1009     public void setActionCommand(String aCommand) {
1010         actionCommand = aCommand;
1011     }
1012 
1013     /**
1014      * Returns the action command that is included in the event sent to
1015      * action listeners.
1016      *
1017      * @return  the string containing the "command" that is sent
1018      *          to action listeners.
1019      */
1020     public String getActionCommand() {
1021         return actionCommand;
1022     }
1023 
1024     private Action action;
1025     private PropertyChangeListener actionPropertyChangeListener;
1026 
1027     /**
1028      * Sets the <code>Action</code> for the <code>ActionEvent</code> source.
1029      * The new <code>Action</code> replaces any previously set
1030      * <code>Action</code> but does not affect <code>ActionListeners</code>
1031      * independently added with <code>addActionListener</code>.
1032      * If the <code>Action</code> is already a registered
1033      * <code>ActionListener</code> for the <code>ActionEvent</code> source,
1034      * it is not re-registered.
1035      * <p>
1036      * Setting the <code>Action</code> results in immediately changing
1037      * all the properties described in <a href="Action.html#buttonActions">
1038      * Swing Components Supporting <code>Action</code></a>.
1039      * Subsequently, the combobox's properties are automatically updated
1040      * as the <code>Action</code>'s properties change.
1041      * <p>
1042      * This method uses three other methods to set
1043      * and help track the <code>Action</code>'s property values.
1044      * It uses the <code>configurePropertiesFromAction</code> method
1045      * to immediately change the combobox's properties.
1046      * To track changes in the <code>Action</code>'s property values,
1047      * this method registers the <code>PropertyChangeListener</code>
1048      * returned by <code>createActionPropertyChangeListener</code>. The
1049      * default {@code PropertyChangeListener} invokes the
1050      * {@code actionPropertyChanged} method when a property in the
1051      * {@code Action} changes.
1052      *
1053      * @param a the <code>Action</code> for the <code>JComboBox</code>,
1054      *                  or <code>null</code>.
1055      * @since 1.3
1056      * @see Action
1057      * @see #getAction
1058      * @see #configurePropertiesFromAction
1059      * @see #createActionPropertyChangeListener
1060      * @see #actionPropertyChanged
1061      * @beaninfo
1062      *        bound: true
1063      *    attribute: visualUpdate true
1064      *  description: the Action instance connected with this ActionEvent source
1065      */
1066     public void setAction(Action a) {
1067         Action oldValue = getAction();
1068         if (action==null || !action.equals(a)) {
1069             action = a;
1070             if (oldValue!=null) {
1071                 removeActionListener(oldValue);
1072                 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1073                 actionPropertyChangeListener = null;
1074             }
1075             configurePropertiesFromAction(action);
1076             if (action!=null) {
1077                 // Don't add if it is already a listener
1078                 if (!isListener(ActionListener.class, action)) {
1079                     addActionListener(action);
1080                 }
1081                 // Reverse linkage:
1082                 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1083                 action.addPropertyChangeListener(actionPropertyChangeListener);
1084             }
1085             firePropertyChange("action", oldValue, action);
1086         }
1087     }
1088 
1089     private boolean isListener(Class c, ActionListener a) {
1090         boolean isListener = false;
1091         Object[] listeners = listenerList.getListenerList();
1092         for (int i = listeners.length-2; i>=0; i-=2) {
1093             if (listeners[i]==c && listeners[i+1]==a) {
1094                     isListener=true;
1095             }
1096         }
1097         return isListener;
1098     }
1099 
1100     /**
1101      * Returns the currently set <code>Action</code> for this
1102      * <code>ActionEvent</code> source, or <code>null</code> if no
1103      * <code>Action</code> is set.
1104      *
1105      * @return the <code>Action</code> for this <code>ActionEvent</code>
1106      *          source; or <code>null</code>
1107      * @since 1.3
1108      * @see Action
1109      * @see #setAction
1110      */
1111     public Action getAction() {
1112         return action;
1113     }
1114 
1115     /**
1116      * Sets the properties on this combobox to match those in the specified
1117      * <code>Action</code>.  Refer to <a href="Action.html#buttonActions">
1118      * Swing Components Supporting <code>Action</code></a> for more
1119      * details as to which properties this sets.
1120      *
1121      * @param a the <code>Action</code> from which to get the properties,
1122      *          or <code>null</code>
1123      * @since 1.3
1124      * @see Action
1125      * @see #setAction
1126      */
1127     protected void configurePropertiesFromAction(Action a) {
1128         AbstractAction.setEnabledFromAction(this, a);
1129         AbstractAction.setToolTipTextFromAction(this, a);
1130         setActionCommandFromAction(a);
1131     }
1132 
1133     /**
1134      * Creates and returns a <code>PropertyChangeListener</code> that is
1135      * responsible for listening for changes from the specified
1136      * <code>Action</code> and updating the appropriate properties.
1137      * <p>
1138      * <b>Warning:</b> If you subclass this do not create an anonymous
1139      * inner class.  If you do the lifetime of the combobox will be tied to
1140      * that of the <code>Action</code>.
1141      *
1142      * @param a the combobox's action
1143      * @since 1.3
1144      * @see Action
1145      * @see #setAction
1146      */
1147     protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1148         return new ComboBoxActionPropertyChangeListener(this, a);
1149     }
1150 
1151     /**
1152      * Updates the combobox's state in response to property changes in
1153      * associated action. This method is invoked from the
1154      * {@code PropertyChangeListener} returned from
1155      * {@code createActionPropertyChangeListener}. Subclasses do not normally
1156      * need to invoke this. Subclasses that support additional {@code Action}
1157      * properties should override this and
1158      * {@code configurePropertiesFromAction}.
1159      * <p>
1160      * Refer to the table at <a href="Action.html#buttonActions">
1161      * Swing Components Supporting <code>Action</code></a> for a list of
1162      * the properties this method sets.
1163      *
1164      * @param action the <code>Action</code> associated with this combobox
1165      * @param propertyName the name of the property that changed
1166      * @since 1.6
1167      * @see Action
1168      * @see #configurePropertiesFromAction
1169      */
1170     protected void actionPropertyChanged(Action action, String propertyName) {
1171         if (propertyName == Action.ACTION_COMMAND_KEY) {
1172             setActionCommandFromAction(action);
1173         } else if (propertyName == "enabled") {
1174             AbstractAction.setEnabledFromAction(this, action);
1175         } else if (Action.SHORT_DESCRIPTION == propertyName) {
1176             AbstractAction.setToolTipTextFromAction(this, action);
1177         }
1178     }
1179 
1180     private void setActionCommandFromAction(Action a) {
1181         setActionCommand((a != null) ?
1182                              (String)a.getValue(Action.ACTION_COMMAND_KEY) :
1183                              null);
1184     }
1185 
1186 
1187     private static class ComboBoxActionPropertyChangeListener
1188                  extends ActionPropertyChangeListener<JComboBox> {
1189         ComboBoxActionPropertyChangeListener(JComboBox b, Action a) {
1190             super(b, a);
1191         }
1192         protected void actionPropertyChanged(JComboBox cb,
1193                                              Action action,
1194                                              PropertyChangeEvent e) {
1195             if (AbstractAction.shouldReconfigure(e)) {
1196                 cb.configurePropertiesFromAction(action);
1197             } else {
1198                 cb.actionPropertyChanged(action, e.getPropertyName());
1199             }
1200         }
1201     }
1202 
1203     /**
1204      * Notifies all listeners that have registered interest for
1205      * notification on this event type.
1206      * @param e  the event of interest
1207      *
1208      * @see EventListenerList
1209      */
1210     protected void fireItemStateChanged(ItemEvent e) {
1211         // Guaranteed to return a non-null array
1212         Object[] listeners = listenerList.getListenerList();
1213         // Process the listeners last to first, notifying
1214         // those that are interested in this event
1215         for ( int i = listeners.length-2; i>=0; i-=2 ) {
1216             if ( listeners[i]==ItemListener.class ) {
1217                 // Lazily create the event:
1218                 // if (changeEvent == null)
1219                 // changeEvent = new ChangeEvent(this);
1220                 ((ItemListener)listeners[i+1]).itemStateChanged(e);
1221             }
1222         }
1223     }
1224 
1225     /**
1226      * Notifies all listeners that have registered interest for
1227      * notification on this event type.
1228      *
1229      * @see EventListenerList
1230      */
1231     protected void fireActionEvent() {
1232         if (!firingActionEvent) {
1233             // Set flag to ensure that an infinite loop is not created
1234             firingActionEvent = true;
1235             ActionEvent e = null;
1236             // Guaranteed to return a non-null array
1237             Object[] listeners = listenerList.getListenerList();
1238             long mostRecentEventTime = EventQueue.getMostRecentEventTime();
1239             int modifiers = 0;
1240             AWTEvent currentEvent = EventQueue.getCurrentEvent();
1241             if (currentEvent instanceof InputEvent) {
1242                 modifiers = ((InputEvent)currentEvent).getModifiers();
1243             } else if (currentEvent instanceof ActionEvent) {
1244                 modifiers = ((ActionEvent)currentEvent).getModifiers();
1245             }
1246             // Process the listeners last to first, notifying
1247             // those that are interested in this event
1248             for ( int i = listeners.length-2; i>=0; i-=2 ) {
1249                 if ( listeners[i]==ActionListener.class ) {
1250                     // Lazily create the event:
1251                     if ( e == null )
1252                         e = new ActionEvent(this,ActionEvent.ACTION_PERFORMED,
1253                                             getActionCommand(),
1254                                             mostRecentEventTime, modifiers);
1255                     ((ActionListener)listeners[i+1]).actionPerformed(e);
1256                 }
1257             }
1258             firingActionEvent = false;
1259         }
1260     }
1261 
1262     /**
1263      * This protected method is implementation specific. Do not access directly
1264      * or override.
1265      */
1266     protected void selectedItemChanged() {
1267         if (selectedItemReminder != null ) {
1268             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1269                                                selectedItemReminder,
1270                                                ItemEvent.DESELECTED));
1271         }
1272 
1273         // set the new selected item.
1274         selectedItemReminder = dataModel.getSelectedItem();
1275 
1276         if (selectedItemReminder != null ) {
1277             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1278                                                selectedItemReminder,
1279                                                ItemEvent.SELECTED));
1280         }
1281     }
1282 
1283     /**
1284      * Returns an array containing the selected item.
1285      * This method is implemented for compatibility with
1286      * <code>ItemSelectable</code>.
1287      *
1288      * @return an array of <code>Objects</code> containing one
1289      *          element -- the selected item
1290      */
1291     public Object[] getSelectedObjects() {
1292         Object selectedObject = getSelectedItem();
1293         if ( selectedObject == null )
1294             return new Object[0];
1295         else {
1296             Object result[] = new Object[1];
1297             result[0] = selectedObject;
1298             return result;
1299         }
1300     }
1301 
1302     /**
1303      * This method is public as an implementation side effect.
1304      * do not call or override.
1305      */
1306     public void actionPerformed(ActionEvent e) {
1307         Object newItem = getEditor().getItem();
1308         setPopupVisible(false);
1309         getModel().setSelectedItem(newItem);
1310         String oldCommand = getActionCommand();
1311         setActionCommand("comboBoxEdited");
1312         fireActionEvent();
1313         setActionCommand(oldCommand);
1314     }
1315 
1316     /**
1317      * This method is public as an implementation side effect.
1318      * do not call or override.
1319      */
1320     public void contentsChanged(ListDataEvent e) {
1321         Object oldSelection = selectedItemReminder;
1322         Object newSelection = dataModel.getSelectedItem();
1323         if (oldSelection == null || !oldSelection.equals(newSelection)) {
1324             selectedItemChanged();
1325             if (!selectingItem) {
1326                 fireActionEvent();
1327             }
1328         }
1329     }
1330 
1331     /**
1332      * This method is public as an implementation side effect.
1333      * do not call or override.
1334      */
1335     public void intervalAdded(ListDataEvent e) {
1336         if (selectedItemReminder != dataModel.getSelectedItem()) {
1337             selectedItemChanged();
1338         }
1339     }
1340 
1341     /**
1342      * This method is public as an implementation side effect.
1343      * do not call or override.
1344      */
1345     public void intervalRemoved(ListDataEvent e) {
1346         contentsChanged(e);
1347     }
1348 
1349     /**
1350      * Selects the list item that corresponds to the specified keyboard
1351      * character and returns true, if there is an item corresponding
1352      * to that character.  Otherwise, returns false.
1353      *
1354      * @param keyChar a char, typically this is a keyboard key
1355      *                  typed by the user
1356      */
1357     public boolean selectWithKeyChar(char keyChar) {
1358         int index;
1359 
1360         if ( keySelectionManager == null )
1361             keySelectionManager = createDefaultKeySelectionManager();
1362 
1363         index = keySelectionManager.selectionForKey(keyChar,getModel());
1364         if ( index != -1 ) {
1365             setSelectedIndex(index);
1366             return true;
1367         }
1368         else
1369             return false;
1370     }
1371 
1372     /**
1373      * Enables the combo box so that items can be selected. When the
1374      * combo box is disabled, items cannot be selected and values
1375      * cannot be typed into its field (if it is editable).
1376      *
1377      * @param b a boolean value, where true enables the component and
1378      *          false disables it
1379      * @beaninfo
1380      *        bound: true
1381      *    preferred: true
1382      *  description: Whether the combo box is enabled.
1383      */
1384     public void setEnabled(boolean b) {
1385         super.setEnabled(b);
1386         firePropertyChange( "enabled", !isEnabled(), isEnabled() );
1387     }
1388 
1389     /**
1390      * Initializes the editor with the specified item.
1391      *
1392      * @param anEditor the <code>ComboBoxEditor</code> that displays
1393      *                  the list item in the
1394      *                  combo box field and allows it to be edited
1395      * @param anItem   the object to display and edit in the field
1396      */
1397     public void configureEditor(ComboBoxEditor anEditor, Object anItem) {
1398         anEditor.setItem(anItem);
1399     }
1400 
1401     /**
1402      * Handles <code>KeyEvent</code>s, looking for the Tab key.
1403      * If the Tab key is found, the popup window is closed.
1404      *
1405      * @param e  the <code>KeyEvent</code> containing the keyboard
1406      *          key that was pressed
1407      */
1408     public void processKeyEvent(KeyEvent e) {
1409         if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
1410             hidePopup();
1411         }
1412         super.processKeyEvent(e);
1413     }
1414 
1415     /**
1416      * Sets the object that translates a keyboard character into a list
1417      * selection. Typically, the first selection with a matching first
1418      * character becomes the selected item.
1419      *
1420      * @beaninfo
1421      *       expert: true
1422      *  description: The objects that changes the selection when a key is pressed.
1423      */
1424     public void setKeySelectionManager(KeySelectionManager aManager) {
1425         keySelectionManager = aManager;
1426     }
1427 
1428     /**
1429      * Returns the list's key-selection manager.
1430      *
1431      * @return the <code>KeySelectionManager</code> currently in use
1432      */
1433     public KeySelectionManager getKeySelectionManager() {
1434         return keySelectionManager;
1435     }
1436 
1437     /* Accessing the model */
1438     /**
1439      * Returns the number of items in the list.
1440      *
1441      * @return an integer equal to the number of items in the list
1442      */
1443     public int getItemCount() {
1444         return dataModel.getSize();
1445     }
1446 
1447     /**
1448      * Returns the list item at the specified index.  If <code>index</code>
1449      * is out of range (less than zero or greater than or equal to size)
1450      * it will return <code>null</code>.
1451      *
1452      * @param index  an integer indicating the list position, where the first
1453      *               item starts at zero
1454      * @return the <code>Object</code> at that list position; or
1455      *                  <code>null</code> if out of range
1456      */
1457     public Object getItemAt(int index) {
1458         return dataModel.getElementAt(index);
1459     }
1460 
1461     /**
1462      * Returns an instance of the default key-selection manager.
1463      *
1464      * @return the <code>KeySelectionManager</code> currently used by the list
1465      * @see #setKeySelectionManager
1466      */
1467     protected KeySelectionManager createDefaultKeySelectionManager() {
1468         return new DefaultKeySelectionManager();
1469     }
1470 
1471 
1472     /**
1473      * The interface that defines a <code>KeySelectionManager</code>.
1474      * To qualify as a <code>KeySelectionManager</code>,
1475      * the class needs to implement the method
1476      * that identifies the list index given a character and the
1477      * combo box data model.
1478      */
1479     public interface KeySelectionManager {
1480         /** Given <code>aKey</code> and the model, returns the row
1481          *  that should become selected. Return -1 if no match was
1482          *  found.
1483          *
1484          * @param  aKey  a char value, usually indicating a keyboard key that
1485          *               was pressed
1486          * @param aModel a ComboBoxModel -- the component's data model, containing
1487          *               the list of selectable items
1488          * @return an int equal to the selected row, where 0 is the
1489          *         first item and -1 is none.
1490          */
1491         int selectionForKey(char aKey,ComboBoxModel aModel);
1492     }
1493 
1494     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
1495         public int selectionForKey(char aKey,ComboBoxModel aModel) {
1496             int i,c;
1497             int currentSelection = -1;
1498             Object selectedItem = aModel.getSelectedItem();
1499             String v;
1500             String pattern;
1501 
1502             if ( selectedItem != null ) {
1503                 for ( i=0,c=aModel.getSize();i<c;i++ ) {
1504                     if ( selectedItem == aModel.getElementAt(i) ) {
1505                         currentSelection  =  i;
1506                         break;
1507                     }
1508                 }
1509             }
1510 
1511             pattern = ("" + aKey).toLowerCase();
1512             aKey = pattern.charAt(0);
1513 
1514             for ( i = ++currentSelection, c = aModel.getSize() ; i < c ; i++ ) {
1515                 Object elem = aModel.getElementAt(i);
1516                 if (elem != null && elem.toString() != null) {
1517                     v = elem.toString().toLowerCase();
1518                     if ( v.length() > 0 && v.charAt(0) == aKey )
1519                         return i;
1520                 }
1521             }
1522 
1523             for ( i = 0 ; i < currentSelection ; i ++ ) {
1524                 Object elem = aModel.getElementAt(i);
1525                 if (elem != null && elem.toString() != null) {
1526                     v = elem.toString().toLowerCase();
1527                     if ( v.length() > 0 && v.charAt(0) == aKey )
1528                         return i;
1529                 }
1530             }
1531             return -1;
1532         }
1533     }
1534 
1535 
1536     /**
1537      * See <code>readObject</code> and <code>writeObject</code> in
1538      * <code>JComponent</code> for more
1539      * information about serialization in Swing.
1540      */
1541     private void writeObject(ObjectOutputStream s) throws IOException {
1542         s.defaultWriteObject();
1543         if (getUIClassID().equals(uiClassID)) {
1544             byte count = JComponent.getWriteObjCounter(this);
1545             JComponent.setWriteObjCounter(this, --count);
1546             if (count == 0 && ui != null) {
1547                 ui.installUI(this);
1548             }
1549         }
1550     }
1551 
1552 
1553     /**
1554      * Returns a string representation of this <code>JComboBox</code>.
1555      * This method is intended to be used only for debugging purposes,
1556      * and the content and format of the returned string may vary between
1557      * implementations. The returned string may be empty but may not
1558      * be <code>null</code>.
1559      *
1560      * @return  a string representation of this <code>JComboBox</code>
1561      */
1562     protected String paramString() {
1563         String selectedItemReminderString = (selectedItemReminder != null ?
1564                                              selectedItemReminder.toString() :
1565                                              "");
1566         String isEditableString = (isEditable ? "true" : "false");
1567         String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
1568                                                 "true" : "false");
1569 
1570         return super.paramString() +
1571         ",isEditable=" + isEditableString +
1572         ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
1573         ",maximumRowCount=" + maximumRowCount +
1574         ",selectedItemReminder=" + selectedItemReminderString;
1575     }
1576 
1577 
1578 ///////////////////
1579 // Accessibility support
1580 ///////////////////
1581 
1582     /**
1583      * Gets the AccessibleContext associated with this JComboBox.
1584      * For combo boxes, the AccessibleContext takes the form of an
1585      * AccessibleJComboBox.
1586      * A new AccessibleJComboBox instance is created if necessary.
1587      *
1588      * @return an AccessibleJComboBox that serves as the
1589      *         AccessibleContext of this JComboBox
1590      */
1591     public AccessibleContext getAccessibleContext() {
1592         if ( accessibleContext == null ) {
1593             accessibleContext = new AccessibleJComboBox();
1594         }
1595         return accessibleContext;
1596     }
1597 
1598     /**
1599      * This class implements accessibility support for the
1600      * <code>JComboBox</code> class.  It provides an implementation of the
1601      * Java Accessibility API appropriate to Combo Box user-interface elements.
1602      * <p>
1603      * <strong>Warning:</strong>
1604      * Serialized objects of this class will not be compatible with
1605      * future Swing releases. The current serialization support is
1606      * appropriate for short term storage or RMI between applications running
1607      * the same version of Swing.  As of 1.4, support for long term storage
1608      * of all JavaBeans<sup><font size="-2">TM</font></sup>
1609      * has been added to the <code>java.beans</code> package.
1610      * Please see {@link java.beans.XMLEncoder}.
1611      */
1612     protected class AccessibleJComboBox extends AccessibleJComponent
1613     implements AccessibleAction, AccessibleSelection {
1614 
1615 
1616         private JList popupList; // combo box popup list
1617         private Accessible previousSelectedAccessible = null;
1618 
1619         /**
1620          * Returns an AccessibleJComboBox instance
1621          * @since 1.4
1622          */
1623         public AccessibleJComboBox() {
1624             // set the combo box editor's accessible name and description
1625             JComboBox.this.addPropertyChangeListener(new AccessibleJComboBoxPropertyChangeListener());
1626             setEditorNameAndDescription();
1627 
1628             // Get the popup list
1629             Accessible a = getUI().getAccessibleChild(JComboBox.this, 0);
1630             if (a instanceof javax.swing.plaf.basic.ComboPopup) {
1631                 // Listen for changes to the popup menu selection.
1632                 popupList = ((javax.swing.plaf.basic.ComboPopup)a).getList();
1633                 popupList.addListSelectionListener(
1634                     new AccessibleJComboBoxListSelectionListener());
1635             }
1636             // Listen for popup menu show/hide events
1637             JComboBox.this.addPopupMenuListener(
1638               new AccessibleJComboBoxPopupMenuListener());
1639         }
1640 
1641         /*
1642          * JComboBox PropertyChangeListener
1643          */
1644         private class AccessibleJComboBoxPropertyChangeListener
1645             implements PropertyChangeListener {
1646 
1647             public void propertyChange(PropertyChangeEvent e) {
1648                 if (e.getPropertyName() == "editor") {
1649                     // set the combo box editor's accessible name
1650                     // and description
1651                     setEditorNameAndDescription();
1652                 }
1653             }
1654         }
1655 
1656         /*
1657          * Sets the combo box editor's accessible name and descripton
1658          */
1659         private void setEditorNameAndDescription() {
1660             ComboBoxEditor editor = JComboBox.this.getEditor();
1661             if (editor != null) {
1662                 Component comp = editor.getEditorComponent();
1663                 if (comp instanceof Accessible) {
1664                     AccessibleContext ac = comp.getAccessibleContext();
1665                     if (ac != null) { // may be null
1666                         ac.setAccessibleName(getAccessibleName());
1667                         ac.setAccessibleDescription(getAccessibleDescription());
1668                     }
1669                 }
1670             }
1671         }
1672 
1673         /*
1674          * Listener for combo box popup menu
1675          * TIGER - 4669379 4894434
1676          */
1677         private class AccessibleJComboBoxPopupMenuListener
1678             implements PopupMenuListener {
1679 
1680             /**
1681              *  This method is called before the popup menu becomes visible
1682              */
1683             public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
1684                 // save the initial selection
1685                 if (popupList == null) {
1686                     return;
1687                 }
1688                 int selectedIndex = popupList.getSelectedIndex();
1689                 if (selectedIndex < 0) {
1690                     return;
1691                 }
1692                 previousSelectedAccessible =
1693                     popupList.getAccessibleContext().getAccessibleChild(selectedIndex);
1694             }
1695 
1696             /**
1697              * This method is called before the popup menu becomes invisible
1698              * Note that a JPopupMenu can become invisible any time
1699              */
1700             public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
1701                 // ignore
1702             }
1703 
1704             /**
1705              * This method is called when the popup menu is canceled
1706              */
1707             public void popupMenuCanceled(PopupMenuEvent e) {
1708                 // ignore
1709             }
1710         }
1711 
1712         /*
1713          * Handles changes to the popup list selection.
1714          * TIGER - 4669379 4894434 4933143
1715          */
1716         private class AccessibleJComboBoxListSelectionListener
1717             implements ListSelectionListener {
1718 
1719             public void valueChanged(ListSelectionEvent e) {
1720                 if (popupList == null) {
1721                     return;
1722                 }
1723 
1724                 // Get the selected popup list item.
1725                 int selectedIndex = popupList.getSelectedIndex();
1726                 if (selectedIndex < 0) {
1727                     return;
1728                 }
1729                 Accessible selectedAccessible =
1730                     popupList.getAccessibleContext().getAccessibleChild(selectedIndex);
1731                 if (selectedAccessible == null) {
1732                     return;
1733                 }
1734 
1735                 // Fire a FOCUSED lost PropertyChangeEvent for the
1736                 // previously selected list item.
1737                 PropertyChangeEvent pce;
1738 
1739                 if (previousSelectedAccessible != null) {
1740                     pce = new PropertyChangeEvent(previousSelectedAccessible,
1741                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1742                         AccessibleState.FOCUSED, null);
1743                     firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1744                                        null, pce);
1745                 }
1746                 // Fire a FOCUSED gained PropertyChangeEvent for the
1747                 // currently selected list item.
1748                 pce = new PropertyChangeEvent(selectedAccessible,
1749                     AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1750                     null, AccessibleState.FOCUSED);
1751                 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
1752                                    null, pce);
1753 
1754                 // Fire the ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY event
1755                 // for the combo box.
1756                 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
1757                                    previousSelectedAccessible, selectedAccessible);
1758 
1759                 // Save the previous selection.
1760                 previousSelectedAccessible = selectedAccessible;
1761             }
1762         }
1763 
1764 
1765         /**
1766          * Returns the number of accessible children in the object.  If all
1767          * of the children of this object implement Accessible, than this
1768          * method should return the number of children of this object.
1769          *
1770          * @return the number of accessible children in the object.
1771          */
1772         public int getAccessibleChildrenCount() {
1773             // Always delegate to the UI if it exists
1774             if (ui != null) {
1775                 return ui.getAccessibleChildrenCount(JComboBox.this);
1776             } else {
1777                 return super.getAccessibleChildrenCount();
1778             }
1779         }
1780 
1781         /**
1782          * Returns the nth Accessible child of the object.
1783          * The child at index zero represents the popup.
1784          * If the combo box is editable, the child at index one
1785          * represents the editor.
1786          *
1787          * @param i zero-based index of child
1788          * @return the nth Accessible child of the object
1789          */
1790         public Accessible getAccessibleChild(int i) {
1791             // Always delegate to the UI if it exists
1792             if (ui != null) {
1793                 return ui.getAccessibleChild(JComboBox.this, i);
1794             } else {
1795                return super.getAccessibleChild(i);
1796             }
1797         }
1798 
1799         /**
1800          * Get the role of this object.
1801          *
1802          * @return an instance of AccessibleRole describing the role of the
1803          * object
1804          * @see AccessibleRole
1805          */
1806         public AccessibleRole getAccessibleRole() {
1807             return AccessibleRole.COMBO_BOX;
1808         }
1809 
1810         /**
1811          * Gets the state set of this object.  The AccessibleStateSet of
1812          * an object is composed of a set of unique AccessibleStates.
1813          * A change in the AccessibleStateSet of an object will cause a
1814          * PropertyChangeEvent to be fired for the ACCESSIBLE_STATE_PROPERTY
1815          * property.
1816          *
1817          * @return an instance of AccessibleStateSet containing the
1818          * current state set of the object
1819          * @see AccessibleStateSet
1820          * @see AccessibleState
1821          * @see #addPropertyChangeListener
1822          *
1823          */
1824         public AccessibleStateSet getAccessibleStateSet() {
1825             // TIGER - 4489748
1826             AccessibleStateSet ass = super.getAccessibleStateSet();
1827             if (ass == null) {
1828                 ass = new AccessibleStateSet();
1829             }
1830             if (JComboBox.this.isPopupVisible()) {
1831                 ass.add(AccessibleState.EXPANDED);
1832             } else {
1833                 ass.add(AccessibleState.COLLAPSED);
1834             }
1835             return ass;
1836         }
1837 
1838         /**
1839          * Get the AccessibleAction associated with this object.  In the
1840          * implementation of the Java Accessibility API for this class,
1841          * return this object, which is responsible for implementing the
1842          * AccessibleAction interface on behalf of itself.
1843          *
1844          * @return this object
1845          */
1846         public AccessibleAction getAccessibleAction() {
1847             return this;
1848         }
1849 
1850         /**
1851          * Return a description of the specified action of the object.
1852          *
1853          * @param i zero-based index of the actions
1854          */
1855         public String getAccessibleActionDescription(int i) {
1856             if (i == 0) {
1857                 return UIManager.getString("ComboBox.togglePopupText");
1858             }
1859             else {
1860                 return null;
1861             }
1862         }
1863 
1864         /**
1865          * Returns the number of Actions available in this object.  The
1866          * default behavior of a combo box is to have one action.
1867          *
1868          * @return 1, the number of Actions in this object
1869          */
1870         public int getAccessibleActionCount() {
1871             return 1;
1872         }
1873 
1874         /**
1875          * Perform the specified Action on the object
1876          *
1877          * @param i zero-based index of actions
1878          * @return true if the the action was performed; else false.
1879          */
1880         public boolean doAccessibleAction(int i) {
1881             if (i == 0) {
1882                 setPopupVisible(!isPopupVisible());
1883                 return true;
1884             }
1885             else {
1886                 return false;
1887             }
1888         }
1889 
1890 
1891         /**
1892          * Get the AccessibleSelection associated with this object.  In the
1893          * implementation of the Java Accessibility API for this class,
1894          * return this object, which is responsible for implementing the
1895          * AccessibleSelection interface on behalf of itself.
1896          *
1897          * @return this object
1898          */
1899         public AccessibleSelection getAccessibleSelection() {
1900             return this;
1901         }
1902 
1903         /**
1904          * Returns the number of Accessible children currently selected.
1905          * If no children are selected, the return value will be 0.
1906          *
1907          * @return the number of items currently selected.
1908          * @since 1.3
1909          */
1910         public int getAccessibleSelectionCount() {
1911             Object o = JComboBox.this.getSelectedItem();
1912             if (o != null) {
1913                 return 1;
1914             } else {
1915                 return 0;
1916             }
1917         }
1918 
1919         /**
1920          * Returns an Accessible representing the specified selected child
1921          * in the popup.  If there isn't a selection, or there are
1922          * fewer children selected than the integer passed in, the return
1923          * value will be null.
1924          * <p>Note that the index represents the i-th selected child, which
1925          * is different from the i-th child.
1926          *
1927          * @param i the zero-based index of selected children
1928          * @return the i-th selected child
1929          * @see #getAccessibleSelectionCount
1930          * @since 1.3
1931          */
1932         public Accessible getAccessibleSelection(int i) {
1933             // Get the popup
1934             Accessible a =
1935                 JComboBox.this.getUI().getAccessibleChild(JComboBox.this, 0);
1936             if (a != null &&
1937                 a instanceof javax.swing.plaf.basic.ComboPopup) {
1938 
1939                 // get the popup list
1940                 JList list = ((javax.swing.plaf.basic.ComboPopup)a).getList();
1941 
1942                 // return the i-th selection in the popup list
1943                 AccessibleContext ac = list.getAccessibleContext();
1944                 if (ac != null) {
1945                     AccessibleSelection as = ac.getAccessibleSelection();
1946                     if (as != null) {
1947                         return as.getAccessibleSelection(i);
1948                     }
1949                 }
1950             }
1951             return null;
1952         }
1953 
1954         /**
1955          * Determines if the current child of this object is selected.
1956          *
1957          * @return true if the current child of this object is selected;
1958          *              else false
1959          * @param i the zero-based index of the child in this Accessible
1960          * object.
1961          * @see AccessibleContext#getAccessibleChild
1962          * @since 1.3
1963          */
1964         public boolean isAccessibleChildSelected(int i) {
1965             return JComboBox.this.getSelectedIndex() == i;
1966         }
1967 
1968         /**
1969          * Adds the specified Accessible child of the object to the object's
1970          * selection.  If the object supports multiple selections,
1971          * the specified child is added to any existing selection, otherwise
1972          * it replaces any existing selection in the object.  If the
1973          * specified child is already selected, this method has no effect.
1974          *
1975          * @param i the zero-based index of the child
1976          * @see AccessibleContext#getAccessibleChild
1977          * @since 1.3
1978          */
1979         public void addAccessibleSelection(int i) {
1980             // TIGER - 4856195
1981             clearAccessibleSelection();
1982             JComboBox.this.setSelectedIndex(i);
1983         }
1984 
1985         /**
1986          * Removes the specified child of the object from the object's
1987          * selection.  If the specified item isn't currently selected, this
1988          * method has no effect.
1989          *
1990          * @param i the zero-based index of the child
1991          * @see AccessibleContext#getAccessibleChild
1992          * @since 1.3
1993          */
1994         public void removeAccessibleSelection(int i) {
1995             if (JComboBox.this.getSelectedIndex() == i) {
1996                 clearAccessibleSelection();
1997             }
1998         }
1999 
2000         /**
2001          * Clears the selection in the object, so that no children in the
2002          * object are selected.
2003          * @since 1.3
2004          */
2005         public void clearAccessibleSelection() {
2006             JComboBox.this.setSelectedIndex(-1);
2007         }
2008 
2009         /**
2010          * Causes every child of the object to be selected
2011          * if the object supports multiple selections.
2012          * @since 1.3
2013          */
2014         public void selectAllAccessibleSelection() {
2015             // do nothing since multiple selection is not supported
2016         }
2017 
2018 //        public Accessible getAccessibleAt(Point p) {
2019 //            Accessible a = getAccessibleChild(1);
2020 //            if ( a != null ) {
2021 //                return a; // the editor
2022 //            }
2023 //            else {
2024 //                return getAccessibleChild(0); // the list
2025 //            }
2026 //        }
2027         private EditorAccessibleContext editorAccessibleContext = null;
2028 
2029         private class AccessibleEditor implements Accessible {
2030             public AccessibleContext getAccessibleContext() {
2031                 if (editorAccessibleContext == null) {
2032                     Component c = JComboBox.this.getEditor().getEditorComponent();
2033                     if (c instanceof Accessible) {
2034                         editorAccessibleContext =
2035                             new EditorAccessibleContext((Accessible)c);
2036                     }
2037                 }
2038                 return editorAccessibleContext;
2039             }
2040         }
2041 
2042         /*
2043          * Wrapper class for the AccessibleContext implemented by the
2044          * combo box editor.  Delegates all method calls except
2045          * getAccessibleIndexInParent to the editor.  The
2046          * getAccessibleIndexInParent method returns the selected
2047          * index in the combo box.
2048          */
2049         private class EditorAccessibleContext extends AccessibleContext {
2050 
2051             private AccessibleContext ac;
2052 
2053             private EditorAccessibleContext() {
2054             }
2055 
2056             /*
2057              * @param a the AccessibleContext implemented by the
2058              * combo box editor
2059              */
2060             EditorAccessibleContext(Accessible a) {
2061                 this.ac = a.getAccessibleContext();
2062             }
2063 
2064             /**
2065              * Gets the accessibleName property of this object.  The accessibleName
2066              * property of an object is a localized String that designates the purpose
2067              * of the object.  For example, the accessibleName property of a label
2068              * or button might be the text of the label or button itself.  In the
2069              * case of an object that doesn't display its name, the accessibleName
2070              * should still be set.  For example, in the case of a text field used
2071              * to enter the name of a city, the accessibleName for the en_US locale
2072              * could be 'city.'
2073              *
2074              * @return the localized name of the object; null if this
2075              * object does not have a name
2076              *
2077              * @see #setAccessibleName
2078              */
2079             public String getAccessibleName() {
2080                 return ac.getAccessibleName();
2081             }
2082 
2083             /**
2084              * Sets the localized accessible name of this object.  Changing the
2085              * name will cause a PropertyChangeEvent to be fired for the
2086              * ACCESSIBLE_NAME_PROPERTY property.
2087              *
2088              * @param s the new localized name of the object.
2089              *
2090              * @see #getAccessibleName
2091              * @see #addPropertyChangeListener
2092              *
2093              * @beaninfo
2094              *    preferred:   true
2095              *    description: Sets the accessible name for the component.
2096              */
2097             public void setAccessibleName(String s) {
2098                 ac.setAccessibleName(s);
2099             }
2100 
2101             /**
2102              * Gets the accessibleDescription property of this object.  The
2103              * accessibleDescription property of this object is a short localized
2104              * phrase describing the purpose of the object.  For example, in the
2105              * case of a 'Cancel' button, the accessibleDescription could be
2106              * 'Ignore changes and close dialog box.'
2107              *
2108              * @return the localized description of the object; null if
2109              * this object does not have a description
2110              *
2111              * @see #setAccessibleDescription
2112              */
2113             public String getAccessibleDescription() {
2114                 return ac.getAccessibleDescription();
2115             }
2116 
2117             /**
2118              * Sets the accessible description of this object.  Changing the
2119              * name will cause a PropertyChangeEvent to be fired for the
2120              * ACCESSIBLE_DESCRIPTION_PROPERTY property.
2121              *
2122              * @param s the new localized description of the object
2123              *
2124              * @see #setAccessibleName
2125              * @see #addPropertyChangeListener
2126              *
2127              * @beaninfo
2128              *    preferred:   true
2129              *    description: Sets the accessible description for the component.
2130              */
2131             public void setAccessibleDescription(String s) {
2132                 ac.setAccessibleDescription(s);
2133             }
2134 
2135             /**
2136              * Gets the role of this object.  The role of the object is the generic
2137              * purpose or use of the class of this object.  For example, the role
2138              * of a push button is AccessibleRole.PUSH_BUTTON.  The roles in
2139              * AccessibleRole are provided so component developers can pick from
2140              * a set of predefined roles.  This enables assistive technologies to
2141              * provide a consistent interface to various tweaked subclasses of
2142              * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
2143              * that act like a push button) as well as distinguish between sublasses
2144              * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
2145              * and AccessibleRole.RADIO_BUTTON for radio buttons).
2146              * <p>Note that the AccessibleRole class is also extensible, so
2147              * custom component developers can define their own AccessibleRole's
2148              * if the set of predefined roles is inadequate.
2149              *
2150              * @return an instance of AccessibleRole describing the role of the object
2151              * @see AccessibleRole
2152              */
2153             public AccessibleRole getAccessibleRole() {
2154                 return ac.getAccessibleRole();
2155             }
2156 
2157             /**
2158              * Gets the state set of this object.  The AccessibleStateSet of an object
2159              * is composed of a set of unique AccessibleStates.  A change in the
2160              * AccessibleStateSet of an object will cause a PropertyChangeEvent to
2161              * be fired for the ACCESSIBLE_STATE_PROPERTY property.
2162              *
2163              * @return an instance of AccessibleStateSet containing the
2164              * current state set of the object
2165              * @see AccessibleStateSet
2166              * @see AccessibleState
2167              * @see #addPropertyChangeListener
2168              */
2169             public AccessibleStateSet getAccessibleStateSet() {
2170                 return ac.getAccessibleStateSet();
2171             }
2172 
2173             /**
2174              * Gets the Accessible parent of this object.
2175              *
2176              * @return the Accessible parent of this object; null if this
2177              * object does not have an Accessible parent
2178              */
2179             public Accessible getAccessibleParent() {
2180                 return ac.getAccessibleParent();
2181             }
2182 
2183             /**
2184              * Sets the Accessible parent of this object.  This is meant to be used
2185              * only in the situations where the actual component's parent should
2186              * not be treated as the component's accessible parent and is a method
2187              * that should only be called by the parent of the accessible child.
2188              *
2189              * @param a - Accessible to be set as the parent
2190              */
2191             public void setAccessibleParent(Accessible a) {
2192                 ac.setAccessibleParent(a);
2193             }
2194 
2195             /**
2196              * Gets the 0-based index of this object in its accessible parent.
2197              *
2198              * @return the 0-based index of this object in its parent; -1 if this
2199              * object does not have an accessible parent.
2200              *
2201              * @see #getAccessibleParent
2202              * @see #getAccessibleChildrenCount
2203              * @see #getAccessibleChild
2204              */
2205             public int getAccessibleIndexInParent() {
2206                 return JComboBox.this.getSelectedIndex();
2207             }
2208 
2209             /**
2210              * Returns the number of accessible children of the object.
2211              *
2212              * @return the number of accessible children of the object.
2213              */
2214             public int getAccessibleChildrenCount() {
2215                 return ac.getAccessibleChildrenCount();
2216             }
2217 
2218             /**
2219              * Returns the specified Accessible child of the object.  The Accessible
2220              * children of an Accessible object are zero-based, so the first child
2221              * of an Accessible child is at index 0, the second child is at index 1,
2222              * and so on.
2223              *
2224              * @param i zero-based index of child
2225              * @return the Accessible child of the object
2226              * @see #getAccessibleChildrenCount
2227              */
2228             public Accessible getAccessibleChild(int i) {
2229                 return ac.getAccessibleChild(i);
2230             }
2231 
2232             /**
2233              * Gets the locale of the component. If the component does not have a
2234              * locale, then the locale of its parent is returned.
2235              *
2236              * @return this component's locale.  If this component does not have
2237              * a locale, the locale of its parent is returned.
2238              *
2239              * @exception IllegalComponentStateException
2240              * If the Component does not have its own locale and has not yet been
2241              * added to a containment hierarchy such that the locale can be
2242              * determined from the containing parent.
2243              */
2244             public Locale getLocale() throws IllegalComponentStateException {
2245                 return ac.getLocale();
2246             }
2247 
2248             /**
2249              * Adds a PropertyChangeListener to the listener list.
2250              * The listener is registered for all Accessible properties and will
2251              * be called when those properties change.
2252              *
2253              * @see #ACCESSIBLE_NAME_PROPERTY
2254              * @see #ACCESSIBLE_DESCRIPTION_PROPERTY
2255              * @see #ACCESSIBLE_STATE_PROPERTY
2256              * @see #ACCESSIBLE_VALUE_PROPERTY
2257              * @see #ACCESSIBLE_SELECTION_PROPERTY
2258              * @see #ACCESSIBLE_TEXT_PROPERTY
2259              * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY
2260              *
2261              * @param listener  The PropertyChangeListener to be added
2262              */
2263             public void addPropertyChangeListener(PropertyChangeListener listener) {
2264                 ac.addPropertyChangeListener(listener);
2265             }
2266 
2267             /**
2268              * Removes a PropertyChangeListener from the listener list.
2269              * This removes a PropertyChangeListener that was registered
2270              * for all properties.
2271              *
2272              * @param listener  The PropertyChangeListener to be removed
2273              */
2274             public void removePropertyChangeListener(PropertyChangeListener listener) {
2275                 ac.removePropertyChangeListener(listener);
2276             }
2277 
2278             /**
2279              * Gets the AccessibleAction associated with this object that supports
2280              * one or more actions.
2281              *
2282              * @return AccessibleAction if supported by object; else return null
2283              * @see AccessibleAction
2284              */
2285             public AccessibleAction getAccessibleAction() {
2286                 return ac.getAccessibleAction();
2287             }
2288 
2289             /**
2290              * Gets the AccessibleComponent associated with this object that has a
2291              * graphical representation.
2292              *
2293              * @return AccessibleComponent if supported by object; else return null
2294              * @see AccessibleComponent
2295              */
2296             public AccessibleComponent getAccessibleComponent() {
2297                 return ac.getAccessibleComponent();
2298             }
2299 
2300             /**
2301              * Gets the AccessibleSelection associated with this object which allows its
2302              * Accessible children to be selected.
2303              *
2304              * @return AccessibleSelection if supported by object; else return null
2305              * @see AccessibleSelection
2306              */
2307             public AccessibleSelection getAccessibleSelection() {
2308                 return ac.getAccessibleSelection();
2309             }
2310 
2311             /**
2312              * Gets the AccessibleText associated with this object presenting
2313              * text on the display.
2314              *
2315              * @return AccessibleText if supported by object; else return null
2316              * @see AccessibleText
2317              */
2318             public AccessibleText getAccessibleText() {
2319                 return ac.getAccessibleText();
2320             }
2321 
2322             /**
2323              * Gets the AccessibleEditableText associated with this object
2324              * presenting editable text on the display.
2325              *
2326              * @return AccessibleEditableText if supported by object; else return null
2327              * @see AccessibleEditableText
2328              */
2329             public AccessibleEditableText getAccessibleEditableText() {
2330                 return ac.getAccessibleEditableText();
2331             }
2332 
2333             /**
2334              * Gets the AccessibleValue associated with this object that supports a
2335              * Numerical value.
2336              *
2337              * @return AccessibleValue if supported by object; else return null
2338              * @see AccessibleValue
2339              */
2340             public AccessibleValue getAccessibleValue() {
2341                 return ac.getAccessibleValue();
2342             }
2343 
2344             /**
2345              * Gets the AccessibleIcons associated with an object that has
2346              * one or more associated icons
2347              *
2348              * @return an array of AccessibleIcon if supported by object;
2349              * otherwise return null
2350              * @see AccessibleIcon
2351              */
2352             public AccessibleIcon [] getAccessibleIcon() {
2353                 return ac.getAccessibleIcon();
2354             }
2355 
2356             /**
2357              * Gets the AccessibleRelationSet associated with an object
2358              *
2359              * @return an AccessibleRelationSet if supported by object;
2360              * otherwise return null
2361              * @see AccessibleRelationSet
2362              */
2363             public AccessibleRelationSet getAccessibleRelationSet() {
2364                 return ac.getAccessibleRelationSet();
2365             }
2366 
2367             /**
2368              * Gets the AccessibleTable associated with an object
2369              *
2370              * @return an AccessibleTable if supported by object;
2371              * otherwise return null
2372              * @see AccessibleTable
2373              */
2374             public AccessibleTable getAccessibleTable() {
2375                 return ac.getAccessibleTable();
2376             }
2377 
2378             /**
2379              * Support for reporting bound property changes.  If oldValue and
2380              * newValue are not equal and the PropertyChangeEvent listener list
2381              * is not empty, then fire a PropertyChange event to each listener.
2382              * In general, this is for use by the Accessible objects themselves
2383              * and should not be called by an application program.
2384              * @param propertyName  The programmatic name of the property that
2385              * was changed.
2386              * @param oldValue  The old value of the property.
2387              * @param newValue  The new value of the property.
2388              * @see java.beans.PropertyChangeSupport
2389              * @see #addPropertyChangeListener
2390              * @see #removePropertyChangeListener
2391              * @see #ACCESSIBLE_NAME_PROPERTY
2392              * @see #ACCESSIBLE_DESCRIPTION_PROPERTY
2393              * @see #ACCESSIBLE_STATE_PROPERTY
2394              * @see #ACCESSIBLE_VALUE_PROPERTY
2395              * @see #ACCESSIBLE_SELECTION_PROPERTY
2396              * @see #ACCESSIBLE_TEXT_PROPERTY
2397              * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY
2398              */
2399             public void firePropertyChange(String propertyName,
2400                                            Object oldValue,
2401                                            Object newValue) {
2402                 ac.firePropertyChange(propertyName, oldValue, newValue);
2403             }
2404         }
2405 
2406     } // innerclass AccessibleJComboBox
2407 }