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