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