Frames | No Frames |
1: /* JComboBox.java -- 2: Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing; 40: 41: import java.awt.ItemSelectable; 42: import java.awt.event.ActionEvent; 43: import java.awt.event.ActionListener; 44: import java.awt.event.ItemEvent; 45: import java.awt.event.ItemListener; 46: import java.awt.event.KeyEvent; 47: import java.beans.PropertyChangeEvent; 48: import java.beans.PropertyChangeListener; 49: import java.util.Vector; 50: 51: import javax.accessibility.Accessible; 52: import javax.accessibility.AccessibleAction; 53: import javax.accessibility.AccessibleContext; 54: import javax.accessibility.AccessibleRole; 55: import javax.accessibility.AccessibleSelection; 56: import javax.swing.event.ListDataEvent; 57: import javax.swing.event.ListDataListener; 58: import javax.swing.event.PopupMenuEvent; 59: import javax.swing.event.PopupMenuListener; 60: import javax.swing.plaf.ComboBoxUI; 61: import javax.swing.plaf.ComponentUI; 62: import javax.swing.plaf.basic.ComboPopup; 63: 64: /** 65: * A component that allows a user to select any item in its list and 66: * displays the selected item to the user. JComboBox also can show/hide a 67: * popup menu containing its list of item whenever the mouse is pressed 68: * over it. 69: * 70: * @author Andrew Selkirk 71: * @author Olga Rodimina 72: * @author Robert Schuster 73: */ 74: public class JComboBox extends JComponent implements ItemSelectable, 75: ListDataListener, 76: ActionListener, 77: Accessible 78: { 79: 80: private static final long serialVersionUID = 5654585963292734470L; 81: 82: /** 83: * Classes implementing this interface are 84: * responsible for matching key characters typed by the user with combo 85: * box's items. 86: */ 87: public static interface KeySelectionManager 88: { 89: int selectionForKey(char aKey, ComboBoxModel aModel); 90: } 91: 92: /** 93: * Maximum number of rows that should be visible by default in the 94: * JComboBox's popup 95: */ 96: private static final int DEFAULT_MAXIMUM_ROW_COUNT = 8; 97: 98: /** 99: * 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: } 475: 476: /** 477: * Returns currently selected item in the combo box. 478: * The result may be <code>null</code> to indicate that nothing is 479: * currently selected. 480: * 481: * @return element that is currently selected in this combo box. 482: */ 483: public Object getSelectedItem() 484: { 485: return dataModel.getSelectedItem(); 486: } 487: 488: /** 489: * Forces JComboBox to select component located in the given index in the 490: * combo box. 491: * <p>If the index is below -1 or exceeds the upper bound an 492: * <code>IllegalArgumentException</code> is thrown.<p/> 493: * <p>If the index is -1 then no item gets selected.</p> 494: * 495: * @param index index specifying location of the component that should be 496: * selected. 497: */ 498: public void setSelectedIndex(int index) 499: { 500: if (index < -1 || index >= dataModel.getSize()) 501: // Fails because index is out of bounds. 502: throw new IllegalArgumentException("illegal index: " + index); 503: else 504: // Selects the item at the given index or clears the selection if the 505: // index value is -1. 506: setSelectedItem((index == -1) ? null : dataModel.getElementAt(index)); 507: } 508: 509: /** 510: * Returns index of the item that is currently selected in the combo box. If 511: * no item is currently selected, then -1 is returned. 512: * <p> 513: * Note: For performance reasons you should minimize invocation of this 514: * method. If the data model is not an instance of 515: * <code>DefaultComboBoxModel</code> the complexity is O(n) where n is the 516: * number of elements in the combo box. 517: * </p> 518: * 519: * @return int Index specifying location of the currently selected item in the 520: * combo box or -1 if nothing is selected in the combo box. 521: */ 522: public int getSelectedIndex() 523: { 524: Object selectedItem = getSelectedItem(); 525: 526: if (selectedItem != null) 527: { 528: if (dataModel instanceof DefaultComboBoxModel) 529: // Uses special method of DefaultComboBoxModel to retrieve the index. 530: return ((DefaultComboBoxModel) dataModel).getIndexOf(selectedItem); 531: else 532: { 533: // Iterates over all items to retrieve the index. 534: int size = dataModel.getSize(); 535: 536: for (int i = 0; i < size; i++) 537: { 538: Object o = dataModel.getElementAt(i); 539: 540: // XXX: Is special handling of ComparableS neccessary? 541: if ((selectedItem != null) ? selectedItem.equals(o) : o == null) 542: return i; 543: } 544: } 545: } 546: 547: // returns that no item is currently selected 548: return -1; 549: } 550: 551: /** 552: * Returns an object that is used as the display value when calculating the 553: * preferred size for the combo box. This value is, of course, never 554: * displayed anywhere. 555: * 556: * @return The prototype display value (possibly <code>null</code>). 557: * 558: * @since 1.4 559: * @see #setPrototypeDisplayValue(Object) 560: */ 561: public Object getPrototypeDisplayValue() 562: { 563: return prototypeDisplayValue; 564: } 565: 566: /** 567: * Sets the object that is assumed to be the displayed item when calculating 568: * the preferred size for the combo box. A {@link PropertyChangeEvent} (with 569: * the name <code>prototypeDisplayValue</code>) is sent to all registered 570: * listeners. 571: * 572: * @param value the new value (<code>null</code> permitted). 573: * 574: * @since 1.4 575: * @see #getPrototypeDisplayValue() 576: */ 577: public void setPrototypeDisplayValue(Object value) 578: { 579: Object oldValue = prototypeDisplayValue; 580: prototypeDisplayValue = value; 581: firePropertyChange("prototypeDisplayValue", oldValue, value); 582: } 583: 584: /** 585: * This method adds given element to this JComboBox. 586: * <p>A <code>RuntimeException</code> is thrown if the data model is not 587: * an instance of {@link MutableComboBoxModel}.</p> 588: * 589: * @param element element to add 590: */ 591: public void addItem(Object element) 592: { 593: if (dataModel instanceof MutableComboBoxModel) 594: ((MutableComboBoxModel) dataModel).addElement(element); 595: else 596: throw new RuntimeException("Unable to add the item because the data " 597: + "model it is not an instance of " 598: + "MutableComboBoxModel."); 599: } 600: 601: /** 602: * Inserts given element at the specified index to this JComboBox. 603: * <p>A <code>RuntimeException</code> is thrown if the data model is not 604: * an instance of {@link MutableComboBoxModel}.</p> 605: * 606: * @param element element to insert 607: * @param index position where to insert the element 608: */ 609: public void insertItemAt(Object element, int index) 610: { 611: if (dataModel instanceof MutableComboBoxModel) 612: ((MutableComboBoxModel) dataModel).insertElementAt(element, index); 613: else 614: throw new RuntimeException("Unable to insert the item because the data " 615: + "model it is not an instance of " 616: + "MutableComboBoxModel."); 617: } 618: 619: /** 620: * This method removes given element from this JComboBox. 621: * <p>A <code>RuntimeException</code> is thrown if the data model is not 622: * an instance of {@link MutableComboBoxModel}.</p> 623: * 624: * @param element element to remove 625: */ 626: public void removeItem(Object element) 627: { 628: if (dataModel instanceof MutableComboBoxModel) 629: ((MutableComboBoxModel) dataModel).removeElement(element); 630: else 631: throw new RuntimeException("Unable to remove the item because the data " 632: + "model it is not an instance of " 633: + "MutableComboBoxModel."); 634: } 635: 636: /** 637: * This method remove element location in the specified index in the 638: * JComboBox. 639: * <p>A <code>RuntimeException</code> is thrown if the data model is not 640: * an instance of {@link MutableComboBoxModel}.</p> 641: * 642: * @param index index specifying position of the element to remove 643: */ 644: public void removeItemAt(int index) 645: { 646: if (dataModel instanceof MutableComboBoxModel) 647: ((MutableComboBoxModel) dataModel).removeElementAt(index); 648: else 649: throw new RuntimeException("Unable to remove the item because the data " 650: + "model it is not an instance of " 651: + "MutableComboBoxModel."); 652: } 653: 654: /** 655: * This method removes all elements from this JComboBox. 656: * <p> 657: * A <code>RuntimeException</code> is thrown if the data model is not an 658: * instance of {@link MutableComboBoxModel}. 659: * </p> 660: */ 661: public void removeAllItems() 662: { 663: if (dataModel instanceof DefaultComboBoxModel) 664: // Uses special method if we have a DefaultComboBoxModel. 665: ((DefaultComboBoxModel) dataModel).removeAllElements(); 666: else if (dataModel instanceof MutableComboBoxModel) 667: { 668: // Iterates over all items and removes each. 669: MutableComboBoxModel mcbm = (MutableComboBoxModel) dataModel; 670: 671: // We intentionally remove the items backwards to support models which 672: // shift their content to the beginning (e.g. linked lists) 673: for (int i = mcbm.getSize() - 1; i >= 0; i--) 674: mcbm.removeElementAt(i); 675: } 676: else 677: throw new RuntimeException("Unable to remove the items because the data " 678: + "model it is not an instance of " 679: + "MutableComboBoxModel."); 680: } 681: 682: /** 683: * This method displays popup with list of combo box's items on the screen 684: */ 685: public void showPopup() 686: { 687: setPopupVisible(true); 688: } 689: 690: /** 691: * This method hides popup containing list of combo box's items 692: */ 693: public void hidePopup() 694: { 695: setPopupVisible(false); 696: } 697: 698: /** 699: * This method either displayes or hides the popup containing list of combo 700: * box's items. 701: * 702: * @param visible show popup if 'visible' is true and hide it otherwise 703: */ 704: public void setPopupVisible(boolean visible) 705: { 706: getUI().setPopupVisible(this, visible); 707: } 708: 709: /** 710: * Checks if popup is currently visible on the screen. 711: * 712: * @return boolean true if popup is visible and false otherwise 713: */ 714: public boolean isPopupVisible() 715: { 716: return getUI().isPopupVisible(this); 717: } 718: 719: /** 720: * This method sets actionCommand to the specified string. ActionEvent fired 721: * to this JComboBox registered ActionListeners will contain this 722: * actionCommand. 723: * 724: * @param aCommand new action command for the JComboBox's ActionEvent 725: */ 726: public void setActionCommand(String aCommand) 727: { 728: actionCommand = aCommand; 729: } 730: 731: /** 732: * Returns actionCommand associated with the ActionEvent fired by the 733: * JComboBox to its registered ActionListeners. 734: * 735: * @return String actionCommand for the ActionEvent 736: */ 737: public String getActionCommand() 738: { 739: return actionCommand; 740: } 741: 742: /** 743: * setAction 744: * 745: * @param a action to set 746: */ 747: public void setAction(Action a) 748: { 749: Action old = action; 750: action = a; 751: configurePropertiesFromAction(action); 752: if (action != null) 753: // FIXME: remove from old action and add to new action 754: // PropertyChangeListener to listen to changes in the action 755: addActionListener(action); 756: } 757: 758: /** 759: * This method returns Action that is invoked when selected item is changed 760: * in the JComboBox. 761: * 762: * @return Action 763: */ 764: public Action getAction() 765: { 766: return action; 767: } 768: 769: /** 770: * Configure properties of the JComboBox by reading properties of specified 771: * action. This method always sets the comboBox's "enabled" property to the 772: * value of the Action's "enabled" property. 773: * 774: * @param a An Action to configure the combo box from 775: */ 776: protected void configurePropertiesFromAction(Action a) 777: { 778: if (a == null) 779: { 780: setEnabled(true); 781: setToolTipText(null); 782: } 783: else 784: { 785: setEnabled(a.isEnabled()); 786: setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION))); 787: } 788: } 789: 790: /** 791: * Creates PropertyChangeListener to listen for the changes in comboBox's 792: * action properties. 793: * 794: * @param action action to listen to for property changes 795: * 796: * @return a PropertyChangeListener that listens to changes in 797: * action properties. 798: */ 799: protected PropertyChangeListener createActionPropertyChangeListener(Action action) 800: { 801: return new PropertyChangeListener() 802: { 803: public void propertyChange(PropertyChangeEvent e) 804: { 805: Action act = (Action) (e.getSource()); 806: configurePropertiesFromAction(act); 807: } 808: }; 809: } 810: 811: /** 812: * This method fires ItemEvent to this JComboBox's registered ItemListeners. 813: * This method is invoked when currently selected item in this combo box 814: * has changed. 815: * 816: * @param e the ItemEvent describing the change in the combo box's 817: * selection. 818: */ 819: protected void fireItemStateChanged(ItemEvent e) 820: { 821: ItemListener[] ll = getItemListeners(); 822: 823: for (int i = 0; i < ll.length; i++) 824: ll[i].itemStateChanged(e); 825: } 826: 827: /** 828: * This method fires ActionEvent to this JComboBox's registered 829: * ActionListeners. This method is invoked when user explicitly changes 830: * currently selected item. 831: */ 832: protected void fireActionEvent() 833: { 834: ActionListener[] ll = getActionListeners(); 835: 836: for (int i = 0; i < ll.length; i++) 837: ll[i].actionPerformed(new ActionEvent(this, 838: ActionEvent.ACTION_PERFORMED, 839: actionCommand)); 840: } 841: 842: /** 843: * Fires a popupMenuCanceled() event to all <code>PopupMenuListeners</code>. 844: * 845: * Note: This method is intended for use by plaf classes only. 846: */ 847: public void firePopupMenuCanceled() 848: { 849: PopupMenuListener[] listeners = getPopupMenuListeners(); 850: PopupMenuEvent e = new PopupMenuEvent(this); 851: for (int i = 0; i < listeners.length; i++) 852: listeners[i].popupMenuCanceled(e); 853: } 854: 855: /** 856: * Fires a popupMenuWillBecomeInvisible() event to all 857: * <code>PopupMenuListeners</code>. 858: * 859: * Note: This method is intended for use by plaf classes only. 860: */ 861: public void firePopupMenuWillBecomeInvisible() 862: { 863: PopupMenuListener[] listeners = getPopupMenuListeners(); 864: PopupMenuEvent e = new PopupMenuEvent(this); 865: for (int i = 0; i < listeners.length; i++) 866: listeners[i].popupMenuWillBecomeInvisible(e); 867: } 868: 869: /** 870: * Fires a popupMenuWillBecomeVisible() event to all 871: * <code>PopupMenuListeners</code>. 872: * 873: * Note: This method is intended for use by plaf classes only. 874: */ 875: public void firePopupMenuWillBecomeVisible() 876: { 877: PopupMenuListener[] listeners = getPopupMenuListeners(); 878: PopupMenuEvent e = new PopupMenuEvent(this); 879: for (int i = 0; i < listeners.length; i++) 880: listeners[i].popupMenuWillBecomeVisible(e); 881: } 882: 883: /** 884: * This method is invoked whenever selected item changes in the combo box's 885: * data model. It fires ItemEvent and ActionEvent to all registered 886: * ComboBox's ItemListeners and ActionListeners respectively, indicating 887: * the change. 888: */ 889: protected void selectedItemChanged() 890: { 891: // Fire ItemEvent to indicated that previously selected item is now 892: // deselected 893: if (selectedItemReminder != null) 894: fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, 895: selectedItemReminder, 896: ItemEvent.DESELECTED)); 897: 898: // Fire ItemEvent to indicate that new item is selected 899: Object newSelection = getSelectedItem(); 900: if (newSelection != null) 901: fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, 902: newSelection, ItemEvent.SELECTED)); 903: 904: // Fire Action Event to JComboBox's registered listeners 905: fireActionEvent(); 906: 907: selectedItemReminder = newSelection; 908: } 909: 910: /** 911: * Returns Object array of size 1 containing currently selected element in 912: * the JComboBox. 913: * 914: * @return Object[] Object array of size 1 containing currently selected 915: * element in the JComboBox. 916: */ 917: public Object[] getSelectedObjects() 918: { 919: return new Object[] { getSelectedItem() }; 920: } 921: 922: /** 923: * This method handles actionEvents fired by the ComboBoxEditor. It changes 924: * this JComboBox's selection to the new value currently in the editor and 925: * hides list of combo box items. 926: * 927: * @param e the ActionEvent 928: */ 929: public void actionPerformed(ActionEvent e) 930: { 931: setSelectedItem(getEditor().getItem()); 932: setPopupVisible(false); 933: } 934: 935: /** 936: * This method selects item in this combo box that matches specified 937: * specified keyChar and returns true if such item is found. Otherwise 938: * false is returned. 939: * 940: * @param keyChar character indicating which item in the combo box should be 941: * selected. 942: * 943: * @return boolean true if item corresponding to the specified keyChar 944: * exists in the combo box. Otherwise false is returned. 945: */ 946: public boolean selectWithKeyChar(char keyChar) 947: { 948: if (keySelectionManager == null) 949: { 950: keySelectionManager = createDefaultKeySelectionManager(); 951: } 952: 953: int index = keySelectionManager.selectionForKey(keyChar, getModel()); 954: boolean retVal = false; 955: if (index >= 0) 956: { 957: setSelectedIndex(index); 958: retVal = true; 959: } 960: return retVal; 961: } 962: 963: /** 964: * The part of implementation of ListDataListener interface. This method is 965: * invoked when some items where added to the JComboBox's data model. 966: * 967: * @param event ListDataEvent describing the change 968: */ 969: public void intervalAdded(ListDataEvent event) 970: { 971: // FIXME: Need to implement 972: repaint(); 973: } 974: 975: /** 976: * The part of implementation of ListDataListener interface. This method is 977: * invoked when some items where removed from the JComboBox's data model. 978: * 979: * @param event ListDataEvent describing the change. 980: */ 981: public void intervalRemoved(ListDataEvent event) 982: { 983: // FIXME: Need to implement 984: repaint(); 985: } 986: 987: /** 988: * The part of implementation of ListDataListener interface. This method is 989: * invoked when contents of the JComboBox's data model changed. 990: * 991: * @param event ListDataEvent describing the change 992: */ 993: public void contentsChanged(ListDataEvent event) 994: { 995: // if first and last index of the given ListDataEvent are both -1, 996: // then it indicates that selected item in the combo box data model 997: // have changed. 998: if (event.getIndex0() == -1 && event.getIndex1() == -1) 999: selectedItemChanged(); 1000: } 1001: 1002: /** 1003: * This method disables or enables JComboBox. If the JComboBox is enabled, 1004: * then user is able to make item choice, otherwise if JComboBox is 1005: * disabled then user is not able to make a selection. 1006: * 1007: * @param enabled if 'enabled' is true then enable JComboBox and disable it 1008: */ 1009: public void setEnabled(boolean enabled) 1010: { 1011: boolean oldEnabled = super.isEnabled(); 1012: if (enabled != oldEnabled) 1013: { 1014: super.setEnabled(enabled); 1015: firePropertyChange("enabled", oldEnabled, enabled); 1016: } 1017: } 1018: 1019: /** 1020: * This method initializes specified ComboBoxEditor to display given item. 1021: * 1022: * @param anEditor ComboBoxEditor to initialize 1023: * @param anItem Item that should displayed in the specified editor 1024: */ 1025: public void configureEditor(ComboBoxEditor anEditor, Object anItem) 1026: { 1027: anEditor.setItem(anItem); 1028: } 1029: 1030: /** 1031: * This method hides combo box's popup whenever TAB key is pressed. 1032: * 1033: * @param e The KeyEvent indicating which key was pressed. 1034: */ 1035: public void processKeyEvent(KeyEvent e) 1036: { 1037: if (e.getKeyCode() == KeyEvent.VK_TAB) 1038: setPopupVisible(false); 1039: else if (keySelectionManager != null) 1040: { 1041: int i = keySelectionManager.selectionForKey(e.getKeyChar(), 1042: getModel()); 1043: if (i >= 0) 1044: setSelectedIndex(i); 1045: else 1046: super.processKeyEvent(e); 1047: } 1048: else 1049: super.processKeyEvent(e); 1050: } 1051: 1052: /** 1053: * setKeySelectionManager 1054: * 1055: * @param aManager 1056: */ 1057: public void setKeySelectionManager(KeySelectionManager aManager) 1058: { 1059: keySelectionManager = aManager; 1060: } 1061: 1062: /** 1063: * getKeySelectionManager 1064: * 1065: * @return JComboBox.KeySelectionManager 1066: */ 1067: public KeySelectionManager getKeySelectionManager() 1068: { 1069: return null; 1070: } 1071: 1072: /** 1073: * This method returns number of elements in this JComboBox 1074: * 1075: * @return int number of elements in this JComboBox 1076: */ 1077: public int getItemCount() 1078: { 1079: return dataModel.getSize(); 1080: } 1081: 1082: /** 1083: * Returns elements located in the combo box at the given index. 1084: * 1085: * @param index index specifying location of the component to return. 1086: * 1087: * @return component in the combo box that is located in the given index. 1088: */ 1089: public Object getItemAt(int index) 1090: { 1091: return dataModel.getElementAt(index); 1092: } 1093: 1094: /** 1095: * createDefaultKeySelectionManager 1096: * 1097: * @return KeySelectionManager 1098: */ 1099: protected KeySelectionManager createDefaultKeySelectionManager() 1100: { 1101: return null; 1102: } 1103: 1104: /** 1105: * Returns an implementation-dependent string describing the attributes of 1106: * this <code>JComboBox</code>. 1107: * 1108: * @return A string describing the attributes of this <code>JComboBox</code> 1109: * (never <code>null</code>). 1110: */ 1111: protected String paramString() 1112: { 1113: String superParamStr = super.paramString(); 1114: StringBuffer sb = new StringBuffer(); 1115: sb.append(",isEditable=").append(isEditable()); 1116: sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled()); 1117: sb.append(",maximumRowCount=").append(getMaximumRowCount()); 1118: 1119: sb.append(",selectedItemReminder="); 1120: if (selectedItemReminder != null) 1121: sb.append(selectedItemReminder); 1122: return superParamStr + sb.toString(); 1123: } 1124: 1125: /** 1126: * Returns the object that provides accessibility features for this 1127: * <code>JComboBox</code> component. 1128: * 1129: * @return The accessible context (an instance of 1130: * {@link AccessibleJComboBox}). 1131: */ 1132: public AccessibleContext getAccessibleContext() 1133: { 1134: if (accessibleContext == null) 1135: accessibleContext = new AccessibleJComboBox(); 1136: 1137: return accessibleContext; 1138: } 1139: 1140: /** 1141: * This methods adds specified ActionListener to this JComboBox. 1142: * 1143: * @param listener to add 1144: */ 1145: public void addActionListener(ActionListener listener) 1146: { 1147: listenerList.add(ActionListener.class, listener); 1148: } 1149: 1150: /** 1151: * This method removes specified ActionListener from this JComboBox. 1152: * 1153: * @param listener ActionListener 1154: */ 1155: public void removeActionListener(ActionListener listener) 1156: { 1157: listenerList.remove(ActionListener.class, listener); 1158: } 1159: 1160: /** 1161: * This method returns array of ActionListeners that are registered with 1162: * this JComboBox. 1163: * 1164: * @since 1.4 1165: */ 1166: public ActionListener[] getActionListeners() 1167: { 1168: return (ActionListener[]) getListeners(ActionListener.class); 1169: } 1170: 1171: /** 1172: * This method registers given ItemListener with this JComboBox 1173: * 1174: * @param listener to remove 1175: */ 1176: public void addItemListener(ItemListener listener) 1177: { 1178: listenerList.add(ItemListener.class, listener); 1179: } 1180: 1181: /** 1182: * This method unregisters given ItemListener from this JComboBox 1183: * 1184: * @param listener to remove 1185: */ 1186: public void removeItemListener(ItemListener listener) 1187: { 1188: listenerList.remove(ItemListener.class, listener); 1189: } 1190: 1191: /** 1192: * This method returns array of ItemListeners that are registered with this 1193: * JComboBox. 1194: * 1195: * @since 1.4 1196: */ 1197: public ItemListener[] getItemListeners() 1198: { 1199: return (ItemListener[]) getListeners(ItemListener.class); 1200: } 1201: 1202: /** 1203: * Adds PopupMenuListener to combo box to listen to the events fired by the 1204: * combo box's popup menu containing its list of items 1205: * 1206: * @param listener to add 1207: */ 1208: public void addPopupMenuListener(PopupMenuListener listener) 1209: { 1210: listenerList.add(PopupMenuListener.class, listener); 1211: } 1212: 1213: /** 1214: * Removes PopupMenuListener to combo box to listen to the events fired by 1215: * the combo box's popup menu containing its list of items 1216: * 1217: * @param listener to add 1218: */ 1219: public void removePopupMenuListener(PopupMenuListener listener) 1220: { 1221: listenerList.remove(PopupMenuListener.class, listener); 1222: } 1223: 1224: /** 1225: * Returns array of PopupMenuListeners that are registered with combo box. 1226: */ 1227: public PopupMenuListener[] getPopupMenuListeners() 1228: { 1229: return (PopupMenuListener[]) getListeners(PopupMenuListener.class); 1230: } 1231: 1232: /** 1233: * Accessibility support for <code>JComboBox</code>. 1234: */ 1235: protected class AccessibleJComboBox extends AccessibleJComponent 1236: implements AccessibleAction, AccessibleSelection 1237: { 1238: private static final long serialVersionUID = 8217828307256675666L; 1239: 1240: /** 1241: * @specnote This constructor was protected in 1.4, but made public 1242: * in 1.5. 1243: */ 1244: public AccessibleJComboBox() 1245: { 1246: // Nothing to do here. 1247: } 1248: 1249: /** 1250: * Returns the number of accessible children of this object. The 1251: * implementation of AccessibleJComboBox delegates this call to the UI 1252: * of the associated JComboBox. 1253: * 1254: * @return the number of accessible children of this object 1255: * 1256: * @see ComponentUI#getAccessibleChildrenCount(JComponent) 1257: */ 1258: public int getAccessibleChildrenCount() 1259: { 1260: ComponentUI ui = getUI(); 1261: int count; 1262: if (ui != null) 1263: count = ui.getAccessibleChildrenCount(JComboBox.this); 1264: else 1265: count = super.getAccessibleChildrenCount(); 1266: return count; 1267: } 1268: 1269: /** 1270: * Returns the number of accessible children of this object. The 1271: * implementation of AccessibleJComboBox delegates this call to the UI 1272: * of the associated JComboBox. 1273: * 1274: * @param index the index of the accessible child to fetch 1275: * 1276: * @return the number of accessible children of this object 1277: * 1278: * @see ComponentUI#getAccessibleChild(JComponent, int) 1279: */ 1280: public Accessible getAccessibleChild(int index) 1281: { 1282: ComponentUI ui = getUI(); 1283: Accessible child = null; 1284: if (ui != null) 1285: child = ui.getAccessibleChild(JComboBox.this, index); 1286: else 1287: child = super.getAccessibleChild(index); 1288: return child; 1289: } 1290: 1291: /** 1292: * Returns the AccessibleSelection object associated with this object. 1293: * AccessibleJComboBoxes handle their selection themselves, so this 1294: * always returns <code>this</code>. 1295: * 1296: * @return the AccessibleSelection object associated with this object 1297: */ 1298: public AccessibleSelection getAccessibleSelection() 1299: { 1300: return this; 1301: } 1302: 1303: /** 1304: * Returns the accessible selection from this AccssibleJComboBox. 1305: * 1306: * @param index the index of the selected child to fetch 1307: * 1308: * @return the accessible selection from this AccssibleJComboBox 1309: */ 1310: public Accessible getAccessibleSelection(int index) 1311: { 1312: // Get hold of the actual popup. 1313: Accessible popup = getUI().getAccessibleChild(JComboBox.this, 0); 1314: Accessible selected = null; 1315: if (popup != null && popup instanceof ComboPopup) 1316: { 1317: ComboPopup cPopup = (ComboPopup) popup; 1318: // Query the list for the currently selected child. 1319: JList l = cPopup.getList(); 1320: AccessibleContext listCtx = l.getAccessibleContext(); 1321: if (listCtx != null) 1322: { 1323: AccessibleSelection s = listCtx.getAccessibleSelection(); 1324: if (s != null) 1325: { 1326: selected = s.getAccessibleSelection(index); 1327: } 1328: } 1329: } 1330: return selected; 1331: } 1332: 1333: /** 1334: * Returns <code>true</code> if the accessible child with the specified 1335: * <code>index</code> is selected, <code>false</code> otherwise. 1336: * 1337: * @param index the index of the accessible child 1338: * 1339: * @return <code>true</code> if the accessible child with the specified 1340: * <code>index</code> is selected, <code>false</code> otherwise 1341: */ 1342: public boolean isAccessibleChildSelected(int index) 1343: { 1344: return getSelectedIndex() == index; 1345: } 1346: 1347: /** 1348: * Returns the accessible role for the <code>JComboBox</code> component. 1349: * 1350: * @return {@link AccessibleRole#COMBO_BOX}. 1351: */ 1352: public AccessibleRole getAccessibleRole() 1353: { 1354: return AccessibleRole.COMBO_BOX; 1355: } 1356: 1357: /** 1358: * Returns the accessible action associated to this accessible object. 1359: * AccessibleJComboBox implements its own AccessibleAction, so this 1360: * method returns <code>this</code>. 1361: * 1362: * @return the accessible action associated to this accessible object 1363: */ 1364: public AccessibleAction getAccessibleAction() 1365: { 1366: return this; 1367: } 1368: 1369: /** 1370: * Returns the description of the specified action. AccessibleJComboBox 1371: * implements 1 action (toggle the popup menu) and thus returns 1372: * <code>UIManager.getString("ComboBox.togglePopupText")</code> 1373: * 1374: * @param actionIndex the index of the action for which to return the 1375: * description 1376: * 1377: * @return the description of the specified action 1378: */ 1379: public String getAccessibleActionDescription(int actionIndex) 1380: { 1381: return UIManager.getString("ComboBox.togglePopupText"); 1382: } 1383: 1384: /** 1385: * Returns the number of accessible actions that can be performed by 1386: * this object. AccessibleJComboBox implement s one accessible action 1387: * (toggle the popup menu), so this method always returns <code>1</code>. 1388: * 1389: * @return the number of accessible actions that can be performed by 1390: * this object 1391: */ 1392: public int getAccessibleActionCount() 1393: { 1394: return 1; 1395: } 1396: 1397: /** 1398: * Performs the accessible action with the specified index. 1399: * AccessibleJComboBox has 1 accessible action 1400: * (<code>actionIndex == 0</code>), which is to toggle the 1401: * popup menu. All other action indices have no effect and return 1402: * <code<>false</code>. 1403: * 1404: * @param actionIndex the index of the action to perform 1405: * 1406: * @return <code>true</code> if the action has been performed, 1407: * <code>false</code> otherwise 1408: */ 1409: public boolean doAccessibleAction(int actionIndex) 1410: { 1411: boolean actionPerformed = false; 1412: if (actionIndex == 0) 1413: { 1414: setPopupVisible(! isPopupVisible()); 1415: actionPerformed = true; 1416: } 1417: return actionPerformed; 1418: } 1419: 1420: /** 1421: * Returns the number of selected accessible children of this object. This 1422: * returns <code>1</code> if the combobox has a selected entry, 1423: * <code>0</code> otherwise. 1424: * 1425: * @return the number of selected accessible children of this object 1426: */ 1427: public int getAccessibleSelectionCount() 1428: { 1429: Object sel = getSelectedItem(); 1430: int count = 0; 1431: if (sel != null) 1432: count = 1; 1433: return count; 1434: } 1435: 1436: /** 1437: * Sets the current selection to the specified <code>index</code>. 1438: * 1439: * @param index the index to set as selection 1440: */ 1441: public void addAccessibleSelection(int index) 1442: { 1443: setSelectedIndex(index); 1444: } 1445: 1446: /** 1447: * Removes the specified index from the current selection. 1448: * 1449: * @param index the index to remove from the selection 1450: */ 1451: public void removeAccessibleSelection(int index) 1452: { 1453: if (getSelectedIndex() == index) 1454: clearAccessibleSelection(); 1455: } 1456: 1457: /** 1458: * Clears the current selection. 1459: */ 1460: public void clearAccessibleSelection() 1461: { 1462: setSelectedIndex(-1); 1463: } 1464: 1465: /** 1466: * Multiple selection is not supported by AccessibleJComboBox, so this 1467: * does nothing. 1468: */ 1469: public void selectAllAccessibleSelection() 1470: { 1471: // Nothing to do here. 1472: } 1473: } 1474: }