001 /* JComboBox.java -- 002 Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package javax.swing; 040 041 import java.awt.ItemSelectable; 042 import java.awt.event.ActionEvent; 043 import java.awt.event.ActionListener; 044 import java.awt.event.ItemEvent; 045 import java.awt.event.ItemListener; 046 import java.awt.event.KeyEvent; 047 import java.beans.PropertyChangeEvent; 048 import java.beans.PropertyChangeListener; 049 import java.util.Vector; 050 051 import javax.accessibility.Accessible; 052 import javax.accessibility.AccessibleAction; 053 import javax.accessibility.AccessibleContext; 054 import javax.accessibility.AccessibleRole; 055 import javax.accessibility.AccessibleSelection; 056 import javax.swing.event.ListDataEvent; 057 import javax.swing.event.ListDataListener; 058 import javax.swing.event.PopupMenuEvent; 059 import javax.swing.event.PopupMenuListener; 060 import javax.swing.plaf.ComboBoxUI; 061 import javax.swing.plaf.ComponentUI; 062 import javax.swing.plaf.basic.ComboPopup; 063 064 /** 065 * A component that allows a user to select any item in its list and 066 * displays the selected item to the user. JComboBox also can show/hide a 067 * popup menu containing its list of item whenever the mouse is pressed 068 * over it. 069 * 070 * @author Andrew Selkirk 071 * @author Olga Rodimina 072 * @author Robert Schuster 073 */ 074 public class JComboBox extends JComponent implements ItemSelectable, 075 ListDataListener, 076 ActionListener, 077 Accessible 078 { 079 080 private static final long serialVersionUID = 5654585963292734470L; 081 082 /** 083 * Classes implementing this interface are 084 * responsible for matching key characters typed by the user with combo 085 * box's items. 086 */ 087 public static interface KeySelectionManager 088 { 089 int selectionForKey(char aKey, ComboBoxModel aModel); 090 } 091 092 /** 093 * Maximum number of rows that should be visible by default in the 094 * JComboBox's popup 095 */ 096 private static final int DEFAULT_MAXIMUM_ROW_COUNT = 8; 097 098 /** 099 * Data model used by JComboBox to keep track of its list data and currently 100 * selected element in the list. 101 */ 102 protected ComboBoxModel dataModel; 103 104 /** 105 * Renderer renders(paints) every object in the combo box list in its 106 * associated list cell. This ListCellRenderer is used only when this 107 * JComboBox is uneditable. 108 */ 109 protected ListCellRenderer renderer; 110 111 /** 112 * Editor that is responsible for editing an object in a combo box list. 113 */ 114 protected ComboBoxEditor editor; 115 116 /** 117 * Number of rows that will be visible in the JComboBox's popup. 118 */ 119 protected int maximumRowCount; 120 121 /** 122 * This field indicates if textfield of this JComboBox is editable or not. 123 */ 124 protected boolean isEditable; 125 126 /** 127 * This field is reference to the current selection of the combo box. 128 */ 129 protected Object selectedItemReminder; 130 131 /** 132 * keySelectionManager 133 */ 134 protected KeySelectionManager keySelectionManager; 135 136 /** 137 * This actionCommand is used in ActionEvent that is fired to JComboBox's 138 * ActionListeneres. 139 */ 140 protected String actionCommand; 141 142 /** 143 * This property indicates if heavyweight popup or lightweight popup will be 144 * used to diplay JComboBox's elements. 145 */ 146 protected boolean lightWeightPopupEnabled; 147 148 /** 149 * The action taken when new item is selected in the JComboBox 150 */ 151 private Action action; 152 153 /** 154 * since 1.4 If this field is set then comboBox's display area for the 155 * selected item will be set by default to this value. 156 */ 157 private Object prototypeDisplayValue; 158 159 /** 160 * Constructs JComboBox object with specified data model for it. 161 * <p>Note that the JComboBox will not change the value that 162 * is preselected by your ComboBoxModel implementation.</p> 163 * 164 * @param model Data model that will be used by this JComboBox to keep track 165 * of its list of items. 166 */ 167 public JComboBox(ComboBoxModel model) 168 { 169 setEditable(false); 170 setEnabled(true); 171 setMaximumRowCount(DEFAULT_MAXIMUM_ROW_COUNT); 172 setModel(model); 173 setActionCommand("comboBoxChanged"); 174 175 lightWeightPopupEnabled = true; 176 isEditable = false; 177 178 updateUI(); 179 } 180 181 /** 182 * Constructs JComboBox with specified list of items. 183 * 184 * @param itemArray array containing list of items for this JComboBox 185 */ 186 public JComboBox(Object[] itemArray) 187 { 188 this(new DefaultComboBoxModel(itemArray)); 189 190 if (itemArray.length > 0) 191 setSelectedIndex(0); 192 } 193 194 /** 195 * Constructs JComboBox object with specified list of items. 196 * 197 * @param itemVector vector containing list of items for this JComboBox. 198 */ 199 public JComboBox(Vector<?> itemVector) 200 { 201 this(new DefaultComboBoxModel(itemVector)); 202 203 if (itemVector.size() > 0) 204 setSelectedIndex(0); 205 } 206 207 /** 208 * Constructor. Creates new empty JComboBox. ComboBox's data model is set to 209 * DefaultComboBoxModel. 210 */ 211 public JComboBox() 212 { 213 this(new DefaultComboBoxModel()); 214 } 215 216 /** 217 * This method returns true JComboBox is editable and false otherwise 218 * 219 * @return boolean true if JComboBox is editable and false otherwise 220 */ 221 public boolean isEditable() 222 { 223 return isEditable; 224 } 225 226 /* 227 * This method adds ancestor listener to this JComboBox. 228 */ 229 protected void installAncestorListener() 230 { 231 /* FIXME: Need to implement. 232 * 233 * Need to add ancestor listener to this JComboBox. This listener 234 * should close combo box's popup list of items whenever it 235 * receives an AncestorEvent. 236 */ 237 } 238 239 /** 240 * Set the "UI" property of the combo box, which is a look and feel class 241 * responsible for handling comboBox's input events and painting it. 242 * 243 * @param ui The new "UI" property 244 */ 245 public void setUI(ComboBoxUI ui) 246 { 247 super.setUI(ui); 248 } 249 250 /** 251 * This method sets this comboBox's UI to the UIManager's default for the 252 * current look and feel. 253 */ 254 public void updateUI() 255 { 256 setUI((ComboBoxUI) UIManager.getUI(this)); 257 } 258 259 /** 260 * This method returns the String identifier for the UI class to the used 261 * with the JComboBox. 262 * 263 * @return The String identifier for the UI class. 264 */ 265 public String getUIClassID() 266 { 267 return "ComboBoxUI"; 268 } 269 270 /** 271 * This method returns the UI used to display the JComboBox. 272 * 273 * @return The UI used to display the JComboBox. 274 */ 275 public ComboBoxUI getUI() 276 { 277 return (ComboBoxUI) ui; 278 } 279 280 /** 281 * Set the data model for this JComboBox. This un-registers all listeners 282 * associated with the current model, and re-registers them with the new 283 * model. 284 * 285 * @param newDataModel The new data model for this JComboBox 286 */ 287 public void setModel(ComboBoxModel newDataModel) 288 { 289 // dataModel is null if it this method is called from inside the constructors. 290 if (dataModel != null) 291 { 292 // Prevents unneccessary updates. 293 if (dataModel == newDataModel) 294 return; 295 296 // Removes itself (as DataListener) from the to-be-replaced model. 297 dataModel.removeListDataListener(this); 298 } 299 300 /* Adds itself as a DataListener to the new model. 301 * It is intentioned that this operation will fail with a NullPointerException if the 302 * caller delivered a null argument. 303 */ 304 newDataModel.addListDataListener(this); 305 306 // Stores old data model for event notification. 307 ComboBoxModel oldDataModel = dataModel; 308 dataModel = newDataModel; 309 selectedItemReminder = newDataModel.getSelectedItem(); 310 311 // Notifies the listeners of the model change. 312 firePropertyChange("model", oldDataModel, dataModel); 313 } 314 315 /** 316 * This method returns data model for this comboBox. 317 * 318 * @return ComboBoxModel containing items for this combo box. 319 */ 320 public ComboBoxModel getModel() 321 { 322 return dataModel; 323 } 324 325 /** 326 * This method sets JComboBox's popup to be either lightweight or 327 * heavyweight. If 'enabled' is true then lightweight popup is used and 328 * heavyweight otherwise. By default lightweight popup is used to display 329 * this JComboBox's elements. 330 * 331 * @param enabled indicates if lightweight popup or heavyweight popup should 332 * be used to display JComboBox's elements. 333 */ 334 public void setLightWeightPopupEnabled(boolean enabled) 335 { 336 lightWeightPopupEnabled = enabled; 337 } 338 339 /** 340 * This method returns whether popup menu that is used to display list of 341 * combo box's item is lightWeight or not. 342 * 343 * @return boolean true if popup menu is lightweight and false otherwise. 344 */ 345 public boolean isLightWeightPopupEnabled() 346 { 347 return lightWeightPopupEnabled; 348 } 349 350 /** 351 * This method sets editability of the combo box. If combo box is editable 352 * the user can choose component from the combo box list by typing 353 * component's name in the editor(JTextfield by default). Otherwise if not 354 * editable, the user should use the list to choose the component. This 355 * method fires PropertyChangeEvents to JComboBox's registered 356 * PropertyChangeListeners to indicate that 'editable' property of the 357 * JComboBox has changed. 358 * 359 * @param editable indicates if the JComboBox's textfield should be editable 360 * or not. 361 */ 362 public void setEditable(boolean editable) 363 { 364 if (isEditable != editable) 365 { 366 isEditable = editable; 367 firePropertyChange("editable", !isEditable, isEditable); 368 } 369 } 370 371 /** 372 * Sets number of rows that should be visible in this JComboBox's popup. If 373 * this JComboBox's popup has more elements that maximum number or rows 374 * then popup will have a scroll pane to allow users to view other 375 * elements. 376 * 377 * @param rowCount number of rows that will be visible in JComboBox's popup. 378 */ 379 public void setMaximumRowCount(int rowCount) 380 { 381 if (maximumRowCount != rowCount) 382 { 383 int oldMaximumRowCount = maximumRowCount; 384 maximumRowCount = rowCount; 385 firePropertyChange("maximumRowCount", oldMaximumRowCount, 386 maximumRowCount); 387 } 388 } 389 390 /** 391 * This method returns number of rows visible in the JComboBox's list of 392 * items. 393 * 394 * @return int maximun number of visible rows in the JComboBox's list. 395 */ 396 public int getMaximumRowCount() 397 { 398 return maximumRowCount; 399 } 400 401 /** 402 * This method sets cell renderer for this JComboBox that will be used to 403 * paint combo box's items. The Renderer should only be used only when 404 * JComboBox is not editable. In the case when JComboBox is editable the 405 * editor must be used. This method also fires PropertyChangeEvent when 406 * cellRendered for this JComboBox has changed. 407 * 408 * @param aRenderer cell renderer that will be used by this JComboBox to 409 * paint its elements. 410 */ 411 public void setRenderer(ListCellRenderer aRenderer) 412 { 413 if (renderer != aRenderer) 414 { 415 ListCellRenderer oldRenderer = renderer; 416 renderer = aRenderer; 417 firePropertyChange("renderer", oldRenderer, renderer); 418 } 419 } 420 421 /** 422 * This method returns renderer responsible for rendering selected item in 423 * the combo box 424 * 425 * @return ListCellRenderer 426 */ 427 public ListCellRenderer getRenderer() 428 { 429 return renderer; 430 } 431 432 /** 433 * Sets editor for this JComboBox 434 * 435 * @param newEditor ComboBoxEditor for this JComboBox. This method fires 436 * PropertyChangeEvent when 'editor' property is changed. 437 */ 438 public void setEditor(ComboBoxEditor newEditor) 439 { 440 if (editor == newEditor) 441 return; 442 443 if (editor != null) 444 editor.removeActionListener(this); 445 446 ComboBoxEditor oldEditor = editor; 447 editor = newEditor; 448 449 if (editor != null) 450 editor.addActionListener(this); 451 452 firePropertyChange("editor", oldEditor, editor); 453 } 454 455 /** 456 * Returns editor component that is responsible for displaying/editing 457 * selected item in the combo box. 458 * 459 * @return ComboBoxEditor 460 */ 461 public ComboBoxEditor getEditor() 462 { 463 return editor; 464 } 465 466 /** 467 * Forces combo box to select given item 468 * 469 * @param item element in the combo box to select. 470 */ 471 public void setSelectedItem(Object item) 472 { 473 dataModel.setSelectedItem(item); 474 fireActionEvent(); 475 } 476 477 /** 478 * Returns currently selected item in the combo box. 479 * The result may be <code>null</code> to indicate that nothing is 480 * currently selected. 481 * 482 * @return element that is currently selected in this combo box. 483 */ 484 public Object getSelectedItem() 485 { 486 return dataModel.getSelectedItem(); 487 } 488 489 /** 490 * Forces JComboBox to select component located in the given index in the 491 * combo box. 492 * <p>If the index is below -1 or exceeds the upper bound an 493 * <code>IllegalArgumentException</code> is thrown.<p/> 494 * <p>If the index is -1 then no item gets selected.</p> 495 * 496 * @param index index specifying location of the component that should be 497 * selected. 498 */ 499 public void setSelectedIndex(int index) 500 { 501 if (index < -1 || index >= dataModel.getSize()) 502 // Fails because index is out of bounds. 503 throw new IllegalArgumentException("illegal index: " + index); 504 else 505 // Selects the item at the given index or clears the selection if the 506 // index value is -1. 507 setSelectedItem((index == -1) ? null : dataModel.getElementAt(index)); 508 } 509 510 /** 511 * Returns index of the item that is currently selected in the combo box. If 512 * no item is currently selected, then -1 is returned. 513 * <p> 514 * Note: For performance reasons you should minimize invocation of this 515 * method. If the data model is not an instance of 516 * <code>DefaultComboBoxModel</code> the complexity is O(n) where n is the 517 * number of elements in the combo box. 518 * </p> 519 * 520 * @return int Index specifying location of the currently selected item in the 521 * combo box or -1 if nothing is selected in the combo box. 522 */ 523 public int getSelectedIndex() 524 { 525 Object selectedItem = getSelectedItem(); 526 527 if (selectedItem != null) 528 { 529 if (dataModel instanceof DefaultComboBoxModel) 530 // Uses special method of DefaultComboBoxModel to retrieve the index. 531 return ((DefaultComboBoxModel) dataModel).getIndexOf(selectedItem); 532 else 533 { 534 // Iterates over all items to retrieve the index. 535 int size = dataModel.getSize(); 536 537 for (int i = 0; i < size; i++) 538 { 539 Object o = dataModel.getElementAt(i); 540 541 // XXX: Is special handling of ComparableS neccessary? 542 if ((selectedItem != null) ? selectedItem.equals(o) : o == null) 543 return i; 544 } 545 } 546 } 547 548 // returns that no item is currently selected 549 return -1; 550 } 551 552 /** 553 * Returns an object that is used as the display value when calculating the 554 * preferred size for the combo box. This value is, of course, never 555 * displayed anywhere. 556 * 557 * @return The prototype display value (possibly <code>null</code>). 558 * 559 * @since 1.4 560 * @see #setPrototypeDisplayValue(Object) 561 */ 562 public Object getPrototypeDisplayValue() 563 { 564 return prototypeDisplayValue; 565 } 566 567 /** 568 * Sets the object that is assumed to be the displayed item when calculating 569 * the preferred size for the combo box. A {@link PropertyChangeEvent} (with 570 * the name <code>prototypeDisplayValue</code>) is sent to all registered 571 * listeners. 572 * 573 * @param value the new value (<code>null</code> permitted). 574 * 575 * @since 1.4 576 * @see #getPrototypeDisplayValue() 577 */ 578 public void setPrototypeDisplayValue(Object value) 579 { 580 Object oldValue = prototypeDisplayValue; 581 prototypeDisplayValue = value; 582 firePropertyChange("prototypeDisplayValue", oldValue, value); 583 } 584 585 /** 586 * This method adds given element to this JComboBox. 587 * <p>A <code>RuntimeException</code> is thrown if the data model is not 588 * an instance of {@link MutableComboBoxModel}.</p> 589 * 590 * @param element element to add 591 */ 592 public void addItem(Object element) 593 { 594 if (dataModel instanceof MutableComboBoxModel) 595 ((MutableComboBoxModel) dataModel).addElement(element); 596 else 597 throw new RuntimeException("Unable to add the item because the data " 598 + "model it is not an instance of " 599 + "MutableComboBoxModel."); 600 } 601 602 /** 603 * Inserts given element at the specified index to this JComboBox. 604 * <p>A <code>RuntimeException</code> is thrown if the data model is not 605 * an instance of {@link MutableComboBoxModel}.</p> 606 * 607 * @param element element to insert 608 * @param index position where to insert the element 609 */ 610 public void insertItemAt(Object element, int index) 611 { 612 if (dataModel instanceof MutableComboBoxModel) 613 ((MutableComboBoxModel) dataModel).insertElementAt(element, index); 614 else 615 throw new RuntimeException("Unable to insert the item because the data " 616 + "model it is not an instance of " 617 + "MutableComboBoxModel."); 618 } 619 620 /** 621 * This method removes given element from this JComboBox. 622 * <p>A <code>RuntimeException</code> is thrown if the data model is not 623 * an instance of {@link MutableComboBoxModel}.</p> 624 * 625 * @param element element to remove 626 */ 627 public void removeItem(Object element) 628 { 629 if (dataModel instanceof MutableComboBoxModel) 630 ((MutableComboBoxModel) dataModel).removeElement(element); 631 else 632 throw new RuntimeException("Unable to remove the item because the data " 633 + "model it is not an instance of " 634 + "MutableComboBoxModel."); 635 } 636 637 /** 638 * This method remove element location in the specified index in the 639 * JComboBox. 640 * <p>A <code>RuntimeException</code> is thrown if the data model is not 641 * an instance of {@link MutableComboBoxModel}.</p> 642 * 643 * @param index index specifying position of the element to remove 644 */ 645 public void removeItemAt(int index) 646 { 647 if (dataModel instanceof MutableComboBoxModel) 648 ((MutableComboBoxModel) dataModel).removeElementAt(index); 649 else 650 throw new RuntimeException("Unable to remove the item because the data " 651 + "model it is not an instance of " 652 + "MutableComboBoxModel."); 653 } 654 655 /** 656 * This method removes all elements from this JComboBox. 657 * <p> 658 * A <code>RuntimeException</code> is thrown if the data model is not an 659 * instance of {@link MutableComboBoxModel}. 660 * </p> 661 */ 662 public void removeAllItems() 663 { 664 if (dataModel instanceof DefaultComboBoxModel) 665 // Uses special method if we have a DefaultComboBoxModel. 666 ((DefaultComboBoxModel) dataModel).removeAllElements(); 667 else if (dataModel instanceof MutableComboBoxModel) 668 { 669 // Iterates over all items and removes each. 670 MutableComboBoxModel mcbm = (MutableComboBoxModel) dataModel; 671 672 // We intentionally remove the items backwards to support models which 673 // shift their content to the beginning (e.g. linked lists) 674 for (int i = mcbm.getSize() - 1; i >= 0; i--) 675 mcbm.removeElementAt(i); 676 } 677 else 678 throw new RuntimeException("Unable to remove the items because the data " 679 + "model it is not an instance of " 680 + "MutableComboBoxModel."); 681 } 682 683 /** 684 * This method displays popup with list of combo box's items on the screen 685 */ 686 public void showPopup() 687 { 688 setPopupVisible(true); 689 } 690 691 /** 692 * This method hides popup containing list of combo box's items 693 */ 694 public void hidePopup() 695 { 696 setPopupVisible(false); 697 } 698 699 /** 700 * This method either displayes or hides the popup containing list of combo 701 * box's items. 702 * 703 * @param visible show popup if 'visible' is true and hide it otherwise 704 */ 705 public void setPopupVisible(boolean visible) 706 { 707 getUI().setPopupVisible(this, visible); 708 } 709 710 /** 711 * Checks if popup is currently visible on the screen. 712 * 713 * @return boolean true if popup is visible and false otherwise 714 */ 715 public boolean isPopupVisible() 716 { 717 return getUI().isPopupVisible(this); 718 } 719 720 /** 721 * This method sets actionCommand to the specified string. ActionEvent fired 722 * to this JComboBox registered ActionListeners will contain this 723 * actionCommand. 724 * 725 * @param aCommand new action command for the JComboBox's ActionEvent 726 */ 727 public void setActionCommand(String aCommand) 728 { 729 actionCommand = aCommand; 730 } 731 732 /** 733 * Returns actionCommand associated with the ActionEvent fired by the 734 * JComboBox to its registered ActionListeners. 735 * 736 * @return String actionCommand for the ActionEvent 737 */ 738 public String getActionCommand() 739 { 740 return actionCommand; 741 } 742 743 /** 744 * setAction 745 * 746 * @param a action to set 747 */ 748 public void setAction(Action a) 749 { 750 Action old = action; 751 action = a; 752 configurePropertiesFromAction(action); 753 if (action != null) 754 // FIXME: remove from old action and add to new action 755 // PropertyChangeListener to listen to changes in the action 756 addActionListener(action); 757 } 758 759 /** 760 * This method returns Action that is invoked when selected item is changed 761 * in the JComboBox. 762 * 763 * @return Action 764 */ 765 public Action getAction() 766 { 767 return action; 768 } 769 770 /** 771 * Configure properties of the JComboBox by reading properties of specified 772 * action. This method always sets the comboBox's "enabled" property to the 773 * value of the Action's "enabled" property. 774 * 775 * @param a An Action to configure the combo box from 776 */ 777 protected void configurePropertiesFromAction(Action a) 778 { 779 if (a == null) 780 { 781 setEnabled(true); 782 setToolTipText(null); 783 } 784 else 785 { 786 setEnabled(a.isEnabled()); 787 setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION))); 788 } 789 } 790 791 /** 792 * Creates PropertyChangeListener to listen for the changes in comboBox's 793 * action properties. 794 * 795 * @param action action to listen to for property changes 796 * 797 * @return a PropertyChangeListener that listens to changes in 798 * action properties. 799 */ 800 protected PropertyChangeListener createActionPropertyChangeListener(Action action) 801 { 802 return new PropertyChangeListener() 803 { 804 public void propertyChange(PropertyChangeEvent e) 805 { 806 Action act = (Action) (e.getSource()); 807 configurePropertiesFromAction(act); 808 } 809 }; 810 } 811 812 /** 813 * This method fires ItemEvent to this JComboBox's registered ItemListeners. 814 * This method is invoked when currently selected item in this combo box 815 * has changed. 816 * 817 * @param e the ItemEvent describing the change in the combo box's 818 * selection. 819 */ 820 protected void fireItemStateChanged(ItemEvent e) 821 { 822 ItemListener[] ll = getItemListeners(); 823 824 for (int i = 0; i < ll.length; i++) 825 ll[i].itemStateChanged(e); 826 } 827 828 /** 829 * This method fires ActionEvent to this JComboBox's registered 830 * ActionListeners. This method is invoked when user explicitly changes 831 * currently selected item. 832 */ 833 protected void fireActionEvent() 834 { 835 ActionListener[] ll = getActionListeners(); 836 837 for (int i = 0; i < ll.length; i++) 838 ll[i].actionPerformed(new ActionEvent(this, 839 ActionEvent.ACTION_PERFORMED, 840 actionCommand)); 841 } 842 843 /** 844 * Fires a popupMenuCanceled() event to all <code>PopupMenuListeners</code>. 845 * 846 * Note: This method is intended for use by plaf classes only. 847 */ 848 public void firePopupMenuCanceled() 849 { 850 PopupMenuListener[] listeners = getPopupMenuListeners(); 851 PopupMenuEvent e = new PopupMenuEvent(this); 852 for (int i = 0; i < listeners.length; i++) 853 listeners[i].popupMenuCanceled(e); 854 } 855 856 /** 857 * Fires a popupMenuWillBecomeInvisible() event to all 858 * <code>PopupMenuListeners</code>. 859 * 860 * Note: This method is intended for use by plaf classes only. 861 */ 862 public void firePopupMenuWillBecomeInvisible() 863 { 864 PopupMenuListener[] listeners = getPopupMenuListeners(); 865 PopupMenuEvent e = new PopupMenuEvent(this); 866 for (int i = 0; i < listeners.length; i++) 867 listeners[i].popupMenuWillBecomeInvisible(e); 868 } 869 870 /** 871 * Fires a popupMenuWillBecomeVisible() event to all 872 * <code>PopupMenuListeners</code>. 873 * 874 * Note: This method is intended for use by plaf classes only. 875 */ 876 public void firePopupMenuWillBecomeVisible() 877 { 878 PopupMenuListener[] listeners = getPopupMenuListeners(); 879 PopupMenuEvent e = new PopupMenuEvent(this); 880 for (int i = 0; i < listeners.length; i++) 881 listeners[i].popupMenuWillBecomeVisible(e); 882 } 883 884 /** 885 * This method is invoked whenever selected item changes in the combo box's 886 * data model. It fires ItemEvent and ActionEvent to all registered 887 * ComboBox's ItemListeners and ActionListeners respectively, indicating 888 * the change. 889 */ 890 protected void selectedItemChanged() 891 { 892 // Fire ItemEvent to indicated that previously selected item is now 893 // deselected 894 if (selectedItemReminder != null) 895 fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, 896 selectedItemReminder, 897 ItemEvent.DESELECTED)); 898 899 // Fire ItemEvent to indicate that new item is selected 900 Object newSelection = getSelectedItem(); 901 if (newSelection != null) 902 fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, 903 newSelection, ItemEvent.SELECTED)); 904 905 // Fire Action Event to JComboBox's registered listeners 906 fireActionEvent(); 907 908 selectedItemReminder = newSelection; 909 } 910 911 /** 912 * Returns Object array of size 1 containing currently selected element in 913 * the JComboBox. 914 * 915 * @return Object[] Object array of size 1 containing currently selected 916 * element in the JComboBox. 917 */ 918 public Object[] getSelectedObjects() 919 { 920 return new Object[] { getSelectedItem() }; 921 } 922 923 /** 924 * This method handles actionEvents fired by the ComboBoxEditor. It changes 925 * this JComboBox's selection to the new value currently in the editor and 926 * hides list of combo box items. 927 * 928 * @param e the ActionEvent 929 */ 930 public void actionPerformed(ActionEvent e) 931 { 932 setSelectedItem(getEditor().getItem()); 933 setPopupVisible(false); 934 } 935 936 /** 937 * This method selects item in this combo box that matches specified 938 * specified keyChar and returns true if such item is found. Otherwise 939 * false is returned. 940 * 941 * @param keyChar character indicating which item in the combo box should be 942 * selected. 943 * 944 * @return boolean true if item corresponding to the specified keyChar 945 * exists in the combo box. Otherwise false is returned. 946 */ 947 public boolean selectWithKeyChar(char keyChar) 948 { 949 if (keySelectionManager == null) 950 { 951 keySelectionManager = createDefaultKeySelectionManager(); 952 } 953 954 int index = keySelectionManager.selectionForKey(keyChar, getModel()); 955 boolean retVal = false; 956 if (index >= 0) 957 { 958 setSelectedIndex(index); 959 retVal = true; 960 } 961 return retVal; 962 } 963 964 /** 965 * The part of implementation of ListDataListener interface. This method is 966 * invoked when some items where added to the JComboBox's data model. 967 * 968 * @param event ListDataEvent describing the change 969 */ 970 public void intervalAdded(ListDataEvent event) 971 { 972 // FIXME: Need to implement 973 repaint(); 974 } 975 976 /** 977 * The part of implementation of ListDataListener interface. This method is 978 * invoked when some items where removed from the JComboBox's data model. 979 * 980 * @param event ListDataEvent describing the change. 981 */ 982 public void intervalRemoved(ListDataEvent event) 983 { 984 // FIXME: Need to implement 985 repaint(); 986 } 987 988 /** 989 * The part of implementation of ListDataListener interface. This method is 990 * invoked when contents of the JComboBox's data model changed. 991 * 992 * @param event ListDataEvent describing the change 993 */ 994 public void contentsChanged(ListDataEvent event) 995 { 996 // if first and last index of the given ListDataEvent are both -1, 997 // then it indicates that selected item in the combo box data model 998 // have changed. 999 if (event.getIndex0() == -1 && event.getIndex1() == -1) 1000 selectedItemChanged(); 1001 } 1002 1003 /** 1004 * This method disables or enables JComboBox. If the JComboBox is enabled, 1005 * then user is able to make item choice, otherwise if JComboBox is 1006 * disabled then user is not able to make a selection. 1007 * 1008 * @param enabled if 'enabled' is true then enable JComboBox and disable it 1009 */ 1010 public void setEnabled(boolean enabled) 1011 { 1012 boolean oldEnabled = super.isEnabled(); 1013 if (enabled != oldEnabled) 1014 { 1015 super.setEnabled(enabled); 1016 firePropertyChange("enabled", oldEnabled, enabled); 1017 } 1018 } 1019 1020 /** 1021 * This method initializes specified ComboBoxEditor to display given item. 1022 * 1023 * @param anEditor ComboBoxEditor to initialize 1024 * @param anItem Item that should displayed in the specified editor 1025 */ 1026 public void configureEditor(ComboBoxEditor anEditor, Object anItem) 1027 { 1028 anEditor.setItem(anItem); 1029 } 1030 1031 /** 1032 * This method is fired whenever a key is pressed with the combo box 1033 * in focus 1034 * 1035 * @param e The KeyEvent indicating which key was pressed. 1036 */ 1037 public void processKeyEvent(KeyEvent e) 1038 { 1039 if (e.getKeyCode() == KeyEvent.VK_TAB) 1040 setPopupVisible(false); 1041 else 1042 super.processKeyEvent(e); 1043 } 1044 1045 /** 1046 * setKeySelectionManager 1047 * 1048 * @param aManager 1049 */ 1050 public void setKeySelectionManager(KeySelectionManager aManager) 1051 { 1052 keySelectionManager = aManager; 1053 } 1054 1055 /** 1056 * getKeySelectionManager 1057 * 1058 * @return JComboBox.KeySelectionManager 1059 */ 1060 public KeySelectionManager getKeySelectionManager() 1061 { 1062 return keySelectionManager; 1063 } 1064 1065 /** 1066 * This method returns number of elements in this JComboBox 1067 * 1068 * @return int number of elements in this JComboBox 1069 */ 1070 public int getItemCount() 1071 { 1072 return dataModel.getSize(); 1073 } 1074 1075 /** 1076 * Returns elements located in the combo box at the given index. 1077 * 1078 * @param index index specifying location of the component to return. 1079 * 1080 * @return component in the combo box that is located in the given index. 1081 */ 1082 public Object getItemAt(int index) 1083 { 1084 return dataModel.getElementAt(index); 1085 } 1086 1087 /** 1088 * createDefaultKeySelectionManager 1089 * 1090 * @return KeySelectionManager 1091 */ 1092 protected KeySelectionManager createDefaultKeySelectionManager() 1093 { 1094 return new DefaultKeySelectionManager(); 1095 } 1096 1097 /** 1098 * Returns an implementation-dependent string describing the attributes of 1099 * this <code>JComboBox</code>. 1100 * 1101 * @return A string describing the attributes of this <code>JComboBox</code> 1102 * (never <code>null</code>). 1103 */ 1104 protected String paramString() 1105 { 1106 String superParamStr = super.paramString(); 1107 StringBuffer sb = new StringBuffer(); 1108 sb.append(",isEditable=").append(isEditable()); 1109 sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled()); 1110 sb.append(",maximumRowCount=").append(getMaximumRowCount()); 1111 1112 sb.append(",selectedItemReminder="); 1113 if (selectedItemReminder != null) 1114 sb.append(selectedItemReminder); 1115 return superParamStr + sb.toString(); 1116 } 1117 1118 /** 1119 * Returns the object that provides accessibility features for this 1120 * <code>JComboBox</code> component. 1121 * 1122 * @return The accessible context (an instance of 1123 * {@link AccessibleJComboBox}). 1124 */ 1125 public AccessibleContext getAccessibleContext() 1126 { 1127 if (accessibleContext == null) 1128 accessibleContext = new AccessibleJComboBox(); 1129 1130 return accessibleContext; 1131 } 1132 1133 /** 1134 * This methods adds specified ActionListener to this JComboBox. 1135 * 1136 * @param listener to add 1137 */ 1138 public void addActionListener(ActionListener listener) 1139 { 1140 listenerList.add(ActionListener.class, listener); 1141 } 1142 1143 /** 1144 * This method removes specified ActionListener from this JComboBox. 1145 * 1146 * @param listener ActionListener 1147 */ 1148 public void removeActionListener(ActionListener listener) 1149 { 1150 listenerList.remove(ActionListener.class, listener); 1151 } 1152 1153 /** 1154 * This method returns array of ActionListeners that are registered with 1155 * this JComboBox. 1156 * 1157 * @since 1.4 1158 */ 1159 public ActionListener[] getActionListeners() 1160 { 1161 return (ActionListener[]) getListeners(ActionListener.class); 1162 } 1163 1164 /** 1165 * This method registers given ItemListener with this JComboBox 1166 * 1167 * @param listener to remove 1168 */ 1169 public void addItemListener(ItemListener listener) 1170 { 1171 listenerList.add(ItemListener.class, listener); 1172 } 1173 1174 /** 1175 * This method unregisters given ItemListener from this JComboBox 1176 * 1177 * @param listener to remove 1178 */ 1179 public void removeItemListener(ItemListener listener) 1180 { 1181 listenerList.remove(ItemListener.class, listener); 1182 } 1183 1184 /** 1185 * This method returns array of ItemListeners that are registered with this 1186 * JComboBox. 1187 * 1188 * @since 1.4 1189 */ 1190 public ItemListener[] getItemListeners() 1191 { 1192 return (ItemListener[]) getListeners(ItemListener.class); 1193 } 1194 1195 /** 1196 * Adds PopupMenuListener to combo box to listen to the events fired by the 1197 * combo box's popup menu containing its list of items 1198 * 1199 * @param listener to add 1200 */ 1201 public void addPopupMenuListener(PopupMenuListener listener) 1202 { 1203 listenerList.add(PopupMenuListener.class, listener); 1204 } 1205 1206 /** 1207 * Removes PopupMenuListener to combo box to listen to the events fired by 1208 * the combo box's popup menu containing its list of items 1209 * 1210 * @param listener to add 1211 */ 1212 public void removePopupMenuListener(PopupMenuListener listener) 1213 { 1214 listenerList.remove(PopupMenuListener.class, listener); 1215 } 1216 1217 /** 1218 * Returns array of PopupMenuListeners that are registered with combo box. 1219 */ 1220 public PopupMenuListener[] getPopupMenuListeners() 1221 { 1222 return (PopupMenuListener[]) getListeners(PopupMenuListener.class); 1223 } 1224 1225 /** 1226 * Accessibility support for <code>JComboBox</code>. 1227 */ 1228 protected class AccessibleJComboBox extends AccessibleJComponent 1229 implements AccessibleAction, AccessibleSelection 1230 { 1231 private static final long serialVersionUID = 8217828307256675666L; 1232 1233 /** 1234 * @specnote This constructor was protected in 1.4, but made public 1235 * in 1.5. 1236 */ 1237 public AccessibleJComboBox() 1238 { 1239 // Nothing to do here. 1240 } 1241 1242 /** 1243 * Returns the number of accessible children of this object. The 1244 * implementation of AccessibleJComboBox delegates this call to the UI 1245 * of the associated JComboBox. 1246 * 1247 * @return the number of accessible children of this object 1248 * 1249 * @see ComponentUI#getAccessibleChildrenCount(JComponent) 1250 */ 1251 public int getAccessibleChildrenCount() 1252 { 1253 ComponentUI ui = getUI(); 1254 int count; 1255 if (ui != null) 1256 count = ui.getAccessibleChildrenCount(JComboBox.this); 1257 else 1258 count = super.getAccessibleChildrenCount(); 1259 return count; 1260 } 1261 1262 /** 1263 * Returns the number of accessible children of this object. The 1264 * implementation of AccessibleJComboBox delegates this call to the UI 1265 * of the associated JComboBox. 1266 * 1267 * @param index the index of the accessible child to fetch 1268 * 1269 * @return the number of accessible children of this object 1270 * 1271 * @see ComponentUI#getAccessibleChild(JComponent, int) 1272 */ 1273 public Accessible getAccessibleChild(int index) 1274 { 1275 ComponentUI ui = getUI(); 1276 Accessible child = null; 1277 if (ui != null) 1278 child = ui.getAccessibleChild(JComboBox.this, index); 1279 else 1280 child = super.getAccessibleChild(index); 1281 return child; 1282 } 1283 1284 /** 1285 * Returns the AccessibleSelection object associated with this object. 1286 * AccessibleJComboBoxes handle their selection themselves, so this 1287 * always returns <code>this</code>. 1288 * 1289 * @return the AccessibleSelection object associated with this object 1290 */ 1291 public AccessibleSelection getAccessibleSelection() 1292 { 1293 return this; 1294 } 1295 1296 /** 1297 * Returns the accessible selection from this AccssibleJComboBox. 1298 * 1299 * @param index the index of the selected child to fetch 1300 * 1301 * @return the accessible selection from this AccssibleJComboBox 1302 */ 1303 public Accessible getAccessibleSelection(int index) 1304 { 1305 // Get hold of the actual popup. 1306 Accessible popup = getUI().getAccessibleChild(JComboBox.this, 0); 1307 Accessible selected = null; 1308 if (popup != null && popup instanceof ComboPopup) 1309 { 1310 ComboPopup cPopup = (ComboPopup) popup; 1311 // Query the list for the currently selected child. 1312 JList l = cPopup.getList(); 1313 AccessibleContext listCtx = l.getAccessibleContext(); 1314 if (listCtx != null) 1315 { 1316 AccessibleSelection s = listCtx.getAccessibleSelection(); 1317 if (s != null) 1318 { 1319 selected = s.getAccessibleSelection(index); 1320 } 1321 } 1322 } 1323 return selected; 1324 } 1325 1326 /** 1327 * Returns <code>true</code> if the accessible child with the specified 1328 * <code>index</code> is selected, <code>false</code> otherwise. 1329 * 1330 * @param index the index of the accessible child 1331 * 1332 * @return <code>true</code> if the accessible child with the specified 1333 * <code>index</code> is selected, <code>false</code> otherwise 1334 */ 1335 public boolean isAccessibleChildSelected(int index) 1336 { 1337 return getSelectedIndex() == index; 1338 } 1339 1340 /** 1341 * Returns the accessible role for the <code>JComboBox</code> component. 1342 * 1343 * @return {@link AccessibleRole#COMBO_BOX}. 1344 */ 1345 public AccessibleRole getAccessibleRole() 1346 { 1347 return AccessibleRole.COMBO_BOX; 1348 } 1349 1350 /** 1351 * Returns the accessible action associated to this accessible object. 1352 * AccessibleJComboBox implements its own AccessibleAction, so this 1353 * method returns <code>this</code>. 1354 * 1355 * @return the accessible action associated to this accessible object 1356 */ 1357 public AccessibleAction getAccessibleAction() 1358 { 1359 return this; 1360 } 1361 1362 /** 1363 * Returns the description of the specified action. AccessibleJComboBox 1364 * implements 1 action (toggle the popup menu) and thus returns 1365 * <code>UIManager.getString("ComboBox.togglePopupText")</code> 1366 * 1367 * @param actionIndex the index of the action for which to return the 1368 * description 1369 * 1370 * @return the description of the specified action 1371 */ 1372 public String getAccessibleActionDescription(int actionIndex) 1373 { 1374 return UIManager.getString("ComboBox.togglePopupText"); 1375 } 1376 1377 /** 1378 * Returns the number of accessible actions that can be performed by 1379 * this object. AccessibleJComboBox implement s one accessible action 1380 * (toggle the popup menu), so this method always returns <code>1</code>. 1381 * 1382 * @return the number of accessible actions that can be performed by 1383 * this object 1384 */ 1385 public int getAccessibleActionCount() 1386 { 1387 return 1; 1388 } 1389 1390 /** 1391 * Performs the accessible action with the specified index. 1392 * AccessibleJComboBox has 1 accessible action 1393 * (<code>actionIndex == 0</code>), which is to toggle the 1394 * popup menu. All other action indices have no effect and return 1395 * <code<>false</code>. 1396 * 1397 * @param actionIndex the index of the action to perform 1398 * 1399 * @return <code>true</code> if the action has been performed, 1400 * <code>false</code> otherwise 1401 */ 1402 public boolean doAccessibleAction(int actionIndex) 1403 { 1404 boolean actionPerformed = false; 1405 if (actionIndex == 0) 1406 { 1407 setPopupVisible(! isPopupVisible()); 1408 actionPerformed = true; 1409 } 1410 return actionPerformed; 1411 } 1412 1413 /** 1414 * Returns the number of selected accessible children of this object. This 1415 * returns <code>1</code> if the combobox has a selected entry, 1416 * <code>0</code> otherwise. 1417 * 1418 * @return the number of selected accessible children of this object 1419 */ 1420 public int getAccessibleSelectionCount() 1421 { 1422 Object sel = getSelectedItem(); 1423 int count = 0; 1424 if (sel != null) 1425 count = 1; 1426 return count; 1427 } 1428 1429 /** 1430 * Sets the current selection to the specified <code>index</code>. 1431 * 1432 * @param index the index to set as selection 1433 */ 1434 public void addAccessibleSelection(int index) 1435 { 1436 setSelectedIndex(index); 1437 } 1438 1439 /** 1440 * Removes the specified index from the current selection. 1441 * 1442 * @param index the index to remove from the selection 1443 */ 1444 public void removeAccessibleSelection(int index) 1445 { 1446 if (getSelectedIndex() == index) 1447 clearAccessibleSelection(); 1448 } 1449 1450 /** 1451 * Clears the current selection. 1452 */ 1453 public void clearAccessibleSelection() 1454 { 1455 setSelectedIndex(-1); 1456 } 1457 1458 /** 1459 * Multiple selection is not supported by AccessibleJComboBox, so this 1460 * does nothing. 1461 */ 1462 public void selectAllAccessibleSelection() 1463 { 1464 // Nothing to do here. 1465 } 1466 } 1467 1468 private class DefaultKeySelectionManager 1469 implements KeySelectionManager 1470 { 1471 1472 public int selectionForKey(char aKey, ComboBoxModel aModel) 1473 { 1474 int selectedIndex = getSelectedIndex(); 1475 1476 // Start at currently selected item and iterate to end of list 1477 for (int i = selectedIndex + 1; i < aModel.getSize(); i++) 1478 { 1479 String nextItem = aModel.getElementAt(i).toString(); 1480 1481 if (nextItem.charAt(0) == aKey) 1482 return i; 1483 } 1484 1485 // Wrap to start of list if no match yet 1486 for (int i = 0; i <= selectedIndex; i++) 1487 { 1488 String nextItem = aModel.getElementAt(i).toString(); 1489 1490 if (nextItem.charAt(0) == aKey) 1491 return i; 1492 } 1493 1494 return - 1; 1495 } 1496 } 1497 }