    rev 48358 : Work in progress for JDK-6303622
    Use generic in Swing components like JComboBox
    1. Added generic return type and parameter type for JComboBox.
    2. Modified JList related classes as well.

       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     @SuppressWarnings("unchecked")
    1306     public E[] getSelectedObjects() {
    1307         E selectedObject = getSelectedItem();
    1308         E[] result = null;
    1309         if ( selectedObject == null ){
    1310             result = (E[]) new Object[0];
    1311         }
    1312         else {
    1313             result = (E[]) new Object[1];
    1314             result[0] = selectedObject;

    1315         }
    1316
    1317         return result;
    1318     }
    1319
    1320     /**
    1321      * This method is public as an implementation side effect.
    1322      * do not call or override.
    1323      */
    1324     @SuppressWarnings("unchecked")
    1325     public void actionPerformed(ActionEvent e) {
    1326         setPopupVisible(false);
    1327         getModel().setSelectedItem((E)getEditor().getItem());
    1328         String oldCommand = getActionCommand();
    1329         setActionCommand("comboBoxEdited");
    1330         fireActionEvent();
    1331         setActionCommand(oldCommand);
    1332     }
    1333
    1334     /**
    1335      * This method is public as an implementation side effect.
    1336      * do not call or override.
    1337      */
    1338     public void contentsChanged(ListDataEvent e) {
    1339         E oldSelection = selectedItemReminder;
    1340         E newSelection = dataModel.getSelectedItem();
    1341         if (oldSelection == null || !oldSelection.equals(newSelection)) {
    1342             selectedItemChanged();
    1343             if (!selectingItem) {
    1344                 fireActionEvent();
    1345             }
    1346         }
    1347     }
    1348
    1349     /**
    1350      * This method is public as an implementation side effect.
    1351      * do not call or override.
    1352      */
    1353     public void intervalAdded(ListDataEvent e) {
    1354         if (selectedItemReminder != dataModel.getSelectedItem()) {
    1355             selectedItemChanged();
    1356         }
    1357     }
    1358
    1359     /**
    1360      * This method is public as an implementation side effect.
    1361      * do not call or override.
    1362      */
    1363     public void intervalRemoved(ListDataEvent e) {
    1364         contentsChanged(e);
    1365     }
    1366
    1367     /**
    1368      * Selects the list item that corresponds to the specified keyboard
    1369      * character and returns true, if there is an item corresponding
    1370      * to that character.  Otherwise, returns false.
    1371      *
    1372      * @param keyChar a char, typically this is a keyboard key
    1373      *                  typed by the user
    1374      * @return {@code true} if there is an item corresponding to that character.
    1375      *         Otherwise, returns {@code false}.
    1376      */
    1377     public boolean selectWithKeyChar(char keyChar) {
    1378         int index;
    1379
    1380         if ( keySelectionManager == null )
    1381             keySelectionManager = createDefaultKeySelectionManager();
    1382
    1383         index = keySelectionManager.selectionForKey(keyChar,getModel());
    1384         if ( index != -1 ) {
    1385             setSelectedIndex(index);
    1386             return true;
    1387         }
    1388         else
    1389             return false;
    1390     }
    1391
    1392     /**
    1393      * Enables the combo box so that items can be selected. When the
    1394      * combo box is disabled, items cannot be selected and values
    1395      * cannot be typed into its field (if it is editable).
    1396      *
    1397      * @param b a boolean value, where true enables the component and
    1398      *          false disables it
    1399      */
    1400     @BeanProperty(preferred = true, description
    1401             = "The enabled state of the component.")
    1402     public void setEnabled(boolean b) {
    1403         super.setEnabled(b);
    1404         firePropertyChange( "enabled", !isEnabled(), isEnabled() );
    1405     }
    1406
    1407     /**
    1408      * Initializes the editor with the specified item.
    1409      *
    1410      * @param anEditor the <code>ComboBoxEditor</code> that displays
    1411      *                  the list item in the
    1412      *                  combo box field and allows it to be edited
    1413      * @param anItem   the object to display and edit in the field
    1414      */
    1415     public void configureEditor(ComboBoxEditor anEditor, Object anItem) {
    1416         anEditor.setItem(anItem);
    1417     }
    1418
    1419     /**
    1420      * Handles <code>KeyEvent</code>s, looking for the Tab key.
    1421      * If the Tab key is found, the popup window is closed.
    1422      *
    1423      * @param e  the <code>KeyEvent</code> containing the keyboard
    1424      *          key that was pressed
    1425      */
    1426     public void processKeyEvent(KeyEvent e) {
    1427         if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
    1428             hidePopup();
    1429         }
    1430         super.processKeyEvent(e);
    1431     }
    1432
    1433     /**
    1434      * {@inheritDoc}
    1435      */
    1436     @Override
    1437     protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
    1438         if (super.processKeyBinding(ks, e, condition, pressed)) {
    1439             return true;
    1440         }
    1441
    1442         if (!isEditable() || condition != WHEN_FOCUSED || getEditor() == null
    1443                 || !Boolean.TRUE.equals(getClientProperty("JComboBox.isTableCellEditor"))) {
    1444             return false;
    1445         }
    1446
    1447         Component editorComponent = getEditor().getEditorComponent();
    1448         if (editorComponent instanceof JComponent) {
    1449             JComponent component = (JComponent) editorComponent;
    1450             return component.processKeyBinding(ks, e, WHEN_FOCUSED, pressed);
    1451         }
    1452         return false;
    1453     }
    1454
    1455     /**
    1456      * Sets the object that translates a keyboard character into a list
    1457      * selection. Typically, the first selection with a matching first
    1458      * character becomes the selected item.
    1459      *
    1460      * @param aManager a key selection manager
    1461      */
    1462     @BeanProperty(bound = false, expert = true, description
    1463             = "The objects that changes the selection when a key is pressed.")
    1464     public void setKeySelectionManager(KeySelectionManager aManager) {
    1465         keySelectionManager = aManager;
    1466     }
    1467
    1468     /**
    1469      * Returns the list's key-selection manager.
    1470      *
    1471      * @return the <code>KeySelectionManager</code> currently in use
    1472      */
    1473     public KeySelectionManager getKeySelectionManager() {
    1474         return keySelectionManager;
    1475     }
    1476
    1477     /* Accessing the model */
    1478     /**
    1479      * Returns the number of items in the list.
    1480      *
    1481      * @return an integer equal to the number of items in the list
    1482      */
    1483     @BeanProperty(bound = false)
    1484     public int getItemCount() {
    1485         return dataModel.getSize();
    1486     }
    1487
    1488     /**
    1489      * Returns the list item at the specified index.  If <code>index</code>
    1490      * is out of range (less than zero or greater than or equal to size)
    1491      * it will return <code>null</code>.
    1492      *
    1493      * @param index  an integer indicating the list position, where the first
    1494      *               item starts at zero
    1495      * @return the item at that list position; or
    1496      *                  <code>null</code> if out of range
    1497      */
    1498     public E getItemAt(int index) {
    1499         return dataModel.getElementAt(index);
    1500     }
    1501
    1502     /**
    1503      * Returns an instance of the default key-selection manager.
    1504      *
    1505      * @return the <code>KeySelectionManager</code> currently used by the list
    1506      * @see #setKeySelectionManager
    1507      */
    1508     protected KeySelectionManager createDefaultKeySelectionManager() {
    1509         return new DefaultKeySelectionManager();
    1510     }
    1511
    1512
    1513     /**
    1514      * The interface that defines a <code>KeySelectionManager</code>.
    1515      * To qualify as a <code>KeySelectionManager</code>,
    1516      * the class needs to implement the method
    1517      * that identifies the list index given a character and the
    1518      * combo box data model.
    1519      */
    1520     public interface KeySelectionManager {
    1521         /** Given <code>aKey</code> and the model, returns the row
    1522          *  that should become selected. Return -1 if no match was
    1523          *  found.
    1524          *
    1525          * @param  aKey  a char value, usually indicating a keyboard key that
    1526          *               was pressed
    1527          * @param aModel a ComboBoxModel -- the component's data model, containing
    1528          *               the list of selectable items
    1529          * @return an int equal to the selected row, where 0 is the
    1530          *         first item and -1 is none.
    1531          */
    1532         int selectionForKey(char aKey,ComboBoxModel<?> aModel);
    1533     }
    1534
    1535     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
    1536         public int selectionForKey(char aKey,ComboBoxModel<?> aModel) {
    1537             int i,c;
    1538             int currentSelection = -1;
    1539             Object selectedItem = aModel.getSelectedItem();
    1540             String v;
    1541             String pattern;
    1542
    1543             if ( selectedItem != null ) {
    1544                 for ( i=0,c=aModel.getSize();i<c;i++ ) {
    1545                     if ( selectedItem == aModel.getElementAt(i) ) {
    1546                         currentSelection  =  i;
    1547                         break;
    1548                     }
    1549                 }
    1550             }
    1551
    1552             pattern = ("" + aKey).toLowerCase();
    1553             aKey = pattern.charAt(0);
    1554
    1555             for ( i = ++currentSelection, c = aModel.getSize() ; i < c ; i++ ) {
    1556                 Object elem = aModel.getElementAt(i);
    1557                 if (elem != null && elem.toString() != null) {
    1558                     v = elem.toString().toLowerCase();
    1559                     if ( v.length() > 0 && v.charAt(0) == aKey )
    1560                         return i;
    1561                 }
    1562             }
    1563
    1564             for ( i = 0 ; i < currentSelection ; i ++ ) {
    1565                 Object elem = aModel.getElementAt(i);
    1566                 if (elem != null && elem.toString() != null) {
    1567                     v = elem.toString().toLowerCase();
    1568                     if ( v.length() > 0 && v.charAt(0) == aKey )
    1569                         return i;
    1570                 }
    1571             }
    1572             return -1;
    1573         }
    1574     }
    1575
    1576
    1577     /**
    1578      * See <code>readObject</code> and <code>writeObject</code> in
    1579      * <code>JComponent</code> for more
    1580      * information about serialization in Swing.
    1581      */
    1582     private void writeObject(ObjectOutputStream s) throws IOException {
    1583         s.defaultWriteObject();
    1584         if (getUIClassID().equals(uiClassID)) {
    1585             byte count = JComponent.getWriteObjCounter(this);
    1586             JComponent.setWriteObjCounter(this, --count);
    1587             if (count == 0 && ui != null) {
    1588                 ui.installUI(this);
    1589             }
    1590         }
    1591     }
    1592
    1593
    1594     /**
    1595      * Returns a string representation of this <code>JComboBox</code>.
    1596      * This method is intended to be used only for debugging purposes,
    1597      * and the content and format of the returned string may vary between
    1598      * implementations. The returned string may be empty but may not
    1599      * be <code>null</code>.
    1600      *
    1601      * @return  a string representation of this <code>JComboBox</code>
    1602      */
    1603     protected String paramString() {
    1604         String selectedItemReminderString = (selectedItemReminder != null ?
    1605                                              selectedItemReminder.toString() :
    1606                                              "");
    1607         String isEditableString = (isEditable ? "true" : "false");
    1608         String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
    1609                                                 "true" : "false");
    1610
    1611         return super.paramString() +
    1612         ",isEditable=" + isEditableString +
    1613         ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
    1614         ",maximumRowCount=" + maximumRowCount +
    1615         ",selectedItemReminder=" + selectedItemReminderString;
    1616     }
    1617
    1618
    1619 ///////////////////
    1620 // Accessibility support
    1621 ///////////////////
    1622
    1623     /**
    1624      * Gets the AccessibleContext associated with this JComboBox.
    1625      * For combo boxes, the AccessibleContext takes the form of an
    1626      * AccessibleJComboBox.
    1627      * A new AccessibleJComboBox instance is created if necessary.
    1628      *
    1629      * @return an AccessibleJComboBox that serves as the
    1630      *         AccessibleContext of this JComboBox
    1631      */
    1632     @BeanProperty(bound = false)
    1633     public AccessibleContext getAccessibleContext() {
    1634         if ( accessibleContext == null ) {
    1635             accessibleContext = new AccessibleJComboBox();
    1636         }
    1637         return accessibleContext;
    1638     }
    1639
    1640     /**
    1641      * This class implements accessibility support for the
    1642      * <code>JComboBox</code> class.  It provides an implementation of the
    1643      * Java Accessibility API appropriate to Combo Box user-interface elements.
    1644      * <p>
    1645      * <strong>Warning:</strong>
    1646      * Serialized objects of this class will not be compatible with
    1647      * future Swing releases. The current serialization support is
    1648      * appropriate for short term storage or RMI between applications running
    1649      * the same version of Swing.  As of 1.4, support for long term storage
    1650      * of all JavaBeans&trade;
    1651      * has been added to the <code>java.beans</code> package.
    1652      * Please see {@link java.beans.XMLEncoder}.
    1653      */
    1654     @SuppressWarnings("serial") // Same-version serialization only
    1655     protected class AccessibleJComboBox extends AccessibleJComponent
    1656     implements AccessibleAction, AccessibleSelection {
    1657
    1658
    1659         private JList<?> popupList; // combo box popup list
    1660         private Accessible previousSelectedAccessible = null;
    1661
    1662         /**
    1663          * Returns an AccessibleJComboBox instance
    1664          * @since 1.4
    1665          */
    1666         public AccessibleJComboBox() {
    1667             // set the combo box editor's accessible name and description
    1668             JComboBox.this.addPropertyChangeListener(new AccessibleJComboBoxPropertyChangeListener());
    1669             setEditorNameAndDescription();
    1670
    1671             // Get the popup list
    1672             Accessible a = getUI().getAccessibleChild(JComboBox.this, 0);
    1673             if (a instanceof javax.swing.plaf.basic.ComboPopup) {
    1674                 // Listen for changes to the popup menu selection.
    1675                 popupList = ((javax.swing.plaf.basic.ComboPopup)a).getList();
    1676                 popupList.addListSelectionListener(
    1677                     new AccessibleJComboBoxListSelectionListener());
    1678             }
    1679             // Listen for popup menu show/hide events
    1680             JComboBox.this.addPopupMenuListener(
    1681               new AccessibleJComboBoxPopupMenuListener());
    1682         }
    1683
    1684         /*
    1685          * JComboBox PropertyChangeListener
    1686          */
    1687         private class AccessibleJComboBoxPropertyChangeListener
    1688             implements PropertyChangeListener {
    1689
    1690             public void propertyChange(PropertyChangeEvent e) {
    1691                 if (e.getPropertyName() == "editor") {
    1692                     // set the combo box editor's accessible name
    1693                     // and description
    1694                     setEditorNameAndDescription();
    1695                 }
    1696             }
    1697         }
    1698
    1699         /*
    1700          * Sets the combo box editor's accessible name and descripton
    1701          */
    1702         private void setEditorNameAndDescription() {
    1703             ComboBoxEditor editor = JComboBox.this.getEditor();
    1704             if (editor != null) {
    1705                 Component comp = editor.getEditorComponent();
    1706                 if (comp instanceof Accessible) {
    1707                     AccessibleContext ac = comp.getAccessibleContext();
    1708                     if (ac != null) { // may be null
    1709                         ac.setAccessibleName(getAccessibleName());
    1710                         ac.setAccessibleDescription(getAccessibleDescription());
    1711                     }
    1712                 }
    1713             }
    1714         }
    1715
    1716         /*
    1717          * Listener for combo box popup menu
    1718          * TIGER - 4669379 4894434
    1719          */
    1720         private class AccessibleJComboBoxPopupMenuListener
    1721             implements PopupMenuListener {
    1722
    1723             /**
    1724              *  This method is called before the popup menu becomes visible
    1725              */
    1726             public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
    1727                 // save the initial selection
    1728                 if (popupList == null) {
    1729                     return;
    1730                 }
    1731                 int selectedIndex = popupList.getSelectedIndex();
    1732                 if (selectedIndex < 0) {
    1733                     return;
    1734                 }
    1735                 previousSelectedAccessible =
    1736                     popupList.getAccessibleContext().getAccessibleChild(selectedIndex);
    1737             }
    1738
    1739             /**
    1740              * This method is called before the popup menu becomes invisible
    1741              * Note that a JPopupMenu can become invisible any time
    1742              */
    1743             public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
    1744                 // ignore
    1745             }
    1746
    1747             /**
    1748              * This method is called when the popup menu is canceled
    1749              */
    1750             public void popupMenuCanceled(PopupMenuEvent e) {
    1751                 // ignore
    1752             }
    1753         }
    1754
    1755         /*
    1756          * Handles changes to the popup list selection.
    1757          * TIGER - 4669379 4894434 4933143
    1758          */
    1759         private class AccessibleJComboBoxListSelectionListener
    1760             implements ListSelectionListener {
    1761
    1762             public void valueChanged(ListSelectionEvent e) {
    1763                 if (popupList == null) {
    1764                     return;
    1765                 }
    1766
    1767                 // Get the selected popup list item.
    1768                 int selectedIndex = popupList.getSelectedIndex();
    1769                 if (selectedIndex < 0) {
    1770                     return;
    1771                 }
    1772                 Accessible selectedAccessible =
    1773                     popupList.getAccessibleContext().getAccessibleChild(selectedIndex);
    1774                 if (selectedAccessible == null) {
    1775                     return;
    1776                 }
    1777
    1778                 // Fire a FOCUSED lost PropertyChangeEvent for the
    1779                 // previously selected list item.
    1780                 PropertyChangeEvent pce;
    1781
    1782                 if (previousSelectedAccessible != null) {
    1783                     pce = new PropertyChangeEvent(previousSelectedAccessible,
    1784                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
    1785                         AccessibleState.FOCUSED, null);
    1786                     firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
    1787                                        null, pce);
    1788                 }
    1789                 // Fire a FOCUSED gained PropertyChangeEvent for the
    1790                 // currently selected list item.
    1791                 pce = new PropertyChangeEvent(selectedAccessible,
    1792                     AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
    1793                     null, AccessibleState.FOCUSED);
    1794                 firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
    1795                                    null, pce);
    1796
    1797                 // Fire the ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY event
    1798                 // for the combo box.
    1799                 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY,
    1800                                    previousSelectedAccessible, selectedAccessible);
    1801
    1802                 // Save the previous selection.
    1803                 previousSelectedAccessible = selectedAccessible;
    1804             }
    1805         }
    1806
    1807
    1808         /**
    1809          * Returns the number of accessible children in the object.  If all
    1810          * of the children of this object implement Accessible, than this
    1811          * method should return the number of children of this object.
    1812          *
    1813          * @return the number of accessible children in the object.
    1814          */
    1815         public int getAccessibleChildrenCount() {
    1816             // Always delegate to the UI if it exists
    1817             if (ui != null) {
    1818                 return ui.getAccessibleChildrenCount(JComboBox.this);
    1819             } else {
    1820                 return super.getAccessibleChildrenCount();
    1821             }
    1822         }
    1823
    1824         /**
    1825          * Returns the nth Accessible child of the object.
    1826          * The child at index zero represents the popup.
    1827          * If the combo box is editable, the child at index one
    1828          * represents the editor.
    1829          *
    1830          * @param i zero-based index of child
    1831          * @return the nth Accessible child of the object
    1832          */
    1833         public Accessible getAccessibleChild(int i) {
    1834             // Always delegate to the UI if it exists
    1835             if (ui != null) {
    1836                 return ui.getAccessibleChild(JComboBox.this, i);
    1837             } else {
    1838                return super.getAccessibleChild(i);
    1839             }
    1840         }
    1841
    1842         /**
    1843          * Get the role of this object.
    1844          *
    1845          * @return an instance of AccessibleRole describing the role of the
    1846          * object
    1847          * @see AccessibleRole
    1848          */
    1849         public AccessibleRole getAccessibleRole() {
    1850             return AccessibleRole.COMBO_BOX;
    1851         }
    1852
    1853         /**
    1854          * Gets the state set of this object.  The AccessibleStateSet of
    1855          * an object is composed of a set of unique AccessibleStates.
    1856          * A change in the AccessibleStateSet of an object will cause a
    1857          * PropertyChangeEvent to be fired for the ACCESSIBLE_STATE_PROPERTY
    1858          * property.
    1859          *
    1860          * @return an instance of AccessibleStateSet containing the
    1861          * current state set of the object
    1862          * @see AccessibleStateSet
    1863          * @see AccessibleState
    1864          * @see #addPropertyChangeListener
    1865          *
    1866          */
    1867         public AccessibleStateSet getAccessibleStateSet() {
    1868             // TIGER - 4489748
    1869             AccessibleStateSet ass = super.getAccessibleStateSet();
    1870             if (ass == null) {
    1871                 ass = new AccessibleStateSet();
    1872             }
    1873             if (JComboBox.this.isPopupVisible()) {
    1874                 ass.add(AccessibleState.EXPANDED);
    1875             } else {
    1876                 ass.add(AccessibleState.COLLAPSED);
    1877             }
    1878             return ass;
    1879         }
    1880
    1881         /**
    1882          * Get the AccessibleAction associated with this object.  In the
    1883          * implementation of the Java Accessibility API for this class,
    1884          * return this object, which is responsible for implementing the
    1885          * AccessibleAction interface on behalf of itself.
    1886          *
    1887          * @return this object
    1888          */
    1889         public AccessibleAction getAccessibleAction() {
    1890             return this;
    1891         }
    1892
    1893         /**
    1894          * Return a description of the specified action of the object.
    1895          *
    1896          * @param i zero-based index of the actions
    1897          */
    1898         public String getAccessibleActionDescription(int i) {
    1899             if (i == 0) {
    1900                 return UIManager.getString("ComboBox.togglePopupText");
    1901             }
    1902             else {
    1903                 return null;
    1904             }
    1905         }
    1906
    1907         /**
    1908          * Returns the number of Actions available in this object.  The
    1909          * default behavior of a combo box is to have one action.
    1910          *
    1911          * @return 1, the number of Actions in this object
    1912          */
    1913         public int getAccessibleActionCount() {
    1914             return 1;
    1915         }
    1916
    1917         /**
    1918          * Perform the specified Action on the object
    1919          *
    1920          * @param i zero-based index of actions
    1921          * @return true if the action was performed; else false.
    1922          */
    1923         public boolean doAccessibleAction(int i) {
    1924             if (i == 0) {
    1925                 setPopupVisible(!isPopupVisible());
    1926                 return true;
    1927             }
    1928             else {
    1929                 return false;
    1930             }
    1931         }
    1932
    1933
    1934         /**
    1935          * Get the AccessibleSelection associated with this object.  In the
    1936          * implementation of the Java Accessibility API for this class,
    1937          * return this object, which is responsible for implementing the
    1938          * AccessibleSelection interface on behalf of itself.
    1939          *
    1940          * @return this object
    1941          */
    1942         public AccessibleSelection getAccessibleSelection() {
    1943             return this;
    1944         }
    1945
    1946         /**
    1947          * Returns the number of Accessible children currently selected.
    1948          * If no children are selected, the return value will be 0.
    1949          *
    1950          * @return the number of items currently selected.
    1951          * @since 1.3
    1952          */
    1953         public int getAccessibleSelectionCount() {
    1954             E o = JComboBox.this.getSelectedItem();
    1955             if (o != null) {
    1956                 return 1;
    1957             } else {
    1958                 return 0;
    1959             }
    1960         }
    1961
    1962         /**
    1963          * Returns an Accessible representing the specified selected child
    1964          * in the popup.  If there isn't a selection, or there are
    1965          * fewer children selected than the integer passed in, the return
    1966          * value will be null.
    1967          * <p>Note that the index represents the i-th selected child, which
    1968          * is different from the i-th child.
    1969          *
    1970          * @param i the zero-based index of selected children
    1971          * @return the i-th selected child
    1972          * @see #getAccessibleSelectionCount
    1973          * @since 1.3
    1974          */
    1975         public Accessible getAccessibleSelection(int i) {
    1976             // Get the popup
    1977             Accessible a =
    1978                 JComboBox.this.getUI().getAccessibleChild(JComboBox.this, 0);
    1979             if (a != null &&
    1980                 a instanceof javax.swing.plaf.basic.ComboPopup) {
    1981
    1982                 // get the popup list
    1983                 JList<?> list = ((javax.swing.plaf.basic.ComboPopup)a).getList();
    1984
    1985                 // return the i-th selection in the popup list
    1986                 AccessibleContext ac = list.getAccessibleContext();
    1987                 if (ac != null) {
    1988                     AccessibleSelection as = ac.getAccessibleSelection();
    1989                     if (as != null) {
    1990                         return as.getAccessibleSelection(i);
    1991                     }
    1992                 }
    1993             }
    1994             return null;
    1995         }
    1996
    1997         /**
    1998          * Determines if the current child of this object is selected.
    1999          *
    2000          * @return true if the current child of this object is selected;
    2001          *              else false
    2002          * @param i the zero-based index of the child in this Accessible
    2003          * object.
    2004          * @see AccessibleContext#getAccessibleChild
    2005          * @since 1.3
    2006          */
    2007         public boolean isAccessibleChildSelected(int i) {
    2008             return JComboBox.this.getSelectedIndex() == i;
    2009         }
    2010
    2011         /**
    2012          * Adds the specified Accessible child of the object to the object's
    2013          * selection.  If the object supports multiple selections,
    2014          * the specified child is added to any existing selection, otherwise
    2015          * it replaces any existing selection in the object.  If the
    2016          * specified child is already selected, this method has no effect.
    2017          *
    2018          * @param i the zero-based index of the child
    2019          * @see AccessibleContext#getAccessibleChild
    2020          * @since 1.3
    2021          */
    2022         public void addAccessibleSelection(int i) {
    2023             // TIGER - 4856195
    2024             clearAccessibleSelection();
    2025             JComboBox.this.setSelectedIndex(i);
    2026         }
    2027
    2028         /**
    2029          * Removes the specified child of the object from the object's
    2030          * selection.  If the specified item isn't currently selected, this
    2031          * method has no effect.
    2032          *
    2033          * @param i the zero-based index of the child
    2034          * @see AccessibleContext#getAccessibleChild
    2035          * @since 1.3
    2036          */
    2037         public void removeAccessibleSelection(int i) {
    2038             if (JComboBox.this.getSelectedIndex() == i) {
    2039                 clearAccessibleSelection();
    2040             }
    2041         }
    2042
    2043         /**
    2044          * Clears the selection in the object, so that no children in the
    2045          * object are selected.
    2046          * @since 1.3
    2047          */
    2048         public void clearAccessibleSelection() {
    2049             JComboBox.this.setSelectedIndex(-1);
    2050         }
    2051
    2052         /**
    2053          * Causes every child of the object to be selected
    2054          * if the object supports multiple selections.
    2055          * @since 1.3
    2056          */
    2057         public void selectAllAccessibleSelection() {
    2058             // do nothing since multiple selection is not supported
    2059         }
    2060
    2061 //        public Accessible getAccessibleAt(Point p) {
    2062 //            Accessible a = getAccessibleChild(1);
    2063 //            if ( a != null ) {
    2064 //                return a; // the editor
    2065 //            }
    2066 //            else {
    2067 //                return getAccessibleChild(0); // the list
    2068 //            }
    2069 //        }
    2070         private EditorAccessibleContext editorAccessibleContext = null;
    2071
    2072         private class AccessibleEditor implements Accessible {
    2073             public AccessibleContext getAccessibleContext() {
    2074                 if (editorAccessibleContext == null) {
    2075                     Component c = JComboBox.this.getEditor().getEditorComponent();
    2076                     if (c instanceof Accessible) {
    2077                         editorAccessibleContext =
    2078                             new EditorAccessibleContext((Accessible)c);
    2079                     }
    2080                 }
    2081                 return editorAccessibleContext;
    2082             }
    2083         }
    2084
    2085         /*
    2086          * Wrapper class for the AccessibleContext implemented by the
    2087          * combo box editor.  Delegates all method calls except
    2088          * getAccessibleIndexInParent to the editor.  The
    2089          * getAccessibleIndexInParent method returns the selected
    2090          * index in the combo box.
    2091          */
    2092         private class EditorAccessibleContext extends AccessibleContext {
    2093
    2094             private AccessibleContext ac;
    2095
    2096             private EditorAccessibleContext() {
    2097             }
    2098
    2099             /*
    2100              * @param a the AccessibleContext implemented by the
    2101              * combo box editor
    2102              */
    2103             EditorAccessibleContext(Accessible a) {
    2104                 this.ac = a.getAccessibleContext();
    2105             }
    2106
    2107             /**
    2108              * Gets the accessibleName property of this object.  The accessibleName
    2109              * property of an object is a localized String that designates the purpose
    2110              * of the object.  For example, the accessibleName property of a label
    2111              * or button might be the text of the label or button itself.  In the
    2112              * case of an object that doesn't display its name, the accessibleName
    2113              * should still be set.  For example, in the case of a text field used
    2114              * to enter the name of a city, the accessibleName for the en_US locale
    2115              * could be 'city.'
    2116              *
    2117              * @return the localized name of the object; null if this
    2118              * object does not have a name
    2119              *
    2120              * @see #setAccessibleName
    2121              */
    2122             public String getAccessibleName() {
    2123                 return ac.getAccessibleName();
    2124             }
    2125
    2126             /**
    2127              * Sets the localized accessible name of this object.  Changing the
    2128              * name will cause a PropertyChangeEvent to be fired for the
    2129              * ACCESSIBLE_NAME_PROPERTY property.
    2130              *
    2131              * @param s the new localized name of the object.
    2132              *
    2133              * @see #getAccessibleName
    2134              * @see #addPropertyChangeListener
    2135              */
    2136             @BeanProperty(preferred = true, description
    2137                     = "Sets the accessible name for the component.")
    2138             public void setAccessibleName(String s) {
    2139                 ac.setAccessibleName(s);
    2140             }
    2141
    2142             /**
    2143              * Gets the accessibleDescription property of this object.  The
    2144              * accessibleDescription property of this object is a short localized
    2145              * phrase describing the purpose of the object.  For example, in the
    2146              * case of a 'Cancel' button, the accessibleDescription could be
    2147              * 'Ignore changes and close dialog box.'
    2148              *
    2149              * @return the localized description of the object; null if
    2150              * this object does not have a description
    2151              *
    2152              * @see #setAccessibleDescription
    2153              */
    2154             public String getAccessibleDescription() {
    2155                 return ac.getAccessibleDescription();
    2156             }
    2157
    2158             /**
    2159              * Sets the accessible description of this object.  Changing the
    2160              * name will cause a PropertyChangeEvent to be fired for the
    2161              * ACCESSIBLE_DESCRIPTION_PROPERTY property.
    2162              *
    2163              * @param s the new localized description of the object
    2164              *
    2165              * @see #setAccessibleName
    2166              * @see #addPropertyChangeListener
    2167              */
    2168             @BeanProperty(preferred = true, description
    2169                     = "Sets the accessible description for the component.")
    2170             public void setAccessibleDescription(String s) {
    2171                 ac.setAccessibleDescription(s);
    2172             }
    2173
    2174             /**
    2175              * Gets the role of this object.  The role of the object is the generic
    2176              * purpose or use of the class of this object.  For example, the role
    2177              * of a push button is AccessibleRole.PUSH_BUTTON.  The roles in
    2178              * AccessibleRole are provided so component developers can pick from
    2179              * a set of predefined roles.  This enables assistive technologies to
    2180              * provide a consistent interface to various tweaked subclasses of
    2181              * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
    2182              * that act like a push button) as well as distinguish between subclasses
    2183              * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
    2184              * and AccessibleRole.RADIO_BUTTON for radio buttons).
    2185              * <p>Note that the AccessibleRole class is also extensible, so
    2186              * custom component developers can define their own AccessibleRole's
    2187              * if the set of predefined roles is inadequate.
    2188              *
    2189              * @return an instance of AccessibleRole describing the role of the object
    2190              * @see AccessibleRole
    2191              */
    2192             public AccessibleRole getAccessibleRole() {
    2193                 return ac.getAccessibleRole();
    2194             }
    2195
    2196             /**
    2197              * Gets the state set of this object.  The AccessibleStateSet of an object
    2198              * is composed of a set of unique AccessibleStates.  A change in the
    2199              * AccessibleStateSet of an object will cause a PropertyChangeEvent to
    2200              * be fired for the ACCESSIBLE_STATE_PROPERTY property.
    2201              *
    2202              * @return an instance of AccessibleStateSet containing the
    2203              * current state set of the object
    2204              * @see AccessibleStateSet
    2205              * @see AccessibleState
    2206              * @see #addPropertyChangeListener
    2207              */
    2208             public AccessibleStateSet getAccessibleStateSet() {
    2209                 return ac.getAccessibleStateSet();
    2210             }
    2211
    2212             /**
    2213              * Gets the Accessible parent of this object.
    2214              *
    2215              * @return the Accessible parent of this object; null if this
    2216              * object does not have an Accessible parent
    2217              */
    2218             public Accessible getAccessibleParent() {
    2219                 return ac.getAccessibleParent();
    2220             }
    2221
    2222             /**
    2223              * Sets the Accessible parent of this object.  This is meant to be used
    2224              * only in the situations where the actual component's parent should
    2225              * not be treated as the component's accessible parent and is a method
    2226              * that should only be called by the parent of the accessible child.
    2227              *
    2228              * @param a - Accessible to be set as the parent
    2229              */
    2230             public void setAccessibleParent(Accessible a) {
    2231                 ac.setAccessibleParent(a);
    2232             }
    2233
    2234             /**
    2235              * Gets the 0-based index of this object in its accessible parent.
    2236              *
    2237              * @return the 0-based index of this object in its parent; -1 if this
    2238              * object does not have an accessible parent.
    2239              *
    2240              * @see #getAccessibleParent
    2241              * @see #getAccessibleChildrenCount
    2242              * @see #getAccessibleChild
    2243              */
    2244             public int getAccessibleIndexInParent() {
    2245                 return JComboBox.this.getSelectedIndex();
    2246             }
    2247
    2248             /**
    2249              * Returns the number of accessible children of the object.
    2250              *
    2251              * @return the number of accessible children of the object.
    2252              */
    2253             public int getAccessibleChildrenCount() {
    2254                 return ac.getAccessibleChildrenCount();
    2255             }
    2256
    2257             /**
    2258              * Returns the specified Accessible child of the object.  The Accessible
    2259              * children of an Accessible object are zero-based, so the first child
    2260              * of an Accessible child is at index 0, the second child is at index 1,
    2261              * and so on.
    2262              *
    2263              * @param i zero-based index of child
    2264              * @return the Accessible child of the object
    2265              * @see #getAccessibleChildrenCount
    2266              */
    2267             public Accessible getAccessibleChild(int i) {
    2268                 return ac.getAccessibleChild(i);
    2269             }
    2270
    2271             /**
    2272              * Gets the locale of the component. If the component does not have a
    2273              * locale, then the locale of its parent is returned.
    2274              *
    2275              * @return this component's locale.  If this component does not have
    2276              * a locale, the locale of its parent is returned.
    2277              *
    2278              * @exception IllegalComponentStateException
    2279              * If the Component does not have its own locale and has not yet been
    2280              * added to a containment hierarchy such that the locale can be
    2281              * determined from the containing parent.
    2282              */
    2283             public Locale getLocale() throws IllegalComponentStateException {
    2284                 return ac.getLocale();
    2285             }
    2286
    2287             /**
    2288              * Adds a PropertyChangeListener to the listener list.
    2289              * The listener is registered for all Accessible properties and will
    2290              * be called when those properties change.
    2291              *
    2292              * @see #ACCESSIBLE_NAME_PROPERTY
    2293              * @see #ACCESSIBLE_DESCRIPTION_PROPERTY
    2294              * @see #ACCESSIBLE_STATE_PROPERTY
    2295              * @see #ACCESSIBLE_VALUE_PROPERTY
    2296              * @see #ACCESSIBLE_SELECTION_PROPERTY
    2297              * @see #ACCESSIBLE_TEXT_PROPERTY
    2298              * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY
    2299              *
    2300              * @param listener  The PropertyChangeListener to be added
    2301              */
    2302             public void addPropertyChangeListener(PropertyChangeListener listener) {
    2303                 ac.addPropertyChangeListener(listener);
    2304             }
    2305
    2306             /**
    2307              * Removes a PropertyChangeListener from the listener list.
    2308              * This removes a PropertyChangeListener that was registered
    2309              * for all properties.
    2310              *
    2311              * @param listener  The PropertyChangeListener to be removed
    2312              */
    2313             public void removePropertyChangeListener(PropertyChangeListener listener) {
    2314                 ac.removePropertyChangeListener(listener);
    2315             }
    2316
    2317             /**
    2318              * Gets the AccessibleAction associated with this object that supports
    2319              * one or more actions.
    2320              *
    2321              * @return AccessibleAction if supported by object; else return null
    2322              * @see AccessibleAction
    2323              */
    2324             public AccessibleAction getAccessibleAction() {
    2325                 return ac.getAccessibleAction();
    2326             }
    2327
    2328             /**
    2329              * Gets the AccessibleComponent associated with this object that has a
    2330              * graphical representation.
    2331              *
    2332              * @return AccessibleComponent if supported by object; else return null
    2333              * @see AccessibleComponent
    2334              */
    2335             public AccessibleComponent getAccessibleComponent() {
    2336                 return ac.getAccessibleComponent();
    2337             }
    2338
    2339             /**
    2340              * Gets the AccessibleSelection associated with this object which allows its
    2341              * Accessible children to be selected.
    2342              *
    2343              * @return AccessibleSelection if supported by object; else return null
    2344              * @see AccessibleSelection
    2345              */
    2346             public AccessibleSelection getAccessibleSelection() {
    2347                 return ac.getAccessibleSelection();
    2348             }
    2349
    2350             /**
    2351              * Gets the AccessibleText associated with this object presenting
    2352              * text on the display.
    2353              *
    2354              * @return AccessibleText if supported by object; else return null
    2355              * @see AccessibleText
    2356              */
    2357             public AccessibleText getAccessibleText() {
    2358                 return ac.getAccessibleText();
    2359             }
    2360
    2361             /**
    2362              * Gets the AccessibleEditableText associated with this object
    2363              * presenting editable text on the display.
    2364              *
    2365              * @return AccessibleEditableText if supported by object; else return null
    2366              * @see AccessibleEditableText
    2367              */
    2368             public AccessibleEditableText getAccessibleEditableText() {
    2369                 return ac.getAccessibleEditableText();
    2370             }
    2371
    2372             /**
    2373              * Gets the AccessibleValue associated with this object that supports a
    2374              * Numerical value.
    2375              *
    2376              * @return AccessibleValue if supported by object; else return null
    2377              * @see AccessibleValue
    2378              */
    2379             public AccessibleValue getAccessibleValue() {
    2380                 return ac.getAccessibleValue();
    2381             }
    2382
    2383             /**
    2384              * Gets the AccessibleIcons associated with an object that has
    2385              * one or more associated icons
    2386              *
    2387              * @return an array of AccessibleIcon if supported by object;
    2388              * otherwise return null
    2389              * @see AccessibleIcon
    2390              */
    2391             public AccessibleIcon [] getAccessibleIcon() {
    2392                 return ac.getAccessibleIcon();
    2393             }
    2394
    2395             /**
    2396              * Gets the AccessibleRelationSet associated with an object
    2397              *
    2398              * @return an AccessibleRelationSet if supported by object;
    2399              * otherwise return null
    2400              * @see AccessibleRelationSet
    2401              */
    2402             public AccessibleRelationSet getAccessibleRelationSet() {
    2403                 return ac.getAccessibleRelationSet();
    2404             }
    2405
    2406             /**
    2407              * Gets the AccessibleTable associated with an object
    2408              *
    2409              * @return an AccessibleTable if supported by object;
    2410              * otherwise return null
    2411              * @see AccessibleTable
    2412              */
    2413             public AccessibleTable getAccessibleTable() {
    2414                 return ac.getAccessibleTable();
    2415             }
    2416
    2417             /**
    2418              * Support for reporting bound property changes.  If oldValue and
    2419              * newValue are not equal and the PropertyChangeEvent listener list
    2420              * is not empty, then fire a PropertyChange event to each listener.
    2421              * In general, this is for use by the Accessible objects themselves
    2422              * and should not be called by an application program.
    2423              * @param propertyName  The programmatic name of the property that
    2424              * was changed.
    2425              * @param oldValue  The old value of the property.
    2426              * @param newValue  The new value of the property.
    2427              * @see java.beans.PropertyChangeSupport
    2428              * @see #addPropertyChangeListener
    2429              * @see #removePropertyChangeListener
    2430              * @see #ACCESSIBLE_NAME_PROPERTY
    2431              * @see #ACCESSIBLE_DESCRIPTION_PROPERTY
    2432              * @see #ACCESSIBLE_STATE_PROPERTY
    2433              * @see #ACCESSIBLE_VALUE_PROPERTY
    2434              * @see #ACCESSIBLE_SELECTION_PROPERTY
    2435              * @see #ACCESSIBLE_TEXT_PROPERTY
    2436              * @see #ACCESSIBLE_VISIBLE_DATA_PROPERTY
    2437              */
    2438             public void firePropertyChange(String propertyName,
    2439                                            Object oldValue,
    2440                                            Object newValue) {
    2441                 ac.firePropertyChange(propertyName, oldValue, newValue);
    2442             }
    2443         }
    2444
    2445     } // innerclass AccessibleJComboBox
    2446 }
    --- EOF ---