1 /*
2 * Copyright (c) 1997, 2014, 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.PropertyChangeEvent;
28 import java.beans.PropertyChangeListener;
29 import java.beans.Transient;
30 import java.util.*;
31
32 import java.awt.*;
33 import java.awt.event.*;
34
35 import java.io.Serializable;
36 import java.io.ObjectOutputStream;
37 import java.io.IOException;
38
39 import javax.swing.event.*;
40 import javax.swing.plaf.*;
41
42 import javax.accessibility.*;
43
44 /**
45 * A component that combines a button or editable field and a drop-down list.
46 * The user can select a value from the drop-down list, which appears at the
54 * <p>
55 * <strong>Warning:</strong>
56 * Serialized objects of this class will not be compatible with
57 * future Swing releases. The current serialization support is
58 * appropriate for short term storage or RMI between applications running
59 * the same version of Swing. As of 1.4, support for long term storage
60 * of all JavaBeans™
61 * has been added to the <code>java.beans</code> package.
62 * Please see {@link java.beans.XMLEncoder}.
63 *
64 * <p>
65 * See <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html">How to Use Combo Boxes</a>
66 * in <a href="http://docs.oracle.com/javase/tutorial/"><em>The Java Tutorial</em></a>
67 * for further information.
68 *
69 * @see ComboBoxModel
70 * @see DefaultComboBoxModel
71 *
72 * @param <E> the type of the elements of this combo box
73 *
74 * @beaninfo
75 * attribute: isContainer false
76 * description: A combination of a text field and a drop-down list.
77 *
78 * @author Arnaud Weber
79 * @author Mark Davidson
80 * @since 1.2
81 */
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.
238 * {@code AncestorEvents} when it or any of its ancestors
239 * move or are made visible or invisible.
240 * Events are also sent when the component or its ancestors are added
241 * or removed from the containment hierarchy.
242 */
243 protected void installAncestorListener() {
244 addAncestorListener(new AncestorListener(){
245 public void ancestorAdded(AncestorEvent event){ hidePopup();}
246 public void ancestorRemoved(AncestorEvent event){ hidePopup();}
247 public void ancestorMoved(AncestorEvent event){
248 if (event.getSource() != JComboBox.this)
249 hidePopup();
250 }});
251 }
252
253 /**
254 * Sets the L&F object that renders this component.
255 *
256 * @param ui the <code>ComboBoxUI</code> L&F object
257 * @see UIDefaults#getUI
258 *
259 * @beaninfo
260 * bound: true
261 * hidden: true
262 * attribute: visualUpdate true
263 * description: The UI object that implements the Component's LookAndFeel.
264 */
265 public void setUI(ComboBoxUI ui) {
266 super.setUI(ui);
267 }
268
269 /**
270 * Resets the UI property to a value from the current look and feel.
271 *
272 * @see JComponent#updateUI
273 */
274 public void updateUI() {
275 setUI((ComboBoxUI)UIManager.getUI(this));
276
277 ListCellRenderer<? super E> renderer = getRenderer();
278 if (renderer instanceof Component) {
279 SwingUtilities.updateComponentTreeUI((Component)renderer);
280 }
281 }
282
283
284 /**
285 * Returns the name of the L&F class that renders this component.
286 *
287 * @return the string "ComboBoxUI"
288 * @see JComponent#getUIClassID
289 * @see UIDefaults#getUI
290 */
291 public String getUIClassID() {
292 return uiClassID;
293 }
294
295
296 /**
297 * Returns the L&F object that renders this component.
298 *
299 * @return the ComboBoxUI object that renders this component
300 */
301 public ComboBoxUI getUI() {
302 return(ComboBoxUI)ui;
303 }
304
305 /**
306 * Sets the data model that the <code>JComboBox</code> uses to obtain
307 * the list of items.
308 *
309 * @param aModel the <code>ComboBoxModel</code> that provides the
310 * displayed list of items
311 *
312 * @beaninfo
313 * bound: true
314 * description: Model that the combo box uses to get data to display.
315 */
316 public void setModel(ComboBoxModel<E> aModel) {
317 ComboBoxModel<E> oldModel = dataModel;
318 if (oldModel != null) {
319 oldModel.removeListDataListener(this);
320 }
321 dataModel = aModel;
322 dataModel.addListDataListener(this);
323
324 // set the current selected item.
325 selectedItemReminder = dataModel.getSelectedItem();
326
327 firePropertyChange( "model", oldModel, dataModel);
328 }
329
330 /**
331 * Returns the data model currently used by the <code>JComboBox</code>.
332 *
333 * @return the <code>ComboBoxModel</code> that provides the displayed
334 * list of items
335 */
346 * provides a hint as to whether or not a lightweight
347 * <code>Component</code> should be used to contain the
348 * <code>JComboBox</code>, versus a heavyweight
349 * <code>Component</code> such as a <code>Panel</code>
350 * or a <code>Window</code>. The decision of lightweight
351 * versus heavyweight is ultimately up to the
352 * <code>JComboBox</code>. Lightweight windows are more
353 * efficient than heavyweight windows, but lightweight
354 * and heavyweight components do not mix well in a GUI.
355 * If your application mixes lightweight and heavyweight
356 * components, you should disable lightweight popups.
357 * The default value for the <code>lightWeightPopupEnabled</code>
358 * property is <code>true</code>, unless otherwise specified
359 * by the look and feel. Some look and feels always use
360 * heavyweight popups, no matter what the value of this property.
361 * <p>
362 * See the article <a href="http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html">Mixing Heavy and Light Components</a>
363 * This method fires a property changed event.
364 *
365 * @param aFlag if <code>true</code>, lightweight popups are desired
366 *
367 * @beaninfo
368 * bound: true
369 * expert: true
370 * description: Set to <code>false</code> to require heavyweight popups.
371 */
372 public void setLightWeightPopupEnabled(boolean aFlag) {
373 boolean oldFlag = lightWeightPopupEnabled;
374 lightWeightPopupEnabled = aFlag;
375 firePropertyChange("lightWeightPopupEnabled", oldFlag, lightWeightPopupEnabled);
376 }
377
378 /**
379 * Gets the value of the <code>lightWeightPopupEnabled</code>
380 * property.
381 *
382 * @return the value of the <code>lightWeightPopupEnabled</code>
383 * property
384 * @see #setLightWeightPopupEnabled
385 */
386 public boolean isLightWeightPopupEnabled() {
387 return lightWeightPopupEnabled;
388 }
389
390 /**
391 * Determines whether the <code>JComboBox</code> field is editable.
392 * An editable <code>JComboBox</code> allows the user to type into the
393 * field or selected an item from the list to initialize the field,
394 * after which it can be edited. (The editing affects only the field,
395 * the list item remains intact.) A non editable <code>JComboBox</code>
396 * displays the selected item in the field,
397 * but the selection cannot be modified.
398 *
399 * @param aFlag a boolean value, where true indicates that the
400 * field is editable
401 *
402 * @beaninfo
403 * bound: true
404 * preferred: true
405 * description: If true, the user can type a new value in the combo box.
406 */
407 public void setEditable(boolean aFlag) {
408 boolean oldFlag = isEditable;
409 isEditable = aFlag;
410 firePropertyChange( "editable", oldFlag, isEditable );
411 }
412
413 /**
414 * Returns true if the <code>JComboBox</code> is editable.
415 * By default, a combo box is not editable.
416 *
417 * @return true if the <code>JComboBox</code> is editable, else false
418 */
419 public boolean isEditable() {
420 return isEditable;
421 }
422
423 /**
424 * Sets the maximum number of rows the <code>JComboBox</code> displays.
425 * If the number of objects in the model is greater than count,
426 * the combo box uses a scrollbar.
427 *
428 * @param count an integer specifying the maximum number of items to
429 * display in the list before using a scrollbar
430 * @beaninfo
431 * bound: true
432 * preferred: true
433 * description: The maximum number of rows the popup should have
434 */
435 public void setMaximumRowCount(int count) {
436 int oldCount = maximumRowCount;
437 maximumRowCount = count;
438 firePropertyChange( "maximumRowCount", oldCount, maximumRowCount );
439 }
440
441 /**
442 * Returns the maximum number of items the combo box can display
443 * without a scrollbar
444 *
445 * @return an integer specifying the maximum number of items that are
446 * displayed in the list before using a scrollbar
447 */
448 public int getMaximumRowCount() {
449 return maximumRowCount;
450 }
451
452 /**
453 * Sets the renderer that paints the list items and the item selected from the list in
454 * the JComboBox field. The renderer is used if the JComboBox is not
455 * editable. If it is editable, the editor is used to render and edit
456 * the selected item.
457 * <p>
458 * The default renderer displays a string or an icon.
459 * Other renderers can handle graphic images and composite items.
460 * <p>
461 * To display the selected item,
462 * <code>aRenderer.getListCellRendererComponent</code>
463 * is called, passing the list object and an index of -1.
464 *
465 * @param aRenderer the <code>ListCellRenderer</code> that
466 * displays the selected item
467 * @see #setEditor
468 * @beaninfo
469 * bound: true
470 * expert: true
471 * description: The renderer that paints the item selected in the list.
472 */
473 public void setRenderer(ListCellRenderer<? super E> aRenderer) {
474 ListCellRenderer<? super E> oldRenderer = renderer;
475 renderer = aRenderer;
476 firePropertyChange( "renderer", oldRenderer, renderer );
477 invalidate();
478 }
479
480 /**
481 * Returns the renderer used to display the selected item in the
482 * <code>JComboBox</code> field.
483 *
484 * @return the <code>ListCellRenderer</code> that displays
485 * the selected item.
486 */
487 public ListCellRenderer<? super E> getRenderer() {
488 return renderer;
489 }
490
491 /**
492 * Sets the editor used to paint and edit the selected item in the
493 * <code>JComboBox</code> field. The editor is used only if the
494 * receiving <code>JComboBox</code> is editable. If not editable,
495 * the combo box uses the renderer to paint the selected item.
496 *
497 * @param anEditor the <code>ComboBoxEditor</code> that
498 * displays the selected item
499 * @see #setRenderer
500 * @beaninfo
501 * bound: true
502 * expert: true
503 * description: The editor that combo box uses to edit the current value
504 */
505 public void setEditor(ComboBoxEditor anEditor) {
506 ComboBoxEditor oldEditor = editor;
507
508 if ( editor != null ) {
509 editor.removeActionListener(this);
510 }
511 editor = anEditor;
512 if ( editor != null ) {
513 editor.addActionListener(this);
514 }
515 firePropertyChange( "editor", oldEditor, editor );
516 }
517
518 /**
519 * Returns the editor used to paint and edit the selected item in the
520 * <code>JComboBox</code> field.
521 *
522 * @return the <code>ComboBoxEditor</code> that displays the selected item
523 */
524 public ComboBoxEditor getEditor() {
536 * <code>anObject</code> selected.
537 * <p>
538 * If <code>anObject</code> is <i>not</i> in the list and the combo box is
539 * uneditable, it will not change the current selection. For editable
540 * combo boxes, the selection will change to <code>anObject</code>.
541 * <p>
542 * If this constitutes a change in the selected item,
543 * <code>ItemListener</code>s added to the combo box will be notified with
544 * one or two <code>ItemEvent</code>s.
545 * If there is a current selected item, an <code>ItemEvent</code> will be
546 * fired and the state change will be <code>ItemEvent.DESELECTED</code>.
547 * If <code>anObject</code> is in the list and is not currently selected
548 * then an <code>ItemEvent</code> will be fired and the state change will
549 * be <code>ItemEvent.SELECTED</code>.
550 * <p>
551 * <code>ActionListener</code>s added to the combo box will be notified
552 * with an <code>ActionEvent</code> when this method is called.
553 *
554 * @param anObject the list object to select; use <code>null</code> to
555 clear the selection
556 * @beaninfo
557 * preferred: true
558 * description: Sets the selected item in the JComboBox.
559 */
560 public void setSelectedItem(Object anObject) {
561 Object oldSelection = selectedItemReminder;
562 Object objectToSelect = anObject;
563 if (oldSelection == null || !oldSelection.equals(anObject)) {
564
565 if (anObject != null && !isEditable()) {
566 // For non editable combo boxes, an invalid selection
567 // will be rejected.
568 boolean found = false;
569 for (int i = 0; i < dataModel.getSize(); i++) {
570 E element = dataModel.getElementAt(i);
571 if (anObject.equals(element)) {
572 found = true;
573 objectToSelect = element;
574 break;
575 }
576 }
577 if (!found) {
578 return;
579 }
601 * Returns the current selected item.
602 * <p>
603 * If the combo box is editable, then this value may not have been added
604 * to the combo box with <code>addItem</code>, <code>insertItemAt</code>
605 * or the data constructors.
606 *
607 * @return the current selected Object
608 * @see #setSelectedItem
609 */
610 public Object getSelectedItem() {
611 return dataModel.getSelectedItem();
612 }
613
614 /**
615 * Selects the item at index <code>anIndex</code>.
616 *
617 * @param anIndex an integer specifying the list item to select,
618 * where 0 specifies the first item in the list and -1 indicates no selection
619 * @exception IllegalArgumentException if <code>anIndex</code> < -1 or
620 * <code>anIndex</code> is greater than or equal to size
621 * @beaninfo
622 * preferred: true
623 * description: The item at index is selected.
624 */
625 public void setSelectedIndex(int anIndex) {
626 int size = dataModel.getSize();
627
628 if ( anIndex == -1 ) {
629 setSelectedItem( null );
630 } else if ( anIndex < -1 || anIndex >= size ) {
631 throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
632 } else {
633 setSelectedItem(dataModel.getElementAt(anIndex));
634 }
635 }
636
637 /**
638 * Returns the first item in the list that matches the given item.
639 * The result is not always defined if the <code>JComboBox</code>
640 * allows selected items that are not in the list.
641 * Returns -1 if there is no selected item or if the user specified
642 * an item which is not in the list.
643
644 * @return an integer specifying the currently selected list item,
672 public E getPrototypeDisplayValue() {
673 return prototypeDisplayValue;
674 }
675
676 /**
677 * Sets the prototype display value used to calculate the size of the display
678 * for the UI portion.
679 * <p>
680 * If a prototype display value is specified, the preferred size of
681 * the combo box is calculated by configuring the renderer with the
682 * prototype display value and obtaining its preferred size. Specifying
683 * the preferred display value is often useful when the combo box will be
684 * displaying large amounts of data. If no prototype display value has
685 * been specified, the renderer must be configured for each value from
686 * the model and its preferred size obtained, which can be
687 * relatively expensive.
688 *
689 * @param prototypeDisplayValue the prototype display value
690 * @see #getPrototypeDisplayValue
691 * @since 1.4
692 * @beaninfo
693 * bound: true
694 * attribute: visualUpdate true
695 * description: The display prototype value, used to compute display width and height.
696 */
697 public void setPrototypeDisplayValue(E prototypeDisplayValue) {
698 Object oldValue = this.prototypeDisplayValue;
699 this.prototypeDisplayValue = prototypeDisplayValue;
700 firePropertyChange("prototypeDisplayValue", oldValue, prototypeDisplayValue);
701 }
702
703 /**
704 * Adds an item to the item list.
705 * This method works only if the <code>JComboBox</code> uses a
706 * mutable data model.
707 * <p>
708 * <strong>Warning:</strong>
709 * Focus and keyboard navigation problems may arise if you add duplicate
710 * String objects. A workaround is to add new objects instead of String
711 * objects and make sure that the toString() method is defined.
712 * For example:
713 * <pre>
714 * comboBox.addItem(makeObj("Item 1"));
715 * comboBox.addItem(makeObj("Item 1"));
716 * ...
852 public void addItemListener(ItemListener aListener) {
853 listenerList.add(ItemListener.class,aListener);
854 }
855
856 /** Removes an <code>ItemListener</code>.
857 *
858 * @param aListener the <code>ItemListener</code> to remove
859 */
860 public void removeItemListener(ItemListener aListener) {
861 listenerList.remove(ItemListener.class,aListener);
862 }
863
864 /**
865 * Returns an array of all the <code>ItemListener</code>s added
866 * to this JComboBox with addItemListener().
867 *
868 * @return all of the <code>ItemListener</code>s added or an empty
869 * array if no listeners have been added
870 * @since 1.4
871 */
872 public ItemListener[] getItemListeners() {
873 return listenerList.getListeners(ItemListener.class);
874 }
875
876 /**
877 * Adds an <code>ActionListener</code>.
878 * <p>
879 * The <code>ActionListener</code> will receive an <code>ActionEvent</code>
880 * when a selection has been made. If the combo box is editable, then
881 * an <code>ActionEvent</code> will be fired when editing has stopped.
882 *
883 * @param l the <code>ActionListener</code> that is to be notified
884 * @see #setSelectedItem
885 */
886 public void addActionListener(ActionListener l) {
887 listenerList.add(ActionListener.class,l);
888 }
889
890 /** Removes an <code>ActionListener</code>.
891 *
892 * @param l the <code>ActionListener</code> to remove
893 */
894 public void removeActionListener(ActionListener l) {
895 if ((l != null) && (getAction() == l)) {
896 setAction(null);
897 } else {
898 listenerList.remove(ActionListener.class, l);
899 }
900 }
901
902 /**
903 * Returns an array of all the <code>ActionListener</code>s added
904 * to this JComboBox with addActionListener().
905 *
906 * @return all of the <code>ActionListener</code>s added or an empty
907 * array if no listeners have been added
908 * @since 1.4
909 */
910 public ActionListener[] getActionListeners() {
911 return listenerList.getListeners(ActionListener.class);
912 }
913
914 /**
915 * Adds a <code>PopupMenu</code> listener which will listen to notification
916 * messages from the popup portion of the combo box.
917 * <p>
918 * For all standard look and feels shipped with Java, the popup list
919 * portion of combo box is implemented as a <code>JPopupMenu</code>.
920 * A custom look and feel may not implement it this way and will
921 * therefore not receive the notification.
922 *
923 * @param l the <code>PopupMenuListener</code> to add
924 * @since 1.4
925 */
926 public void addPopupMenuListener(PopupMenuListener l) {
927 listenerList.add(PopupMenuListener.class,l);
928 }
929
930 /**
931 * Removes a <code>PopupMenuListener</code>.
932 *
933 * @param l the <code>PopupMenuListener</code> to remove
934 * @see #addPopupMenuListener
935 * @since 1.4
936 */
937 public void removePopupMenuListener(PopupMenuListener l) {
938 listenerList.remove(PopupMenuListener.class,l);
939 }
940
941 /**
942 * Returns an array of all the <code>PopupMenuListener</code>s added
943 * to this JComboBox with addPopupMenuListener().
944 *
945 * @return all of the <code>PopupMenuListener</code>s added or an empty
946 * array if no listeners have been added
947 * @since 1.4
948 */
949 public PopupMenuListener[] getPopupMenuListeners() {
950 return listenerList.getListeners(PopupMenuListener.class);
951 }
952
953 /**
954 * Notifies <code>PopupMenuListener</code>s that the popup portion of the
955 * combo box will become visible.
956 * <p>
957 * This method is public but should not be called by anything other than
958 * the UI delegate.
959 * @see #addPopupMenuListener
960 * @since 1.4
961 */
962 public void firePopupMenuWillBecomeVisible() {
963 Object[] listeners = listenerList.getListenerList();
964 PopupMenuEvent e=null;
965 for (int i = listeners.length-2; i>=0; i-=2) {
966 if (listeners[i]==PopupMenuListener.class) {
967 if (e == null)
968 e = new PopupMenuEvent(this);
1057 * <p>
1058 * This method uses three other methods to set
1059 * and help track the <code>Action</code>'s property values.
1060 * It uses the <code>configurePropertiesFromAction</code> method
1061 * to immediately change the combobox's properties.
1062 * To track changes in the <code>Action</code>'s property values,
1063 * this method registers the <code>PropertyChangeListener</code>
1064 * returned by <code>createActionPropertyChangeListener</code>. The
1065 * default {@code PropertyChangeListener} invokes the
1066 * {@code actionPropertyChanged} method when a property in the
1067 * {@code Action} changes.
1068 *
1069 * @param a the <code>Action</code> for the <code>JComboBox</code>,
1070 * or <code>null</code>.
1071 * @since 1.3
1072 * @see Action
1073 * @see #getAction
1074 * @see #configurePropertiesFromAction
1075 * @see #createActionPropertyChangeListener
1076 * @see #actionPropertyChanged
1077 * @beaninfo
1078 * bound: true
1079 * attribute: visualUpdate true
1080 * description: the Action instance connected with this ActionEvent source
1081 */
1082 public void setAction(Action a) {
1083 Action oldValue = getAction();
1084 if (action==null || !action.equals(a)) {
1085 action = a;
1086 if (oldValue!=null) {
1087 removeActionListener(oldValue);
1088 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1089 actionPropertyChangeListener = null;
1090 }
1091 configurePropertiesFromAction(action);
1092 if (action!=null) {
1093 // Don't add if it is already a listener
1094 if (!isListener(ActionListener.class, action)) {
1095 addActionListener(action);
1096 }
1097 // Reverse linkage:
1098 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1099 action.addPropertyChangeListener(actionPropertyChangeListener);
1100 }
1101 firePropertyChange("action", oldValue, action);
1288 }
1289
1290 // set the new selected item.
1291 selectedItemReminder = dataModel.getSelectedItem();
1292
1293 if (selectedItemReminder != null ) {
1294 fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1295 selectedItemReminder,
1296 ItemEvent.SELECTED));
1297 }
1298 }
1299
1300 /**
1301 * Returns an array containing the selected item.
1302 * This method is implemented for compatibility with
1303 * <code>ItemSelectable</code>.
1304 *
1305 * @return an array of <code>Objects</code> containing one
1306 * element -- the selected item
1307 */
1308 public Object[] getSelectedObjects() {
1309 Object selectedObject = getSelectedItem();
1310 if ( selectedObject == null )
1311 return new Object[0];
1312 else {
1313 Object result[] = new Object[1];
1314 result[0] = selectedObject;
1315 return result;
1316 }
1317 }
1318
1319 /**
1320 * This method is public as an implementation side effect.
1321 * do not call or override.
1322 */
1323 public void actionPerformed(ActionEvent e) {
1324 setPopupVisible(false);
1325 getModel().setSelectedItem(getEditor().getItem());
1326 String oldCommand = getActionCommand();
1327 setActionCommand("comboBoxEdited");
1377
1378 if ( keySelectionManager == null )
1379 keySelectionManager = createDefaultKeySelectionManager();
1380
1381 index = keySelectionManager.selectionForKey(keyChar,getModel());
1382 if ( index != -1 ) {
1383 setSelectedIndex(index);
1384 return true;
1385 }
1386 else
1387 return false;
1388 }
1389
1390 /**
1391 * Enables the combo box so that items can be selected. When the
1392 * combo box is disabled, items cannot be selected and values
1393 * cannot be typed into its field (if it is editable).
1394 *
1395 * @param b a boolean value, where true enables the component and
1396 * false disables it
1397 * @beaninfo
1398 * bound: true
1399 * preferred: true
1400 * description: Whether the combo box is enabled.
1401 */
1402 public void setEnabled(boolean b) {
1403 super.setEnabled(b);
1404 firePropertyChange( "enabled", !isEnabled(), isEnabled() );
1405 }
1406
1407 /**
1408 * Initializes the editor with the specified item.
1409 *
1410 * @param anEditor the <code>ComboBoxEditor</code> that displays
1411 * the list item in the
1412 * combo box field and allows it to be edited
1413 * @param anItem the object to display and edit in the field
1414 */
1415 public void configureEditor(ComboBoxEditor anEditor, Object anItem) {
1416 anEditor.setItem(anItem);
1417 }
1418
1419 /**
1420 * Handles <code>KeyEvent</code>s, looking for the Tab key.
1421 * If the Tab key is found, the popup window is closed.
1441
1442 if (!isEditable() || condition != WHEN_FOCUSED || getEditor() == null
1443 || !Boolean.TRUE.equals(getClientProperty("JComboBox.isTableCellEditor"))) {
1444 return false;
1445 }
1446
1447 Component editorComponent = getEditor().getEditorComponent();
1448 if (editorComponent instanceof JComponent) {
1449 JComponent component = (JComponent) editorComponent;
1450 return component.processKeyBinding(ks, e, WHEN_FOCUSED, pressed);
1451 }
1452 return false;
1453 }
1454
1455 /**
1456 * Sets the object that translates a keyboard character into a list
1457 * selection. Typically, the first selection with a matching first
1458 * character becomes the selected item.
1459 *
1460 * @param aManager a key selection manager
1461 * @beaninfo
1462 * expert: true
1463 * description: The objects that changes the selection when a key is pressed.
1464 */
1465 public void setKeySelectionManager(KeySelectionManager aManager) {
1466 keySelectionManager = aManager;
1467 }
1468
1469 /**
1470 * Returns the list's key-selection manager.
1471 *
1472 * @return the <code>KeySelectionManager</code> currently in use
1473 */
1474 public KeySelectionManager getKeySelectionManager() {
1475 return keySelectionManager;
1476 }
1477
1478 /* Accessing the model */
1479 /**
1480 * Returns the number of items in the list.
1481 *
1482 * @return an integer equal to the number of items in the list
1483 */
1484 public int getItemCount() {
1485 return dataModel.getSize();
1486 }
1487
1488 /**
1489 * Returns the list item at the specified index. If <code>index</code>
1490 * is out of range (less than zero or greater than or equal to size)
1491 * it will return <code>null</code>.
1492 *
1493 * @param index an integer indicating the list position, where the first
1494 * item starts at zero
1495 * @return the item at that list position; or
1496 * <code>null</code> if out of range
1497 */
1498 public E getItemAt(int index) {
1499 return dataModel.getElementAt(index);
1500 }
1501
1502 /**
1503 * Returns an instance of the default key-selection manager.
1612 ",isEditable=" + isEditableString +
1613 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
1614 ",maximumRowCount=" + maximumRowCount +
1615 ",selectedItemReminder=" + selectedItemReminderString;
1616 }
1617
1618
1619 ///////////////////
1620 // Accessibility support
1621 ///////////////////
1622
1623 /**
1624 * Gets the AccessibleContext associated with this JComboBox.
1625 * For combo boxes, the AccessibleContext takes the form of an
1626 * AccessibleJComboBox.
1627 * A new AccessibleJComboBox instance is created if necessary.
1628 *
1629 * @return an AccessibleJComboBox that serves as the
1630 * AccessibleContext of this JComboBox
1631 */
1632 public AccessibleContext getAccessibleContext() {
1633 if ( accessibleContext == null ) {
1634 accessibleContext = new AccessibleJComboBox();
1635 }
1636 return accessibleContext;
1637 }
1638
1639 /**
1640 * This class implements accessibility support for the
1641 * <code>JComboBox</code> class. It provides an implementation of the
1642 * Java Accessibility API appropriate to Combo Box user-interface elements.
1643 * <p>
1644 * <strong>Warning:</strong>
1645 * Serialized objects of this class will not be compatible with
1646 * future Swing releases. The current serialization support is
1647 * appropriate for short term storage or RMI between applications running
1648 * the same version of Swing. As of 1.4, support for long term storage
1649 * of all JavaBeans™
1650 * has been added to the <code>java.beans</code> package.
1651 * Please see {@link java.beans.XMLEncoder}.
2114 * could be 'city.'
2115 *
2116 * @return the localized name of the object; null if this
2117 * object does not have a name
2118 *
2119 * @see #setAccessibleName
2120 */
2121 public String getAccessibleName() {
2122 return ac.getAccessibleName();
2123 }
2124
2125 /**
2126 * Sets the localized accessible name of this object. Changing the
2127 * name will cause a PropertyChangeEvent to be fired for the
2128 * ACCESSIBLE_NAME_PROPERTY property.
2129 *
2130 * @param s the new localized name of the object.
2131 *
2132 * @see #getAccessibleName
2133 * @see #addPropertyChangeListener
2134 *
2135 * @beaninfo
2136 * preferred: true
2137 * description: Sets the accessible name for the component.
2138 */
2139 public void setAccessibleName(String s) {
2140 ac.setAccessibleName(s);
2141 }
2142
2143 /**
2144 * Gets the accessibleDescription property of this object. The
2145 * accessibleDescription property of this object is a short localized
2146 * phrase describing the purpose of the object. For example, in the
2147 * case of a 'Cancel' button, the accessibleDescription could be
2148 * 'Ignore changes and close dialog box.'
2149 *
2150 * @return the localized description of the object; null if
2151 * this object does not have a description
2152 *
2153 * @see #setAccessibleDescription
2154 */
2155 public String getAccessibleDescription() {
2156 return ac.getAccessibleDescription();
2157 }
2158
2159 /**
2160 * Sets the accessible description of this object. Changing the
2161 * name will cause a PropertyChangeEvent to be fired for the
2162 * ACCESSIBLE_DESCRIPTION_PROPERTY property.
2163 *
2164 * @param s the new localized description of the object
2165 *
2166 * @see #setAccessibleName
2167 * @see #addPropertyChangeListener
2168 *
2169 * @beaninfo
2170 * preferred: true
2171 * description: Sets the accessible description for the component.
2172 */
2173 public void setAccessibleDescription(String s) {
2174 ac.setAccessibleDescription(s);
2175 }
2176
2177 /**
2178 * Gets the role of this object. The role of the object is the generic
2179 * purpose or use of the class of this object. For example, the role
2180 * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
2181 * AccessibleRole are provided so component developers can pick from
2182 * a set of predefined roles. This enables assistive technologies to
2183 * provide a consistent interface to various tweaked subclasses of
2184 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
2185 * that act like a push button) as well as distinguish between subclasses
2186 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
2187 * and AccessibleRole.RADIO_BUTTON for radio buttons).
2188 * <p>Note that the AccessibleRole class is also extensible, so
2189 * custom component developers can define their own AccessibleRole's
2190 * if the set of predefined roles is inadequate.
2191 *
2192 * @return an instance of AccessibleRole describing the role of the object
|
1 /*
2 * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package javax.swing;
26
27 import java.beans.JavaBean;
28 import java.beans.BeanProperty;
29 import java.beans.PropertyChangeEvent;
30 import java.beans.PropertyChangeListener;
31 import java.beans.Transient;
32 import java.util.*;
33
34 import java.awt.*;
35 import java.awt.event.*;
36
37 import java.io.Serializable;
38 import java.io.ObjectOutputStream;
39 import java.io.IOException;
40
41 import javax.swing.event.*;
42 import javax.swing.plaf.*;
43
44 import javax.accessibility.*;
45
46 /**
47 * A component that combines a button or editable field and a drop-down list.
48 * The user can select a value from the drop-down list, which appears at the
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="http://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html">How to Use Combo Boxes</a>
68 * in <a href="http://docs.oracle.com/javase/tutorial/"><em>The Java Tutorial</em></a>
69 * for further information.
70 *
71 * @see ComboBoxModel
72 * @see DefaultComboBoxModel
73 *
74 * @param <E> the type of the elements of this combo box
75 *
76 * @author Arnaud Weber
77 * @author Mark Davidson
78 * @since 1.2
79 */
80 @JavaBean(defaultProperty = "UI", description = "A combination of a text field and a drop-down list.")
81 @SwingContainer(false)
82 @SuppressWarnings("serial") // Same-version serialization only
83 public class JComboBox<E> extends JComponent
84 implements ItemSelectable,ListDataListener,ActionListener, Accessible {
85 /**
86 * @see #getUIClassID
87 * @see #readObject
88 */
89 private static final String uiClassID = "ComboBoxUI";
90
91 /**
92 * This protected field is implementation specific. Do not access directly
93 * or override. Use the accessor methods instead.
94 *
95 * @see #getModel
96 * @see #setModel
97 */
98 protected ComboBoxModel<E> dataModel;
99 /**
100 * This protected field is implementation specific. Do not access directly
101 * or override. Use the accessor methods instead.
238 * {@code AncestorEvents} when it or any of its ancestors
239 * move or are made visible or invisible.
240 * Events are also sent when the component or its ancestors are added
241 * or removed from the containment hierarchy.
242 */
243 protected void installAncestorListener() {
244 addAncestorListener(new AncestorListener(){
245 public void ancestorAdded(AncestorEvent event){ hidePopup();}
246 public void ancestorRemoved(AncestorEvent event){ hidePopup();}
247 public void ancestorMoved(AncestorEvent event){
248 if (event.getSource() != JComboBox.this)
249 hidePopup();
250 }});
251 }
252
253 /**
254 * Sets the L&F object that renders this component.
255 *
256 * @param ui the <code>ComboBoxUI</code> L&F object
257 * @see UIDefaults#getUI
258 */
259 @BeanProperty(hidden = true, visualUpdate = true, description
260 = "The UI object that implements the Component's LookAndFeel.")
261 public void setUI(ComboBoxUI ui) {
262 super.setUI(ui);
263 }
264
265 /**
266 * Resets the UI property to a value from the current look and feel.
267 *
268 * @see JComponent#updateUI
269 */
270 public void updateUI() {
271 setUI((ComboBoxUI)UIManager.getUI(this));
272
273 ListCellRenderer<? super E> renderer = getRenderer();
274 if (renderer instanceof Component) {
275 SwingUtilities.updateComponentTreeUI((Component)renderer);
276 }
277 }
278
279
280 /**
281 * Returns the name of the L&F class that renders this component.
282 *
283 * @return the string "ComboBoxUI"
284 * @see JComponent#getUIClassID
285 * @see UIDefaults#getUI
286 */
287 @BeanProperty(bound = false)
288 public String getUIClassID() {
289 return uiClassID;
290 }
291
292
293 /**
294 * Returns the L&F object that renders this component.
295 *
296 * @return the ComboBoxUI object that renders this component
297 */
298 public ComboBoxUI getUI() {
299 return(ComboBoxUI)ui;
300 }
301
302 /**
303 * Sets the data model that the <code>JComboBox</code> uses to obtain
304 * the list of items.
305 *
306 * @param aModel the <code>ComboBoxModel</code> that provides the
307 * displayed list of items
308 */
309 @BeanProperty(description
310 = "Model that the combo box uses to get data to display.")
311 public void setModel(ComboBoxModel<E> aModel) {
312 ComboBoxModel<E> oldModel = dataModel;
313 if (oldModel != null) {
314 oldModel.removeListDataListener(this);
315 }
316 dataModel = aModel;
317 dataModel.addListDataListener(this);
318
319 // set the current selected item.
320 selectedItemReminder = dataModel.getSelectedItem();
321
322 firePropertyChange( "model", oldModel, dataModel);
323 }
324
325 /**
326 * Returns the data model currently used by the <code>JComboBox</code>.
327 *
328 * @return the <code>ComboBoxModel</code> that provides the displayed
329 * list of items
330 */
341 * provides a hint as to whether or not a lightweight
342 * <code>Component</code> should be used to contain the
343 * <code>JComboBox</code>, versus a heavyweight
344 * <code>Component</code> such as a <code>Panel</code>
345 * or a <code>Window</code>. The decision of lightweight
346 * versus heavyweight is ultimately up to the
347 * <code>JComboBox</code>. Lightweight windows are more
348 * efficient than heavyweight windows, but lightweight
349 * and heavyweight components do not mix well in a GUI.
350 * If your application mixes lightweight and heavyweight
351 * components, you should disable lightweight popups.
352 * The default value for the <code>lightWeightPopupEnabled</code>
353 * property is <code>true</code>, unless otherwise specified
354 * by the look and feel. Some look and feels always use
355 * heavyweight popups, no matter what the value of this property.
356 * <p>
357 * See the article <a href="http://www.oracle.com/technetwork/articles/java/mixing-components-433992.html">Mixing Heavy and Light Components</a>
358 * This method fires a property changed event.
359 *
360 * @param aFlag if <code>true</code>, lightweight popups are desired
361 */
362 @BeanProperty(expert = true, description
363 = "Set to <code>false</code> to require heavyweight popups.")
364 public void setLightWeightPopupEnabled(boolean aFlag) {
365 boolean oldFlag = lightWeightPopupEnabled;
366 lightWeightPopupEnabled = aFlag;
367 firePropertyChange("lightWeightPopupEnabled", oldFlag, lightWeightPopupEnabled);
368 }
369
370 /**
371 * Gets the value of the <code>lightWeightPopupEnabled</code>
372 * property.
373 *
374 * @return the value of the <code>lightWeightPopupEnabled</code>
375 * property
376 * @see #setLightWeightPopupEnabled
377 */
378 public boolean isLightWeightPopupEnabled() {
379 return lightWeightPopupEnabled;
380 }
381
382 /**
383 * Determines whether the <code>JComboBox</code> field is editable.
384 * An editable <code>JComboBox</code> allows the user to type into the
385 * field or selected an item from the list to initialize the field,
386 * after which it can be edited. (The editing affects only the field,
387 * the list item remains intact.) A non editable <code>JComboBox</code>
388 * displays the selected item in the field,
389 * but the selection cannot be modified.
390 *
391 * @param aFlag a boolean value, where true indicates that the
392 * field is editable
393 */
394 @BeanProperty(preferred = true, description
395 = "If true, the user can type a new value in the combo box.")
396 public void setEditable(boolean aFlag) {
397 boolean oldFlag = isEditable;
398 isEditable = aFlag;
399 firePropertyChange( "editable", oldFlag, isEditable );
400 }
401
402 /**
403 * Returns true if the <code>JComboBox</code> is editable.
404 * By default, a combo box is not editable.
405 *
406 * @return true if the <code>JComboBox</code> is editable, else false
407 */
408 public boolean isEditable() {
409 return isEditable;
410 }
411
412 /**
413 * Sets the maximum number of rows the <code>JComboBox</code> displays.
414 * If the number of objects in the model is greater than count,
415 * the combo box uses a scrollbar.
416 *
417 * @param count an integer specifying the maximum number of items to
418 * display in the list before using a scrollbar
419 */
420 @BeanProperty(preferred = true, description
421 = "The maximum number of rows the popup should have")
422 public void setMaximumRowCount(int count) {
423 int oldCount = maximumRowCount;
424 maximumRowCount = count;
425 firePropertyChange( "maximumRowCount", oldCount, maximumRowCount );
426 }
427
428 /**
429 * Returns the maximum number of items the combo box can display
430 * without a scrollbar
431 *
432 * @return an integer specifying the maximum number of items that are
433 * displayed in the list before using a scrollbar
434 */
435 public int getMaximumRowCount() {
436 return maximumRowCount;
437 }
438
439 /**
440 * Sets the renderer that paints the list items and the item selected from the list in
441 * the JComboBox field. The renderer is used if the JComboBox is not
442 * editable. If it is editable, the editor is used to render and edit
443 * the selected item.
444 * <p>
445 * The default renderer displays a string or an icon.
446 * Other renderers can handle graphic images and composite items.
447 * <p>
448 * To display the selected item,
449 * <code>aRenderer.getListCellRendererComponent</code>
450 * is called, passing the list object and an index of -1.
451 *
452 * @param aRenderer the <code>ListCellRenderer</code> that
453 * displays the selected item
454 * @see #setEditor
455 */
456 @BeanProperty(expert = true, description
457 = "The renderer that paints the item selected in the list.")
458 public void setRenderer(ListCellRenderer<? super E> aRenderer) {
459 ListCellRenderer<? super E> oldRenderer = renderer;
460 renderer = aRenderer;
461 firePropertyChange( "renderer", oldRenderer, renderer );
462 invalidate();
463 }
464
465 /**
466 * Returns the renderer used to display the selected item in the
467 * <code>JComboBox</code> field.
468 *
469 * @return the <code>ListCellRenderer</code> that displays
470 * the selected item.
471 */
472 public ListCellRenderer<? super E> getRenderer() {
473 return renderer;
474 }
475
476 /**
477 * Sets the editor used to paint and edit the selected item in the
478 * <code>JComboBox</code> field. The editor is used only if the
479 * receiving <code>JComboBox</code> is editable. If not editable,
480 * the combo box uses the renderer to paint the selected item.
481 *
482 * @param anEditor the <code>ComboBoxEditor</code> that
483 * displays the selected item
484 * @see #setRenderer
485 */
486 @BeanProperty(expert = true, description
487 = "The editor that combo box uses to edit the current value")
488 public void setEditor(ComboBoxEditor anEditor) {
489 ComboBoxEditor oldEditor = editor;
490
491 if ( editor != null ) {
492 editor.removeActionListener(this);
493 }
494 editor = anEditor;
495 if ( editor != null ) {
496 editor.addActionListener(this);
497 }
498 firePropertyChange( "editor", oldEditor, editor );
499 }
500
501 /**
502 * Returns the editor used to paint and edit the selected item in the
503 * <code>JComboBox</code> field.
504 *
505 * @return the <code>ComboBoxEditor</code> that displays the selected item
506 */
507 public ComboBoxEditor getEditor() {
519 * <code>anObject</code> selected.
520 * <p>
521 * If <code>anObject</code> is <i>not</i> in the list and the combo box is
522 * uneditable, it will not change the current selection. For editable
523 * combo boxes, the selection will change to <code>anObject</code>.
524 * <p>
525 * If this constitutes a change in the selected item,
526 * <code>ItemListener</code>s added to the combo box will be notified with
527 * one or two <code>ItemEvent</code>s.
528 * If there is a current selected item, an <code>ItemEvent</code> will be
529 * fired and the state change will be <code>ItemEvent.DESELECTED</code>.
530 * If <code>anObject</code> is in the list and is not currently selected
531 * then an <code>ItemEvent</code> will be fired and the state change will
532 * be <code>ItemEvent.SELECTED</code>.
533 * <p>
534 * <code>ActionListener</code>s added to the combo box will be notified
535 * with an <code>ActionEvent</code> when this method is called.
536 *
537 * @param anObject the list object to select; use <code>null</code> to
538 clear the selection
539 */
540 @BeanProperty(bound = false, preferred = true, description
541 = "Sets the selected item in the JComboBox.")
542 public void setSelectedItem(Object anObject) {
543 Object oldSelection = selectedItemReminder;
544 Object objectToSelect = anObject;
545 if (oldSelection == null || !oldSelection.equals(anObject)) {
546
547 if (anObject != null && !isEditable()) {
548 // For non editable combo boxes, an invalid selection
549 // will be rejected.
550 boolean found = false;
551 for (int i = 0; i < dataModel.getSize(); i++) {
552 E element = dataModel.getElementAt(i);
553 if (anObject.equals(element)) {
554 found = true;
555 objectToSelect = element;
556 break;
557 }
558 }
559 if (!found) {
560 return;
561 }
583 * Returns the current selected item.
584 * <p>
585 * If the combo box is editable, then this value may not have been added
586 * to the combo box with <code>addItem</code>, <code>insertItemAt</code>
587 * or the data constructors.
588 *
589 * @return the current selected Object
590 * @see #setSelectedItem
591 */
592 public Object getSelectedItem() {
593 return dataModel.getSelectedItem();
594 }
595
596 /**
597 * Selects the item at index <code>anIndex</code>.
598 *
599 * @param anIndex an integer specifying the list item to select,
600 * where 0 specifies the first item in the list and -1 indicates no selection
601 * @exception IllegalArgumentException if <code>anIndex</code> < -1 or
602 * <code>anIndex</code> is greater than or equal to size
603 */
604 @BeanProperty(bound = false, preferred = true, description
605 = "The item at index is selected.")
606 public void setSelectedIndex(int anIndex) {
607 int size = dataModel.getSize();
608
609 if ( anIndex == -1 ) {
610 setSelectedItem( null );
611 } else if ( anIndex < -1 || anIndex >= size ) {
612 throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
613 } else {
614 setSelectedItem(dataModel.getElementAt(anIndex));
615 }
616 }
617
618 /**
619 * Returns the first item in the list that matches the given item.
620 * The result is not always defined if the <code>JComboBox</code>
621 * allows selected items that are not in the list.
622 * Returns -1 if there is no selected item or if the user specified
623 * an item which is not in the list.
624
625 * @return an integer specifying the currently selected list item,
653 public E getPrototypeDisplayValue() {
654 return prototypeDisplayValue;
655 }
656
657 /**
658 * Sets the prototype display value used to calculate the size of the display
659 * for the UI portion.
660 * <p>
661 * If a prototype display value is specified, the preferred size of
662 * the combo box is calculated by configuring the renderer with the
663 * prototype display value and obtaining its preferred size. Specifying
664 * the preferred display value is often useful when the combo box will be
665 * displaying large amounts of data. If no prototype display value has
666 * been specified, the renderer must be configured for each value from
667 * the model and its preferred size obtained, which can be
668 * relatively expensive.
669 *
670 * @param prototypeDisplayValue the prototype display value
671 * @see #getPrototypeDisplayValue
672 * @since 1.4
673 */
674 @BeanProperty(visualUpdate = true, description
675 = "The display prototype value, used to compute display width and height.")
676 public void setPrototypeDisplayValue(E prototypeDisplayValue) {
677 Object oldValue = this.prototypeDisplayValue;
678 this.prototypeDisplayValue = prototypeDisplayValue;
679 firePropertyChange("prototypeDisplayValue", oldValue, prototypeDisplayValue);
680 }
681
682 /**
683 * Adds an item to the item list.
684 * This method works only if the <code>JComboBox</code> uses a
685 * mutable data model.
686 * <p>
687 * <strong>Warning:</strong>
688 * Focus and keyboard navigation problems may arise if you add duplicate
689 * String objects. A workaround is to add new objects instead of String
690 * objects and make sure that the toString() method is defined.
691 * For example:
692 * <pre>
693 * comboBox.addItem(makeObj("Item 1"));
694 * comboBox.addItem(makeObj("Item 1"));
695 * ...
831 public void addItemListener(ItemListener aListener) {
832 listenerList.add(ItemListener.class,aListener);
833 }
834
835 /** Removes an <code>ItemListener</code>.
836 *
837 * @param aListener the <code>ItemListener</code> to remove
838 */
839 public void removeItemListener(ItemListener aListener) {
840 listenerList.remove(ItemListener.class,aListener);
841 }
842
843 /**
844 * Returns an array of all the <code>ItemListener</code>s added
845 * to this JComboBox with addItemListener().
846 *
847 * @return all of the <code>ItemListener</code>s added or an empty
848 * array if no listeners have been added
849 * @since 1.4
850 */
851 @BeanProperty(bound = false)
852 public ItemListener[] getItemListeners() {
853 return listenerList.getListeners(ItemListener.class);
854 }
855
856 /**
857 * Adds an <code>ActionListener</code>.
858 * <p>
859 * The <code>ActionListener</code> will receive an <code>ActionEvent</code>
860 * when a selection has been made. If the combo box is editable, then
861 * an <code>ActionEvent</code> will be fired when editing has stopped.
862 *
863 * @param l the <code>ActionListener</code> that is to be notified
864 * @see #setSelectedItem
865 */
866 public void addActionListener(ActionListener l) {
867 listenerList.add(ActionListener.class,l);
868 }
869
870 /** Removes an <code>ActionListener</code>.
871 *
872 * @param l the <code>ActionListener</code> to remove
873 */
874 public void removeActionListener(ActionListener l) {
875 if ((l != null) && (getAction() == l)) {
876 setAction(null);
877 } else {
878 listenerList.remove(ActionListener.class, l);
879 }
880 }
881
882 /**
883 * Returns an array of all the <code>ActionListener</code>s added
884 * to this JComboBox with addActionListener().
885 *
886 * @return all of the <code>ActionListener</code>s added or an empty
887 * array if no listeners have been added
888 * @since 1.4
889 */
890 @BeanProperty(bound = false)
891 public ActionListener[] getActionListeners() {
892 return listenerList.getListeners(ActionListener.class);
893 }
894
895 /**
896 * Adds a <code>PopupMenu</code> listener which will listen to notification
897 * messages from the popup portion of the combo box.
898 * <p>
899 * For all standard look and feels shipped with Java, the popup list
900 * portion of combo box is implemented as a <code>JPopupMenu</code>.
901 * A custom look and feel may not implement it this way and will
902 * therefore not receive the notification.
903 *
904 * @param l the <code>PopupMenuListener</code> to add
905 * @since 1.4
906 */
907 public void addPopupMenuListener(PopupMenuListener l) {
908 listenerList.add(PopupMenuListener.class,l);
909 }
910
911 /**
912 * Removes a <code>PopupMenuListener</code>.
913 *
914 * @param l the <code>PopupMenuListener</code> to remove
915 * @see #addPopupMenuListener
916 * @since 1.4
917 */
918 public void removePopupMenuListener(PopupMenuListener l) {
919 listenerList.remove(PopupMenuListener.class,l);
920 }
921
922 /**
923 * Returns an array of all the <code>PopupMenuListener</code>s added
924 * to this JComboBox with addPopupMenuListener().
925 *
926 * @return all of the <code>PopupMenuListener</code>s added or an empty
927 * array if no listeners have been added
928 * @since 1.4
929 */
930 @BeanProperty(bound = false)
931 public PopupMenuListener[] getPopupMenuListeners() {
932 return listenerList.getListeners(PopupMenuListener.class);
933 }
934
935 /**
936 * Notifies <code>PopupMenuListener</code>s that the popup portion of the
937 * combo box will become visible.
938 * <p>
939 * This method is public but should not be called by anything other than
940 * the UI delegate.
941 * @see #addPopupMenuListener
942 * @since 1.4
943 */
944 public void firePopupMenuWillBecomeVisible() {
945 Object[] listeners = listenerList.getListenerList();
946 PopupMenuEvent e=null;
947 for (int i = listeners.length-2; i>=0; i-=2) {
948 if (listeners[i]==PopupMenuListener.class) {
949 if (e == null)
950 e = new PopupMenuEvent(this);
1039 * <p>
1040 * This method uses three other methods to set
1041 * and help track the <code>Action</code>'s property values.
1042 * It uses the <code>configurePropertiesFromAction</code> method
1043 * to immediately change the combobox's properties.
1044 * To track changes in the <code>Action</code>'s property values,
1045 * this method registers the <code>PropertyChangeListener</code>
1046 * returned by <code>createActionPropertyChangeListener</code>. The
1047 * default {@code PropertyChangeListener} invokes the
1048 * {@code actionPropertyChanged} method when a property in the
1049 * {@code Action} changes.
1050 *
1051 * @param a the <code>Action</code> for the <code>JComboBox</code>,
1052 * or <code>null</code>.
1053 * @since 1.3
1054 * @see Action
1055 * @see #getAction
1056 * @see #configurePropertiesFromAction
1057 * @see #createActionPropertyChangeListener
1058 * @see #actionPropertyChanged
1059 */
1060 @BeanProperty(visualUpdate = true, description
1061 = "the Action instance connected with this ActionEvent source")
1062 public void setAction(Action a) {
1063 Action oldValue = getAction();
1064 if (action==null || !action.equals(a)) {
1065 action = a;
1066 if (oldValue!=null) {
1067 removeActionListener(oldValue);
1068 oldValue.removePropertyChangeListener(actionPropertyChangeListener);
1069 actionPropertyChangeListener = null;
1070 }
1071 configurePropertiesFromAction(action);
1072 if (action!=null) {
1073 // Don't add if it is already a listener
1074 if (!isListener(ActionListener.class, action)) {
1075 addActionListener(action);
1076 }
1077 // Reverse linkage:
1078 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1079 action.addPropertyChangeListener(actionPropertyChangeListener);
1080 }
1081 firePropertyChange("action", oldValue, action);
1268 }
1269
1270 // set the new selected item.
1271 selectedItemReminder = dataModel.getSelectedItem();
1272
1273 if (selectedItemReminder != null ) {
1274 fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1275 selectedItemReminder,
1276 ItemEvent.SELECTED));
1277 }
1278 }
1279
1280 /**
1281 * Returns an array containing the selected item.
1282 * This method is implemented for compatibility with
1283 * <code>ItemSelectable</code>.
1284 *
1285 * @return an array of <code>Objects</code> containing one
1286 * element -- the selected item
1287 */
1288 @BeanProperty(bound = false)
1289 public Object[] getSelectedObjects() {
1290 Object selectedObject = getSelectedItem();
1291 if ( selectedObject == null )
1292 return new Object[0];
1293 else {
1294 Object result[] = new Object[1];
1295 result[0] = selectedObject;
1296 return result;
1297 }
1298 }
1299
1300 /**
1301 * This method is public as an implementation side effect.
1302 * do not call or override.
1303 */
1304 public void actionPerformed(ActionEvent e) {
1305 setPopupVisible(false);
1306 getModel().setSelectedItem(getEditor().getItem());
1307 String oldCommand = getActionCommand();
1308 setActionCommand("comboBoxEdited");
1358
1359 if ( keySelectionManager == null )
1360 keySelectionManager = createDefaultKeySelectionManager();
1361
1362 index = keySelectionManager.selectionForKey(keyChar,getModel());
1363 if ( index != -1 ) {
1364 setSelectedIndex(index);
1365 return true;
1366 }
1367 else
1368 return false;
1369 }
1370
1371 /**
1372 * Enables the combo box so that items can be selected. When the
1373 * combo box is disabled, items cannot be selected and values
1374 * cannot be typed into its field (if it is editable).
1375 *
1376 * @param b a boolean value, where true enables the component and
1377 * false disables it
1378 */
1379 @BeanProperty(preferred = true, description
1380 = "The enabled state of the component.")
1381 public void setEnabled(boolean b) {
1382 super.setEnabled(b);
1383 firePropertyChange( "enabled", !isEnabled(), isEnabled() );
1384 }
1385
1386 /**
1387 * Initializes the editor with the specified item.
1388 *
1389 * @param anEditor the <code>ComboBoxEditor</code> that displays
1390 * the list item in the
1391 * combo box field and allows it to be edited
1392 * @param anItem the object to display and edit in the field
1393 */
1394 public void configureEditor(ComboBoxEditor anEditor, Object anItem) {
1395 anEditor.setItem(anItem);
1396 }
1397
1398 /**
1399 * Handles <code>KeyEvent</code>s, looking for the Tab key.
1400 * If the Tab key is found, the popup window is closed.
1420
1421 if (!isEditable() || condition != WHEN_FOCUSED || getEditor() == null
1422 || !Boolean.TRUE.equals(getClientProperty("JComboBox.isTableCellEditor"))) {
1423 return false;
1424 }
1425
1426 Component editorComponent = getEditor().getEditorComponent();
1427 if (editorComponent instanceof JComponent) {
1428 JComponent component = (JComponent) editorComponent;
1429 return component.processKeyBinding(ks, e, WHEN_FOCUSED, pressed);
1430 }
1431 return false;
1432 }
1433
1434 /**
1435 * Sets the object that translates a keyboard character into a list
1436 * selection. Typically, the first selection with a matching first
1437 * character becomes the selected item.
1438 *
1439 * @param aManager a key selection manager
1440 */
1441 @BeanProperty(bound = false, expert = true, description
1442 = "The objects that changes the selection when a key is pressed.")
1443 public void setKeySelectionManager(KeySelectionManager aManager) {
1444 keySelectionManager = aManager;
1445 }
1446
1447 /**
1448 * Returns the list's key-selection manager.
1449 *
1450 * @return the <code>KeySelectionManager</code> currently in use
1451 */
1452 public KeySelectionManager getKeySelectionManager() {
1453 return keySelectionManager;
1454 }
1455
1456 /* Accessing the model */
1457 /**
1458 * Returns the number of items in the list.
1459 *
1460 * @return an integer equal to the number of items in the list
1461 */
1462 @BeanProperty(bound = false)
1463 public int getItemCount() {
1464 return dataModel.getSize();
1465 }
1466
1467 /**
1468 * Returns the list item at the specified index. If <code>index</code>
1469 * is out of range (less than zero or greater than or equal to size)
1470 * it will return <code>null</code>.
1471 *
1472 * @param index an integer indicating the list position, where the first
1473 * item starts at zero
1474 * @return the item at that list position; or
1475 * <code>null</code> if out of range
1476 */
1477 public E getItemAt(int index) {
1478 return dataModel.getElementAt(index);
1479 }
1480
1481 /**
1482 * Returns an instance of the default key-selection manager.
1591 ",isEditable=" + isEditableString +
1592 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
1593 ",maximumRowCount=" + maximumRowCount +
1594 ",selectedItemReminder=" + selectedItemReminderString;
1595 }
1596
1597
1598 ///////////////////
1599 // Accessibility support
1600 ///////////////////
1601
1602 /**
1603 * Gets the AccessibleContext associated with this JComboBox.
1604 * For combo boxes, the AccessibleContext takes the form of an
1605 * AccessibleJComboBox.
1606 * A new AccessibleJComboBox instance is created if necessary.
1607 *
1608 * @return an AccessibleJComboBox that serves as the
1609 * AccessibleContext of this JComboBox
1610 */
1611 @BeanProperty(bound = false)
1612 public AccessibleContext getAccessibleContext() {
1613 if ( accessibleContext == null ) {
1614 accessibleContext = new AccessibleJComboBox();
1615 }
1616 return accessibleContext;
1617 }
1618
1619 /**
1620 * This class implements accessibility support for the
1621 * <code>JComboBox</code> class. It provides an implementation of the
1622 * Java Accessibility API appropriate to Combo Box user-interface elements.
1623 * <p>
1624 * <strong>Warning:</strong>
1625 * Serialized objects of this class will not be compatible with
1626 * future Swing releases. The current serialization support is
1627 * appropriate for short term storage or RMI between applications running
1628 * the same version of Swing. As of 1.4, support for long term storage
1629 * of all JavaBeans™
1630 * has been added to the <code>java.beans</code> package.
1631 * Please see {@link java.beans.XMLEncoder}.
2094 * could be 'city.'
2095 *
2096 * @return the localized name of the object; null if this
2097 * object does not have a name
2098 *
2099 * @see #setAccessibleName
2100 */
2101 public String getAccessibleName() {
2102 return ac.getAccessibleName();
2103 }
2104
2105 /**
2106 * Sets the localized accessible name of this object. Changing the
2107 * name will cause a PropertyChangeEvent to be fired for the
2108 * ACCESSIBLE_NAME_PROPERTY property.
2109 *
2110 * @param s the new localized name of the object.
2111 *
2112 * @see #getAccessibleName
2113 * @see #addPropertyChangeListener
2114 */
2115 @BeanProperty(preferred = true, description
2116 = "Sets the accessible name for the component.")
2117 public void setAccessibleName(String s) {
2118 ac.setAccessibleName(s);
2119 }
2120
2121 /**
2122 * Gets the accessibleDescription property of this object. The
2123 * accessibleDescription property of this object is a short localized
2124 * phrase describing the purpose of the object. For example, in the
2125 * case of a 'Cancel' button, the accessibleDescription could be
2126 * 'Ignore changes and close dialog box.'
2127 *
2128 * @return the localized description of the object; null if
2129 * this object does not have a description
2130 *
2131 * @see #setAccessibleDescription
2132 */
2133 public String getAccessibleDescription() {
2134 return ac.getAccessibleDescription();
2135 }
2136
2137 /**
2138 * Sets the accessible description of this object. Changing the
2139 * name will cause a PropertyChangeEvent to be fired for the
2140 * ACCESSIBLE_DESCRIPTION_PROPERTY property.
2141 *
2142 * @param s the new localized description of the object
2143 *
2144 * @see #setAccessibleName
2145 * @see #addPropertyChangeListener
2146 */
2147 @BeanProperty(preferred = true, description
2148 = "Sets the accessible description for the component.")
2149 public void setAccessibleDescription(String s) {
2150 ac.setAccessibleDescription(s);
2151 }
2152
2153 /**
2154 * Gets the role of this object. The role of the object is the generic
2155 * purpose or use of the class of this object. For example, the role
2156 * of a push button is AccessibleRole.PUSH_BUTTON. The roles in
2157 * AccessibleRole are provided so component developers can pick from
2158 * a set of predefined roles. This enables assistive technologies to
2159 * provide a consistent interface to various tweaked subclasses of
2160 * components (e.g., use AccessibleRole.PUSH_BUTTON for all components
2161 * that act like a push button) as well as distinguish between subclasses
2162 * that behave differently (e.g., AccessibleRole.CHECK_BOX for check boxes
2163 * and AccessibleRole.RADIO_BUTTON for radio buttons).
2164 * <p>Note that the AccessibleRole class is also extensible, so
2165 * custom component developers can define their own AccessibleRole's
2166 * if the set of predefined roles is inadequate.
2167 *
2168 * @return an instance of AccessibleRole describing the role of the object
|