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