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
47 * user's request. If you make the combo box editable, then the combo box
48 * includes an editable field into which the user can type a value.
49 * <p>
50 * <strong>Warning:</strong> Swing is not thread safe. For more
51 * information see <a
52 * href="package-summary.html#threading">Swing's Threading
53 * Policy</a>.
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 */
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 /**
172 * Creates a <code>JComboBox</code> that takes its items from an
173 * existing <code>ComboBoxModel</code>. Since the
174 * <code>ComboBoxModel</code> is provided, a combo box created using
175 * this constructor does not create a default combo box model and
176 * may impact how the insert, remove and add methods behave.
177 *
178 * @param aModel the <code>ComboBoxModel</code> that provides the
179 * displayed list of items
180 * @see DefaultComboBoxModel
181 */
182 public JComboBox(ComboBoxModel<E> aModel) {
183 super();
184 setModel(aModel);
185 init();
186 }
187
188 /**
189 * Creates a <code>JComboBox</code> that contains the elements
190 * in the specified array. By default the first item in the array
191 * (and therefore the data model) becomes selected.
192 *
193 * @param items an array of objects to insert into the combo box
194 * @see DefaultComboBoxModel
195 */
196 public JComboBox(E[] items) {
197 super();
198 setModel(new DefaultComboBoxModel<E>(items));
199 init();
200 }
201
202 /**
203 * Creates a <code>JComboBox</code> that contains the elements
204 * in the specified Vector. By default the first item in the vector
205 * (and therefore the data model) becomes selected.
206 *
207 * @param items an array of vectors to insert into the combo box
208 * @see DefaultComboBoxModel
209 */
210 public JComboBox(Vector<E> items) {
211 super();
212 setModel(new DefaultComboBoxModel<E>(items));
213 init();
214 }
215
216 /**
217 * Creates a <code>JComboBox</code> with a default data model.
218 * The default data model is an empty list of objects.
219 * Use <code>addItem</code> to add items. By default the first item
220 * in the data model becomes selected.
221 *
222 * @see DefaultComboBoxModel
223 */
224 public JComboBox() {
225 super();
226 setModel(new DefaultComboBoxModel<E>());
227 init();
228 }
229
230 private void init() {
231 installAncestorListener();
232 setUIProperty("opaque",true);
233 updateUI();
234 }
235
236 /**
237 * Registers ancestor listener so that it will receive
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
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 */
336 public ComboBoxModel<E> getModel() {
337 return dataModel;
338 }
339
340 /*
341 * Properties
342 */
343
344 /**
345 * Sets the <code>lightWeightPopupEnabled</code> property, which
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() {
525 return editor;
526 }
527
528 //
529 // Selection
530 //
531
532 /**
533 * Sets the selected item in the combo box display area to the object in
534 * the argument.
535 * If <code>anObject</code> is in the list, the display area shows
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;
584 // Must toggle the state of this flag since this method
585 // call may result in ListDataEvents being fired.
586 selectingItem = true;
587 dataModel.setSelectedItem(objectToSelect);
588 selectingItem = false;
589
590 if (selectedItemReminder != dataModel.getSelectedItem()) {
591 // in case a users implementation of ComboBoxModel
592 // doesn't fire a ListDataEvent when the selection
593 // changes.
594 selectedItemChanged();
595 }
596 }
597 fireActionEvent();
598 }
599
600 /**
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,
645 * where 0 specifies
646 * the first item in the list;
647 * or -1 if no item is selected or if
648 * the currently selected item is not in the list
649 */
650 @Transient
651 public int getSelectedIndex() {
652 Object sObject = dataModel.getSelectedItem();
653 int i,c;
654 E obj;
655
656 for ( i=0,c=dataModel.getSize();i<c;i++ ) {
657 obj = dataModel.getElementAt(i);
658 if ( obj != null && obj.equals(sObject) )
659 return i;
660 }
661 return -1;
662 }
663
664 /**
665 * Returns the "prototypical display" value - an Object used
666 * for the calculation of the display height and width.
667 *
668 * @return the value of the <code>prototypeDisplayValue</code> property
669 * @see #setPrototypeDisplayValue
670 * @since 1.4
671 */
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 * ...
717 * private Object makeObj(final String item) {
718 * return new Object() { public String toString() { return item; } };
719 * }
720 * </pre>
721 *
722 * @param item the item to add to the list
723 * @see MutableComboBoxModel
724 */
725 public void addItem(E item) {
726 checkMutableComboBoxModel();
727 ((MutableComboBoxModel<E>)dataModel).addElement(item);
728 }
729
730 /**
731 * Inserts an item into the item list at a given index.
732 * This method works only if the <code>JComboBox</code> uses a
733 * mutable data model.
734 *
735 * @param item the item to add to the list
736 * @param index an integer specifying the position at which
737 * to add the item
738 * @see MutableComboBoxModel
739 */
740 public void insertItemAt(E item, int index) {
741 checkMutableComboBoxModel();
742 ((MutableComboBoxModel<E>)dataModel).insertElementAt(item,index);
743 }
744
745 /**
746 * Removes an item from the item list.
747 * This method works only if the <code>JComboBox</code> uses a
748 * mutable data model.
749 *
750 * @param anObject the object to remove from the item list
751 * @see MutableComboBoxModel
752 */
753 public void removeItem(Object anObject) {
754 checkMutableComboBoxModel();
755 ((MutableComboBoxModel)dataModel).removeElement(anObject);
756 }
757
758 /**
759 * Removes the item at <code>anIndex</code>
760 * This method works only if the <code>JComboBox</code> uses a
761 * mutable data model.
762 *
763 * @param anIndex an int specifying the index of the item to remove,
764 * where 0
765 * indicates the first item in the list
766 * @see MutableComboBoxModel
767 */
768 public void removeItemAt(int anIndex) {
769 checkMutableComboBoxModel();
770 ((MutableComboBoxModel<E>)dataModel).removeElementAt( anIndex );
771 }
772
773 /**
774 * Removes all items from the item list.
775 */
776 public void removeAllItems() {
777 checkMutableComboBoxModel();
778 MutableComboBoxModel<E> model = (MutableComboBoxModel<E>)dataModel;
779 int size = model.getSize();
780
781 if ( model instanceof DefaultComboBoxModel ) {
782 ((DefaultComboBoxModel)model).removeAllElements();
783 }
784 else {
785 for ( int i = 0; i < size; ++i ) {
786 E element = model.getElementAt( 0 );
787 model.removeElement( element );
788 }
789 }
790 selectedItemReminder = null;
791 if (isEditable()) {
792 editor.setItem(null);
793 }
794 }
795
796 /**
797 * Checks that the <code>dataModel</code> is an instance of
798 * <code>MutableComboBoxModel</code>. If not, it throws an exception.
799 * @exception RuntimeException if <code>dataModel</code> is not an
800 * instance of <code>MutableComboBoxModel</code>.
801 */
802 void checkMutableComboBoxModel() {
803 if ( !(dataModel instanceof MutableComboBoxModel) )
804 throw new RuntimeException("Cannot use this method with a non-Mutable data model.");
805 }
806
807 /**
808 * Causes the combo box to display its popup window.
809 * @see #setPopupVisible
810 */
811 public void showPopup() {
812 setPopupVisible(true);
813 }
814
815 /**
816 * Causes the combo box to close its popup window.
817 * @see #setPopupVisible
818 */
819 public void hidePopup() {
820 setPopupVisible(false);
824 * Sets the visibility of the popup.
825 *
826 * @param v if {@code true} shows the popup, otherwise, hides the popup.
827 */
828 public void setPopupVisible(boolean v) {
829 getUI().setPopupVisible(this, v);
830 }
831
832 /**
833 * Determines the visibility of the popup.
834 *
835 * @return true if the popup is visible, otherwise returns false
836 */
837 public boolean isPopupVisible() {
838 return getUI().isPopupVisible(this);
839 }
840
841 /** Selection **/
842
843 /**
844 * Adds an <code>ItemListener</code>.
845 * <p>
846 * <code>aListener</code> will receive one or two <code>ItemEvent</code>s when
847 * the selected item changes.
848 *
849 * @param aListener the <code>ItemListener</code> that is to be notified
850 * @see #setSelectedItem
851 */
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);
969 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
970 }
971 }
972 }
973
974 /**
975 * Notifies <code>PopupMenuListener</code>s that the popup portion of the
976 * combo box has become invisible.
977 * <p>
978 * This method is public but should not be called by anything other than
979 * the UI delegate.
980 * @see #addPopupMenuListener
981 * @since 1.4
982 */
983 public void firePopupMenuWillBecomeInvisible() {
984 Object[] listeners = listenerList.getListenerList();
985 PopupMenuEvent e=null;
986 for (int i = listeners.length-2; i>=0; i-=2) {
987 if (listeners[i]==PopupMenuListener.class) {
988 if (e == null)
989 e = new PopupMenuEvent(this);
990 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
991 }
992 }
993 }
994
995 /**
996 * Notifies <code>PopupMenuListener</code>s that the popup portion of the
997 * combo box has been canceled.
998 * <p>
999 * This method is public but should not be called by anything other than
1000 * the UI delegate.
1001 * @see #addPopupMenuListener
1002 * @since 1.4
1003 */
1004 public void firePopupMenuCanceled() {
1005 Object[] listeners = listenerList.getListenerList();
1006 PopupMenuEvent e=null;
1007 for (int i = listeners.length-2; i>=0; i-=2) {
1008 if (listeners[i]==PopupMenuListener.class) {
1009 if (e == null)
1010 e = new PopupMenuEvent(this);
1011 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
1012 }
1013 }
1014 }
1015
1016 /**
1024 */
1025 public void setActionCommand(String aCommand) {
1026 actionCommand = aCommand;
1027 }
1028
1029 /**
1030 * Returns the action command that is included in the event sent to
1031 * action listeners.
1032 *
1033 * @return the string containing the "command" that is sent
1034 * to action listeners.
1035 */
1036 public String getActionCommand() {
1037 return actionCommand;
1038 }
1039
1040 private Action action;
1041 private PropertyChangeListener actionPropertyChangeListener;
1042
1043 /**
1044 * Sets the <code>Action</code> for the <code>ActionEvent</code> source.
1045 * The new <code>Action</code> replaces any previously set
1046 * <code>Action</code> but does not affect <code>ActionListeners</code>
1047 * independently added with <code>addActionListener</code>.
1048 * If the <code>Action</code> is already a registered
1049 * <code>ActionListener</code> for the <code>ActionEvent</code> source,
1050 * it is not re-registered.
1051 * <p>
1052 * Setting the <code>Action</code> results in immediately changing
1053 * all the properties described in <a href="Action.html#buttonActions">
1054 * Swing Components Supporting <code>Action</code></a>.
1055 * Subsequently, the combobox's properties are automatically updated
1056 * as the <code>Action</code>'s properties change.
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 }
1097 // Reverse linkage:
1098 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1099 action.addPropertyChangeListener(actionPropertyChangeListener);
1100 }
1101 firePropertyChange("action", oldValue, action);
1102 }
1103 }
1104
1105 private boolean isListener(Class<?> c, ActionListener a) {
1106 boolean isListener = false;
1107 Object[] listeners = listenerList.getListenerList();
1108 for (int i = listeners.length-2; i>=0; i-=2) {
1109 if (listeners[i]==c && listeners[i+1]==a) {
1110 isListener=true;
1111 }
1112 }
1113 return isListener;
1114 }
1115
1116 /**
1117 * Returns the currently set <code>Action</code> for this
1118 * <code>ActionEvent</code> source, or <code>null</code> if no
1119 * <code>Action</code> is set.
1120 *
1121 * @return the <code>Action</code> for this <code>ActionEvent</code>
1122 * source; or <code>null</code>
1123 * @since 1.3
1124 * @see Action
1125 * @see #setAction
1126 */
1127 public Action getAction() {
1128 return action;
1129 }
1130
1131 /**
1132 * Sets the properties on this combobox to match those in the specified
1133 * <code>Action</code>. Refer to <a href="Action.html#buttonActions">
1134 * Swing Components Supporting <code>Action</code></a> for more
1135 * details as to which properties this sets.
1136 *
1137 * @param a the <code>Action</code> from which to get the properties,
1138 * or <code>null</code>
1139 * @since 1.3
1140 * @see Action
1141 * @see #setAction
1142 */
1143 protected void configurePropertiesFromAction(Action a) {
1144 AbstractAction.setEnabledFromAction(this, a);
1145 AbstractAction.setToolTipTextFromAction(this, a);
1146 setActionCommandFromAction(a);
1147 }
1148
1149 /**
1150 * Creates and returns a <code>PropertyChangeListener</code> that is
1151 * responsible for listening for changes from the specified
1152 * <code>Action</code> and updating the appropriate properties.
1153 * <p>
1154 * <b>Warning:</b> If you subclass this do not create an anonymous
1155 * inner class. If you do the lifetime of the combobox will be tied to
1156 * that of the <code>Action</code>.
1157 *
1158 * @param a the combobox's action
1159 * @return the {@code PropertyChangeListener}
1160 * @since 1.3
1161 * @see Action
1162 * @see #setAction
1163 */
1164 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1165 return new ComboBoxActionPropertyChangeListener(this, a);
1166 }
1167
1168 /**
1169 * Updates the combobox's state in response to property changes in
1170 * associated action. This method is invoked from the
1171 * {@code PropertyChangeListener} returned from
1172 * {@code createActionPropertyChangeListener}. Subclasses do not normally
1173 * need to invoke this. Subclasses that support additional {@code Action}
1174 * properties should override this and
1175 * {@code configurePropertiesFromAction}.
1176 * <p>
1177 * Refer to the table at <a href="Action.html#buttonActions">
1178 * Swing Components Supporting <code>Action</code></a> for a list of
1179 * the properties this method sets.
1180 *
1181 * @param action the <code>Action</code> associated with this combobox
1182 * @param propertyName the name of the property that changed
1183 * @since 1.6
1184 * @see Action
1185 * @see #configurePropertiesFromAction
1186 */
1187 protected void actionPropertyChanged(Action action, String propertyName) {
1188 if (propertyName == Action.ACTION_COMMAND_KEY) {
1189 setActionCommandFromAction(action);
1190 } else if (propertyName == "enabled") {
1191 AbstractAction.setEnabledFromAction(this, action);
1192 } else if (Action.SHORT_DESCRIPTION == propertyName) {
1193 AbstractAction.setToolTipTextFromAction(this, action);
1194 }
1195 }
1196
1197 private void setActionCommandFromAction(Action a) {
1198 setActionCommand((a != null) ?
1199 (String)a.getValue(Action.ACTION_COMMAND_KEY) :
1200 null);
1201 }
1283 protected void selectedItemChanged() {
1284 if (selectedItemReminder != null ) {
1285 fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1286 selectedItemReminder,
1287 ItemEvent.DESELECTED));
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());
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.
1422 *
1423 * @param e the <code>KeyEvent</code> containing the keyboard
1424 * key that was pressed
1425 */
1426 public void processKeyEvent(KeyEvent e) {
1427 if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
1428 hidePopup();
1429 }
1430 super.processKeyEvent(e);
1431 }
1432
1433 /**
1434 * {@inheritDoc}
1435 */
1436 @Override
1437 protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
1438 if (super.processKeyBinding(ks, e, condition, pressed)) {
1439 return true;
1440 }
1441
1442 if (!isEditable() || condition != WHEN_FOCUSED || getEditor() == null
1443 || !Boolean.TRUE.equals(getClientProperty("JComboBox.isTableCellEditor"))) {
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.
1504 *
1505 * @return the <code>KeySelectionManager</code> currently used by the list
1506 * @see #setKeySelectionManager
1507 */
1508 protected KeySelectionManager createDefaultKeySelectionManager() {
1509 return new DefaultKeySelectionManager();
1510 }
1511
1512
1513 /**
1514 * The interface that defines a <code>KeySelectionManager</code>.
1515 * To qualify as a <code>KeySelectionManager</code>,
1516 * the class needs to implement the method
1517 * that identifies the list index given a character and the
1518 * combo box data model.
1519 */
1520 public interface KeySelectionManager {
1521 /** Given <code>aKey</code> and the model, returns the row
1522 * that should become selected. Return -1 if no match was
1523 * found.
1524 *
1525 * @param aKey a char value, usually indicating a keyboard key that
1526 * was pressed
1527 * @param aModel a ComboBoxModel -- the component's data model, containing
1528 * the list of selectable items
1529 * @return an int equal to the selected row, where 0 is the
1530 * first item and -1 is none.
1531 */
1532 int selectionForKey(char aKey,ComboBoxModel<?> aModel);
1533 }
1534
1535 class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
1536 public int selectionForKey(char aKey,ComboBoxModel<?> aModel) {
1537 int i,c;
1538 int currentSelection = -1;
1539 Object selectedItem = aModel.getSelectedItem();
1540 String v;
1541 String pattern;
1558 v = elem.toString().toLowerCase();
1559 if ( v.length() > 0 && v.charAt(0) == aKey )
1560 return i;
1561 }
1562 }
1563
1564 for ( i = 0 ; i < currentSelection ; i ++ ) {
1565 Object elem = aModel.getElementAt(i);
1566 if (elem != null && elem.toString() != null) {
1567 v = elem.toString().toLowerCase();
1568 if ( v.length() > 0 && v.charAt(0) == aKey )
1569 return i;
1570 }
1571 }
1572 return -1;
1573 }
1574 }
1575
1576
1577 /**
1578 * See <code>readObject</code> and <code>writeObject</code> in
1579 * <code>JComponent</code> for more
1580 * information about serialization in Swing.
1581 */
1582 private void writeObject(ObjectOutputStream s) throws IOException {
1583 s.defaultWriteObject();
1584 if (getUIClassID().equals(uiClassID)) {
1585 byte count = JComponent.getWriteObjCounter(this);
1586 JComponent.setWriteObjCounter(this, --count);
1587 if (count == 0 && ui != null) {
1588 ui.installUI(this);
1589 }
1590 }
1591 }
1592
1593
1594 /**
1595 * Returns a string representation of this <code>JComboBox</code>.
1596 * This method is intended to be used only for debugging purposes,
1597 * and the content and format of the returned string may vary between
1598 * implementations. The returned string may be empty but may not
1599 * be <code>null</code>.
1600 *
1601 * @return a string representation of this <code>JComboBox</code>
1602 */
1603 protected String paramString() {
1604 String selectedItemReminderString = (selectedItemReminder != null ?
1605 selectedItemReminder.toString() :
1606 "");
1607 String isEditableString = (isEditable ? "true" : "false");
1608 String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
1609 "true" : "false");
1610
1611 return super.paramString() +
1612 ",isEditable=" + isEditableString +
1613 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
1614 ",maximumRowCount=" + maximumRowCount +
1615 ",selectedItemReminder=" + selectedItemReminderString;
1616 }
1617
1618
1619 ///////////////////
1620 // Accessibility support
1621 ///////////////////
1622
1623 /**
1624 * Gets the AccessibleContext associated with this JComboBox.
1625 * For combo boxes, the AccessibleContext takes the form of an
1626 * AccessibleJComboBox.
1627 * A new AccessibleJComboBox instance is created if necessary.
1628 *
1629 * @return an AccessibleJComboBox that serves as the
1630 * AccessibleContext of this JComboBox
1631 */
1632 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}.
1652 */
1653 @SuppressWarnings("serial") // Same-version serialization only
1654 protected class AccessibleJComboBox extends AccessibleJComponent
1655 implements AccessibleAction, AccessibleSelection {
1656
1657
1658 private JList<?> popupList; // combo box popup list
1659 private Accessible previousSelectedAccessible = null;
1660
1661 /**
1662 * Returns an AccessibleJComboBox instance
1663 * @since 1.4
1664 */
1665 public AccessibleJComboBox() {
1666 // set the combo box editor's accessible name and description
1667 JComboBox.this.addPropertyChangeListener(new AccessibleJComboBoxPropertyChangeListener());
1668 setEditorNameAndDescription();
1669
1670 // Get the popup list
|
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
47 * user's request. If you make the combo box editable, then the combo box
48 * includes an editable field into which the user can type a value.
49 * <p>
50 * <strong>Warning:</strong> Swing is not thread safe. For more
51 * information see <a
52 * href="package-summary.html#threading">Swing's Threading
53 * Policy</a>.
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} 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 */
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 /**
172 * Creates a {@code JComboBox} that takes its items from an
173 * existing {@code ComboBoxModel}. Since the
174 * {@code ComboBoxModel} is provided, a combo box created using
175 * this constructor does not create a default combo box model and
176 * may impact how the insert, remove and add methods behave.
177 *
178 * @param aModel the {@code ComboBoxModel} that provides the
179 * displayed list of items
180 * @see DefaultComboBoxModel
181 */
182 public JComboBox(ComboBoxModel<E> aModel) {
183 super();
184 setModel(aModel);
185 init();
186 }
187
188 /**
189 * Creates a {@code JComboBox} that contains the elements
190 * in the specified array. By default the first item in the array
191 * (and therefore the data model) becomes selected.
192 *
193 * @param items an array of objects to insert into the combo box
194 * @see DefaultComboBoxModel
195 */
196 public JComboBox(E[] items) {
197 super();
198 setModel(new DefaultComboBoxModel<E>(items));
199 init();
200 }
201
202 /**
203 * Creates a {@code JComboBox} that contains the elements
204 * in the specified Vector. By default the first item in the vector
205 * (and therefore the data model) becomes selected.
206 *
207 * @param items an array of vectors to insert into the combo box
208 * @see DefaultComboBoxModel
209 */
210 public JComboBox(Vector<E> items) {
211 super();
212 setModel(new DefaultComboBoxModel<E>(items));
213 init();
214 }
215
216 /**
217 * Creates a {@code JComboBox} with a default data model.
218 * The default data model is an empty list of objects.
219 * Use {@code addItem} to add items. By default the first item
220 * in the data model becomes selected.
221 *
222 * @see DefaultComboBoxModel
223 */
224 public JComboBox() {
225 super();
226 setModel(new DefaultComboBoxModel<E>());
227 init();
228 }
229
230 private void init() {
231 installAncestorListener();
232 setUIProperty("opaque",true);
233 updateUI();
234 }
235
236 /**
237 * Registers ancestor listener so that it will receive
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} 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
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} uses to obtain
307 * the list of items.
308 *
309 * @param aModel the {@code ComboBoxModel} 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}.
332 *
333 * @return the {@code ComboBoxModel} that provides the displayed
334 * list of items
335 */
336 public ComboBoxModel<E> getModel() {
337 return dataModel;
338 }
339
340 /*
341 * Properties
342 */
343
344 /**
345 * Sets the {@code lightWeightPopupEnabled} property, which
346 * provides a hint as to whether or not a lightweight
347 * {@code Component} should be used to contain the
348 * {@code JComboBox}, versus a heavyweight
349 * {@code Component} such as a {@code Panel}
350 * or a {@code Window}. The decision of lightweight
351 * versus heavyweight is ultimately up to the
352 * {@code JComboBox}. 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}
358 * property is {@code true}, 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}, lightweight popups are desired
366 *
367 * @beaninfo
368 * bound: true
369 * expert: true
370 * description: Set to {@code false} 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}
380 * property.
381 *
382 * @return the value of the {@code lightWeightPopupEnabled}
383 * property
384 * @see #setLightWeightPopupEnabled
385 */
386 public boolean isLightWeightPopupEnabled() {
387 return lightWeightPopupEnabled;
388 }
389
390 /**
391 * Determines whether the {@code JComboBox} field is editable.
392 * An editable {@code JComboBox} 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}
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} is editable.
415 * By default, a combo box is not editable.
416 *
417 * @return true if the {@code JComboBox} 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} 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}
463 * is called, passing the list object and an index of -1.
464 *
465 * @param aRenderer the {@code ListCellRenderer} 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} field.
483 *
484 * @return the {@code ListCellRenderer} 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} field. The editor is used only if the
494 * receiving {@code JComboBox} is editable. If not editable,
495 * the combo box uses the renderer to paint the selected item.
496 *
497 * @param anEditor the {@code ComboBoxEditor} 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} field.
521 *
522 * @return the {@code ComboBoxEditor} that displays the selected item
523 */
524 public ComboBoxEditor getEditor() {
525 return editor;
526 }
527
528 //
529 // Selection
530 //
531
532 /**
533 * Sets the selected item in the combo box display area to the object in
534 * the argument.
535 * If {@code anObject} is in the list, the display area shows
536 * {@code anObject} selected.
537 * <p>
538 * If {@code anObject} 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}.
541 * <p>
542 * If this constitutes a change in the selected item,
543 * {@code ItemListener}s added to the combo box will be notified with
544 * one or two {@code ItemEvent}s.
545 * If there is a current selected item, an {@code ItemEvent} will be
546 * fired and the state change will be {@code ItemEvent.DESELECTED}.
547 * If {@code anObject} is in the list and is not currently selected
548 * then an {@code ItemEvent} will be fired and the state change will
549 * be {@code ItemEvent.SELECTED}.
550 * <p>
551 * {@code ActionListener}s added to the combo box will be notified
552 * with an {@code ActionEvent} when this method is called.
553 *
554 * @param anObject the list object to select; use {@code null} 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;
584 // Must toggle the state of this flag since this method
585 // call may result in ListDataEvents being fired.
586 selectingItem = true;
587 dataModel.setSelectedItem(objectToSelect);
588 selectingItem = false;
589
590 if (selectedItemReminder != dataModel.getSelectedItem()) {
591 // in case a users implementation of ComboBoxModel
592 // doesn't fire a ListDataEvent when the selection
593 // changes.
594 selectedItemChanged();
595 }
596 }
597 fireActionEvent();
598 }
599
600 /**
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 insertItemAt}
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}.
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 < -1} or
620 * {@code anIndex} 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}
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,
645 * where 0 specifies
646 * the first item in the list;
647 * or -1 if no item is selected or if
648 * the currently selected item is not in the list
649 */
650 @Transient
651 public int getSelectedIndex() {
652 Object sObject = dataModel.getSelectedItem();
653 int i,c;
654 E obj;
655
656 for ( i=0,c=dataModel.getSize();i<c;i++ ) {
657 obj = dataModel.getElementAt(i);
658 if ( obj != null && obj.equals(sObject) )
659 return i;
660 }
661 return -1;
662 }
663
664 /**
665 * Returns the "prototypical display" value - an Object used
666 * for the calculation of the display height and width.
667 *
668 * @return the value of the {@code prototypeDisplayValue} property
669 * @see #setPrototypeDisplayValue
670 * @since 1.4
671 */
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} 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 * ...
717 * private Object makeObj(final String item) {
718 * return new Object() { public String toString() { return item; } };
719 * }
720 * </pre>
721 *
722 * @param item the item to add to the list
723 * @see MutableComboBoxModel
724 */
725 public void addItem(E item) {
726 checkMutableComboBoxModel();
727 ((MutableComboBoxModel<E>)dataModel).addElement(item);
728 }
729
730 /**
731 * Inserts an item into the item list at a given index.
732 * This method works only if the {@code JComboBox} uses a
733 * mutable data model.
734 *
735 * @param item the item to add to the list
736 * @param index an integer specifying the position at which
737 * to add the item
738 * @see MutableComboBoxModel
739 */
740 public void insertItemAt(E item, int index) {
741 checkMutableComboBoxModel();
742 ((MutableComboBoxModel<E>)dataModel).insertElementAt(item,index);
743 }
744
745 /**
746 * Removes an item from the item list.
747 * This method works only if the {@code JComboBox} uses a
748 * mutable data model.
749 *
750 * @param anObject the object to remove from the item list
751 * @see MutableComboBoxModel
752 */
753 public void removeItem(Object anObject) {
754 checkMutableComboBoxModel();
755 ((MutableComboBoxModel)dataModel).removeElement(anObject);
756 }
757
758 /**
759 * Removes the item at {@code anIndex}
760 * This method works only if the {@code JComboBox} uses a
761 * mutable data model.
762 *
763 * @param anIndex an int specifying the index of the item to remove,
764 * where 0
765 * indicates the first item in the list
766 * @see MutableComboBoxModel
767 */
768 public void removeItemAt(int anIndex) {
769 checkMutableComboBoxModel();
770 ((MutableComboBoxModel<E>)dataModel).removeElementAt( anIndex );
771 }
772
773 /**
774 * Removes all items from the item list.
775 */
776 public void removeAllItems() {
777 checkMutableComboBoxModel();
778 MutableComboBoxModel<E> model = (MutableComboBoxModel<E>)dataModel;
779 int size = model.getSize();
780
781 if ( model instanceof DefaultComboBoxModel ) {
782 ((DefaultComboBoxModel)model).removeAllElements();
783 }
784 else {
785 for ( int i = 0; i < size; ++i ) {
786 E element = model.getElementAt( 0 );
787 model.removeElement( element );
788 }
789 }
790 selectedItemReminder = null;
791 if (isEditable()) {
792 editor.setItem(null);
793 }
794 }
795
796 /**
797 * Checks that the {@code dataModel} is an instance of
798 * {@code MutableComboBoxModel}. If not, it throws an exception.
799 * @exception RuntimeException if {@code dataModel} is not an
800 * instance of {@code MutableComboBoxModel}.
801 */
802 void checkMutableComboBoxModel() {
803 if ( !(dataModel instanceof MutableComboBoxModel) )
804 throw new RuntimeException("Cannot use this method with a non-Mutable data model.");
805 }
806
807 /**
808 * Causes the combo box to display its popup window.
809 * @see #setPopupVisible
810 */
811 public void showPopup() {
812 setPopupVisible(true);
813 }
814
815 /**
816 * Causes the combo box to close its popup window.
817 * @see #setPopupVisible
818 */
819 public void hidePopup() {
820 setPopupVisible(false);
824 * Sets the visibility of the popup.
825 *
826 * @param v if {@code true} shows the popup, otherwise, hides the popup.
827 */
828 public void setPopupVisible(boolean v) {
829 getUI().setPopupVisible(this, v);
830 }
831
832 /**
833 * Determines the visibility of the popup.
834 *
835 * @return true if the popup is visible, otherwise returns false
836 */
837 public boolean isPopupVisible() {
838 return getUI().isPopupVisible(this);
839 }
840
841 /** Selection **/
842
843 /**
844 * Adds an {@code ItemListener}.
845 * <p>
846 * {@code aListener} will receive one or two {@code ItemEvent}s when
847 * the selected item changes.
848 *
849 * @param aListener the {@code ItemListener} that is to be notified
850 * @see #setSelectedItem
851 */
852 public void addItemListener(ItemListener aListener) {
853 listenerList.add(ItemListener.class,aListener);
854 }
855
856 /** Removes an {@code ItemListener}.
857 *
858 * @param aListener the {@code ItemListener} 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}s added
866 * to this JComboBox with addItemListener().
867 *
868 * @return all of the {@code ItemListener}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}.
878 * <p>
879 * The {@code ActionListener} will receive an {@code ActionEvent}
880 * when a selection has been made. If the combo box is editable, then
881 * an {@code ActionEvent} will be fired when editing has stopped.
882 *
883 * @param l the {@code ActionListener} 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}.
891 *
892 * @param l the {@code ActionListener} 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}s added
904 * to this JComboBox with addActionListener().
905 *
906 * @return all of the {@code ActionListener}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} 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}.
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} 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}.
932 *
933 * @param l the {@code PopupMenuListener} 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}s added
943 * to this JComboBox with addPopupMenuListener().
944 *
945 * @return all of the {@code PopupMenuListener}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}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);
969 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeVisible(e);
970 }
971 }
972 }
973
974 /**
975 * Notifies {@code PopupMenuListener}s that the popup portion of the
976 * combo box has become invisible.
977 * <p>
978 * This method is public but should not be called by anything other than
979 * the UI delegate.
980 * @see #addPopupMenuListener
981 * @since 1.4
982 */
983 public void firePopupMenuWillBecomeInvisible() {
984 Object[] listeners = listenerList.getListenerList();
985 PopupMenuEvent e=null;
986 for (int i = listeners.length-2; i>=0; i-=2) {
987 if (listeners[i]==PopupMenuListener.class) {
988 if (e == null)
989 e = new PopupMenuEvent(this);
990 ((PopupMenuListener)listeners[i+1]).popupMenuWillBecomeInvisible(e);
991 }
992 }
993 }
994
995 /**
996 * Notifies {@code PopupMenuListener}s that the popup portion of the
997 * combo box has been canceled.
998 * <p>
999 * This method is public but should not be called by anything other than
1000 * the UI delegate.
1001 * @see #addPopupMenuListener
1002 * @since 1.4
1003 */
1004 public void firePopupMenuCanceled() {
1005 Object[] listeners = listenerList.getListenerList();
1006 PopupMenuEvent e=null;
1007 for (int i = listeners.length-2; i>=0; i-=2) {
1008 if (listeners[i]==PopupMenuListener.class) {
1009 if (e == null)
1010 e = new PopupMenuEvent(this);
1011 ((PopupMenuListener)listeners[i+1]).popupMenuCanceled(e);
1012 }
1013 }
1014 }
1015
1016 /**
1024 */
1025 public void setActionCommand(String aCommand) {
1026 actionCommand = aCommand;
1027 }
1028
1029 /**
1030 * Returns the action command that is included in the event sent to
1031 * action listeners.
1032 *
1033 * @return the string containing the "command" that is sent
1034 * to action listeners.
1035 */
1036 public String getActionCommand() {
1037 return actionCommand;
1038 }
1039
1040 private Action action;
1041 private PropertyChangeListener actionPropertyChangeListener;
1042
1043 /**
1044 * Sets the {@code Action} for the {@code ActionEvent} source.
1045 * The new {@code Action} replaces any previously set
1046 * {@code Action} but does not affect {@code ActionListeners}
1047 * independently added with {@code addActionListener}.
1048 * If the {@code Action} is already a registered
1049 * {@code ActionListener} for the {@code ActionEvent} source,
1050 * it is not re-registered.
1051 * <p>
1052 * Setting the {@code Action} results in immediately changing
1053 * all the properties described in <a href="Action.html#buttonActions">
1054 * Swing Components Supporting {@code Action}</a>.
1055 * Subsequently, the combobox's properties are automatically updated
1056 * as the {@code Action}'s properties change.
1057 * <p>
1058 * This method uses three other methods to set
1059 * and help track the {@code Action}'s property values.
1060 * It uses the {@code configurePropertiesFromAction} method
1061 * to immediately change the combobox's properties.
1062 * To track changes in the {@code Action}'s property values,
1063 * this method registers the {@code PropertyChangeListener}
1064 * returned by {@code createActionPropertyChangeListener}. 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} for the {@code JComboBox},
1070 * or {@code null}.
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 }
1097 // Reverse linkage:
1098 actionPropertyChangeListener = createActionPropertyChangeListener(action);
1099 action.addPropertyChangeListener(actionPropertyChangeListener);
1100 }
1101 firePropertyChange("action", oldValue, action);
1102 }
1103 }
1104
1105 private boolean isListener(Class<?> c, ActionListener a) {
1106 boolean isListener = false;
1107 Object[] listeners = listenerList.getListenerList();
1108 for (int i = listeners.length-2; i>=0; i-=2) {
1109 if (listeners[i]==c && listeners[i+1]==a) {
1110 isListener=true;
1111 }
1112 }
1113 return isListener;
1114 }
1115
1116 /**
1117 * Returns the currently set {@code Action} for this
1118 * {@code ActionEvent} source, or {@code null} if no
1119 * {@code Action} is set.
1120 *
1121 * @return the {@code Action} for this {@code ActionEvent}
1122 * source; or {@code null}
1123 * @since 1.3
1124 * @see Action
1125 * @see #setAction
1126 */
1127 public Action getAction() {
1128 return action;
1129 }
1130
1131 /**
1132 * Sets the properties on this combobox to match those in the specified
1133 * {@code Action}. Refer to <a href="Action.html#buttonActions">
1134 * Swing Components Supporting {@code Action}</a> for more
1135 * details as to which properties this sets.
1136 *
1137 * @param a the {@code Action} from which to get the properties,
1138 * or {@code null}
1139 * @since 1.3
1140 * @see Action
1141 * @see #setAction
1142 */
1143 protected void configurePropertiesFromAction(Action a) {
1144 AbstractAction.setEnabledFromAction(this, a);
1145 AbstractAction.setToolTipTextFromAction(this, a);
1146 setActionCommandFromAction(a);
1147 }
1148
1149 /**
1150 * Creates and returns a {@code PropertyChangeListener} that is
1151 * responsible for listening for changes from the specified
1152 * {@code Action} and updating the appropriate properties.
1153 * <p>
1154 * <b>Warning:</b> If you subclass this do not create an anonymous
1155 * inner class. If you do the lifetime of the combobox will be tied to
1156 * that of the {@code Action}.
1157 *
1158 * @param a the combobox's action
1159 * @return the {@code PropertyChangeListener}
1160 * @since 1.3
1161 * @see Action
1162 * @see #setAction
1163 */
1164 protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
1165 return new ComboBoxActionPropertyChangeListener(this, a);
1166 }
1167
1168 /**
1169 * Updates the combobox's state in response to property changes in
1170 * associated action. This method is invoked from the
1171 * {@code PropertyChangeListener} returned from
1172 * {@code createActionPropertyChangeListener}. Subclasses do not normally
1173 * need to invoke this. Subclasses that support additional {@code Action}
1174 * properties should override this and
1175 * {@code configurePropertiesFromAction}.
1176 * <p>
1177 * Refer to the table at <a href="Action.html#buttonActions">
1178 * Swing Components Supporting {@code Action}</a> for a list of
1179 * the properties this method sets.
1180 *
1181 * @param action the {@code Action} associated with this combobox
1182 * @param propertyName the name of the property that changed
1183 * @since 1.6
1184 * @see Action
1185 * @see #configurePropertiesFromAction
1186 */
1187 protected void actionPropertyChanged(Action action, String propertyName) {
1188 if (propertyName == Action.ACTION_COMMAND_KEY) {
1189 setActionCommandFromAction(action);
1190 } else if (propertyName == "enabled") {
1191 AbstractAction.setEnabledFromAction(this, action);
1192 } else if (Action.SHORT_DESCRIPTION == propertyName) {
1193 AbstractAction.setToolTipTextFromAction(this, action);
1194 }
1195 }
1196
1197 private void setActionCommandFromAction(Action a) {
1198 setActionCommand((a != null) ?
1199 (String)a.getValue(Action.ACTION_COMMAND_KEY) :
1200 null);
1201 }
1283 protected void selectedItemChanged() {
1284 if (selectedItemReminder != null ) {
1285 fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
1286 selectedItemReminder,
1287 ItemEvent.DESELECTED));
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}.
1304 *
1305 * @return an array of {@code Objects} 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());
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} 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}s, looking for the Tab key.
1421 * If the Tab key is found, the popup window is closed.
1422 *
1423 * @param e the {@code KeyEvent} containing the keyboard
1424 * key that was pressed
1425 */
1426 public void processKeyEvent(KeyEvent e) {
1427 if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
1428 hidePopup();
1429 }
1430 super.processKeyEvent(e);
1431 }
1432
1433 /**
1434 * {@inheritDoc}
1435 */
1436 @Override
1437 protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
1438 if (super.processKeyBinding(ks, e, condition, pressed)) {
1439 return true;
1440 }
1441
1442 if (!isEditable() || condition != WHEN_FOCUSED || getEditor() == null
1443 || !Boolean.TRUE.equals(getClientProperty("JComboBox.isTableCellEditor"))) {
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} 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}
1490 * is out of range (less than zero or greater than or equal to size)
1491 * it will return {@code null}.
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} if out of range
1497 */
1498 public E getItemAt(int index) {
1499 return dataModel.getElementAt(index);
1500 }
1501
1502 /**
1503 * Returns an instance of the default key-selection manager.
1504 *
1505 * @return the {@code KeySelectionManager} currently used by the list
1506 * @see #setKeySelectionManager
1507 */
1508 protected KeySelectionManager createDefaultKeySelectionManager() {
1509 return new DefaultKeySelectionManager();
1510 }
1511
1512
1513 /**
1514 * The interface that defines a {@code KeySelectionManager}.
1515 * To qualify as a {@code KeySelectionManager},
1516 * the class needs to implement the method
1517 * that identifies the list index given a character and the
1518 * combo box data model.
1519 */
1520 public interface KeySelectionManager {
1521 /** Given {@code aKey} and the model, returns the row
1522 * that should become selected. Return -1 if no match was
1523 * found.
1524 *
1525 * @param aKey a char value, usually indicating a keyboard key that
1526 * was pressed
1527 * @param aModel a ComboBoxModel -- the component's data model, containing
1528 * the list of selectable items
1529 * @return an int equal to the selected row, where 0 is the
1530 * first item and -1 is none.
1531 */
1532 int selectionForKey(char aKey,ComboBoxModel<?> aModel);
1533 }
1534
1535 class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
1536 public int selectionForKey(char aKey,ComboBoxModel<?> aModel) {
1537 int i,c;
1538 int currentSelection = -1;
1539 Object selectedItem = aModel.getSelectedItem();
1540 String v;
1541 String pattern;
1558 v = elem.toString().toLowerCase();
1559 if ( v.length() > 0 && v.charAt(0) == aKey )
1560 return i;
1561 }
1562 }
1563
1564 for ( i = 0 ; i < currentSelection ; i ++ ) {
1565 Object elem = aModel.getElementAt(i);
1566 if (elem != null && elem.toString() != null) {
1567 v = elem.toString().toLowerCase();
1568 if ( v.length() > 0 && v.charAt(0) == aKey )
1569 return i;
1570 }
1571 }
1572 return -1;
1573 }
1574 }
1575
1576
1577 /**
1578 * See {@code readObject} and {@code writeObject} in
1579 * {@code JComponent} for more
1580 * information about serialization in Swing.
1581 */
1582 private void writeObject(ObjectOutputStream s) throws IOException {
1583 s.defaultWriteObject();
1584 if (getUIClassID().equals(uiClassID)) {
1585 byte count = JComponent.getWriteObjCounter(this);
1586 JComponent.setWriteObjCounter(this, --count);
1587 if (count == 0 && ui != null) {
1588 ui.installUI(this);
1589 }
1590 }
1591 }
1592
1593
1594 /**
1595 * Returns a string representation of this {@code JComboBox}.
1596 * This method is intended to be used only for debugging purposes,
1597 * and the content and format of the returned string may vary between
1598 * implementations. The returned string may be empty but may not
1599 * be {@code null}.
1600 *
1601 * @return a string representation of this {@code JComboBox}
1602 */
1603 protected String paramString() {
1604 String selectedItemReminderString = (selectedItemReminder != null ?
1605 selectedItemReminder.toString() :
1606 "");
1607 String isEditableString = (isEditable ? "true" : "false");
1608 String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
1609 "true" : "false");
1610
1611 return super.paramString() +
1612 ",isEditable=" + isEditableString +
1613 ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
1614 ",maximumRowCount=" + maximumRowCount +
1615 ",selectedItemReminder=" + selectedItemReminderString;
1616 }
1617
1618
1619 ///////////////////
1620 // Accessibility support
1621 ///////////////////
1622
1623 /**
1624 * Gets the AccessibleContext associated with this JComboBox.
1625 * For combo boxes, the AccessibleContext takes the form of an
1626 * AccessibleJComboBox.
1627 * A new AccessibleJComboBox instance is created if necessary.
1628 *
1629 * @return an AccessibleJComboBox that serves as the
1630 * AccessibleContext of this JComboBox
1631 */
1632 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} 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} package.
1651 * Please see {@link java.beans.XMLEncoder}.
1652 */
1653 @SuppressWarnings("serial") // Same-version serialization only
1654 protected class AccessibleJComboBox extends AccessibleJComponent
1655 implements AccessibleAction, AccessibleSelection {
1656
1657
1658 private JList<?> popupList; // combo box popup list
1659 private Accessible previousSelectedAccessible = null;
1660
1661 /**
1662 * Returns an AccessibleJComboBox instance
1663 * @since 1.4
1664 */
1665 public AccessibleJComboBox() {
1666 // set the combo box editor's accessible name and description
1667 JComboBox.this.addPropertyChangeListener(new AccessibleJComboBoxPropertyChangeListener());
1668 setEditorNameAndDescription();
1669
1670 // Get the popup list
|