1 /*
2  * Copyright (c) 1997, 2018, 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
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="https://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html">How to Use Combo Boxes</a>
68  * in <a href="https://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      */
JComboBox(ComboBoxModel<E> aModel)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      */
JComboBox(E[] items)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      */
JComboBox(Vector<E> items)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      */
JComboBox()227     public JComboBox() {
228         super();
229         setModel(new DefaultComboBoxModel<E>());
230         init();
231     }
232 
init()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      */
installAncestorListener()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.")
setUI(ComboBoxUI ui)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      */
updateUI()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)
getUIClassID()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      */
getUI()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.")
setModel(ComboBoxModel<E> aModel)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      */
getModel()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.")
setLightWeightPopupEnabled(boolean aFlag)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      */
isLightWeightPopupEnabled()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.")
setEditable(boolean aFlag)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      */
isEditable()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")
setMaximumRowCount(int count)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      */
getMaximumRowCount()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.")
setRenderer(ListCellRenderer<? super E> aRenderer)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      */
getRenderer()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")
setEditor(ComboBoxEditor anEditor)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      */
getEditor()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.")
setSelectedItem(Object anObject)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      */
getSelectedItem()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.")
setSelectedIndex(int anIndex)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
getSelectedIndex()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      */
getPrototypeDisplayValue()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.")
setPrototypeDisplayValue(E prototypeDisplayValue)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      */
addItem(E item)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      */
insertItemAt(E item, int index)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      */
removeItem(Object anObject)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      */
removeItemAt(int anIndex)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      */
removeAllItems()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      */
checkMutableComboBoxModel()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      */
showPopup()800     public void showPopup() {
801         setPopupVisible(true);
802     }
803 
804     /**
805      * Causes the combo box to close its popup window.
806      * @see #setPopupVisible
807      */
hidePopup()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      */
setPopupVisible(boolean v)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      */
isPopupVisible()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      */
addItemListener(ItemListener aListener)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      */
removeItemListener(ItemListener aListener)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)
getItemListeners()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      */
addActionListener(ActionListener l)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      */
removeActionListener(ActionListener l)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)
getActionListeners()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      */
addPopupMenuListener(PopupMenuListener l)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      */
removePopupMenuListener(PopupMenuListener l)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)
getPopupMenuListeners()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      */
firePopupMenuWillBecomeVisible()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      */
firePopupMenuWillBecomeInvisible()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      */
firePopupMenuCanceled()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      */
setActionCommand(String aCommand)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      */
getActionCommand()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")
setAction(Action a)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 
isListener(Class<?> c, ActionListener a)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      */
getAction()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      */
configurePropertiesFromAction(Action a)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      */
createActionPropertyChangeListener(Action a)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      */
actionPropertyChanged(Action action, String propertyName)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 
setActionCommandFromAction(Action a)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<?>> {
ComboBoxActionPropertyChangeListener(JComboBox<?> b, Action a)1196         ComboBoxActionPropertyChangeListener(JComboBox<?> b, Action a) {
1197             super(b, a);
1198         }
actionPropertyChanged(JComboBox<?> cb, Action action, PropertyChangeEvent e)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      */
fireItemStateChanged(ItemEvent e)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")
fireActionEvent()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      */
selectedItemChanged()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)
getSelectedObjects()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      */
actionPerformed(ActionEvent e)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      */
contentsChanged(ListDataEvent e)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      */
intervalAdded(ListDataEvent e)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      */
intervalRemoved(ListDataEvent e)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      */
selectWithKeyChar(char keyChar)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.")
setEnabled(boolean b)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      */
configureEditor(ComboBoxEditor anEditor, Object anItem)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      */
processKeyEvent(KeyEvent e)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
processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed)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.")
setKeySelectionManager(KeySelectionManager aManager)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      */
getKeySelectionManager()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)
getItemCount()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      */
getItemAt(int index)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      */
createDefaultKeySelectionManager()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          */
selectionForKey(char aKey,ComboBoxModel<?> aModel)1525         int selectionForKey(char aKey,ComboBoxModel<?> aModel);
1526     }
1527 
1528     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
selectionForKey(char aKey,ComboBoxModel<?> aModel)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      */
writeObject(ObjectOutputStream s)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      */
paramString()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)
getAccessibleContext()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
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          */
AccessibleJComboBox()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 
propertyChange(PropertyChangeEvent e)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          */
setEditorNameAndDescription()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              */
popupMenuWillBecomeVisible(PopupMenuEvent e)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              */
popupMenuWillBecomeInvisible(PopupMenuEvent e)1736             public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
1737                 // ignore
1738             }
1739 
1740             /**
1741              * This method is called when the popup menu is canceled
1742              */
popupMenuCanceled(PopupMenuEvent e)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 
valueChanged(ListSelectionEvent e)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          */
getAccessibleChildrenCount()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          */
getAccessibleChild(int i)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          */
getAccessibleRole()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          */
getAccessibleStateSet()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          */
getAccessibleAction()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          */
getAccessibleActionDescription(int i)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          */
getAccessibleActionCount()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          */
doAccessibleAction(int i)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          */
getAccessibleSelection()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          */
getAccessibleSelectionCount()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          */
getAccessibleSelection(int i)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          */
isAccessibleChildSelected(int i)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          */
addAccessibleSelection(int i)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          */
removeAccessibleSelection(int i)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          */
clearAccessibleSelection()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          */
selectAllAccessibleSelection()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 {
getAccessibleContext()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 
EditorAccessibleContext()2089             private EditorAccessibleContext() {
2090             }
2091 
2092             /*
2093              * @param a the AccessibleContext implemented by the
2094              * combo box editor
2095              */
EditorAccessibleContext(Accessible a)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              */
getAccessibleName()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.")
setAccessibleName(String s)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              */
getAccessibleDescription()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.")
setAccessibleDescription(String s)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              */
getAccessibleRole()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              */
getAccessibleStateSet()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              */
getAccessibleParent()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              */
setAccessibleParent(Accessible a)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              */
getAccessibleIndexInParent()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              */
getAccessibleChildrenCount()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              */
getAccessibleChild(int i)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              */
getLocale()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              */
addPropertyChangeListener(PropertyChangeListener listener)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              */
removePropertyChangeListener(PropertyChangeListener listener)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              */
getAccessibleAction()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              */
getAccessibleComponent()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              */
getAccessibleSelection()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              */
getAccessibleText()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              */
getAccessibleEditableText()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              */
getAccessibleValue()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              */
getAccessibleIcon()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              */
getAccessibleRelationSet()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              */
getAccessibleTable()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              */
firePropertyChange(String propertyName, Object oldValue, Object newValue)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 }
2440