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