1. /*
  2. * @(#)JComboBox.java 1.62 01/11/29
  3. *
  4. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.beans.*;
  9. import java.util.*;
  10. import java.awt.*;
  11. import java.awt.event.*;
  12. import java.io.Serializable;
  13. import java.io.ObjectOutputStream;
  14. import java.io.ObjectInputStream;
  15. import java.io.IOException;
  16. import javax.swing.event.*;
  17. import javax.swing.plaf.*;
  18. import javax.swing.border.*;
  19. import javax.accessibility.*;
  20. /**
  21. * Swing's implementation of a ComboBox -- a combination of a text field and
  22. * drop-down list that lets the user either type in a value or select it from
  23. * a list that is displayed when the user asks for it. The editing capability
  24. * can also be disabled so that the JComboBox acts only as a drop down list.
  25. * <p>
  26. * For the keyboard keys used by this component in the standard Look and
  27. * Feel (L&F) renditions, see the
  28. * <a href="doc-files/Key-Index.html#JComboBox">JComboBox</a> key assignments.
  29. * <p>
  30. * <strong>Warning:</strong>
  31. * Serialized objects of this class will not be compatible with
  32. * future Swing releases. The current serialization support is appropriate
  33. * for short term storage or RMI between applications running the same
  34. * version of Swing. A future release of Swing will provide support for
  35. * long term persistence.
  36. *
  37. * @beaninfo
  38. * attribute: isContainer false
  39. *
  40. * @version 1.57 10/08/98
  41. * @author Arnaud Weber
  42. */
  43. public class JComboBox extends JComponent
  44. implements ItemSelectable,ListDataListener,ActionListener, Accessible {
  45. /**
  46. * @see #getUIClassID
  47. * @see #readObject
  48. */
  49. private static final String uiClassID = "ComboBoxUI";
  50. protected ComboBoxModel dataModel;
  51. protected ListCellRenderer renderer;
  52. protected ComboBoxEditor editor;
  53. protected int maximumRowCount = 8;
  54. protected boolean isEditable = false;
  55. protected Object selectedItemReminder = null;
  56. protected KeySelectionManager keySelectionManager = null;
  57. protected String actionCommand = "comboBoxChanged";
  58. protected boolean lightWeightPopupEnabled = JPopupMenu.getDefaultLightWeightPopupEnabled();
  59. boolean firedActionEventOnContentsChanged = false; // Flag for keeping actionEvents under control
  60. boolean firingActionEvent = false;
  61. /**
  62. * Creates a JComboBox that takes its items from an existing ComboBoxModel.
  63. *
  64. * @param aModel the ComboBoxModel that provides the displayed list of items
  65. */
  66. public JComboBox(ComboBoxModel aModel) {
  67. super();
  68. setModel(aModel);
  69. init();
  70. }
  71. /**
  72. * Creates a JComboBox that contains the elements in the specified array.
  73. */
  74. public JComboBox(final Object items[]) {
  75. super();
  76. setModel(new DefaultComboBoxModel(items));
  77. init();
  78. }
  79. /**
  80. * Creates a JComboBox that contains the elements in the specified Vector.
  81. */
  82. public JComboBox(Vector items) {
  83. super();
  84. setModel(new DefaultComboBoxModel(items));
  85. init();
  86. }
  87. /**
  88. * Creates a JComboBox with a default data model.
  89. * The default data model is an empty list of objects.
  90. * Use <code>addItem</code> to add items.
  91. */
  92. public JComboBox() {
  93. super();
  94. setModel(new DefaultComboBoxModel());
  95. init();
  96. }
  97. private void init()
  98. {
  99. installAncestorListener();
  100. setOpaque(true);
  101. setAlignmentX(LEFT_ALIGNMENT);
  102. setAlignmentY(CENTER_ALIGNMENT);
  103. updateUI();
  104. }
  105. protected void installAncestorListener() {
  106. addAncestorListener(new AncestorListener(){
  107. public void ancestorAdded(AncestorEvent event){ hidePopup();}
  108. public void ancestorRemoved(AncestorEvent event){ hidePopup();}
  109. public void ancestorMoved(AncestorEvent event){ hidePopup();}});
  110. }
  111. /**
  112. * Sets the L&F object that renders this component.
  113. *
  114. * @param ui the ComboBoxUI L&F object
  115. * @see UIDefaults#getUI
  116. *
  117. * @beaninfo
  118. * expert: true
  119. * description: The ComboBoxUI implementation that defines the combo box look and feel.
  120. */
  121. public void setUI(ComboBoxUI ui) {
  122. super.setUI(ui);
  123. }
  124. /**
  125. * Notification from the UIFactory that the L&F has changed.
  126. *
  127. * @see JComponent#updateUI
  128. */
  129. public void updateUI() {
  130. setUI((ComboBoxUI)UIManager.getUI(this));
  131. }
  132. /**
  133. * Returns the name of the L&F class that renders this component.
  134. *
  135. * @return "ComboBoxUI"
  136. * @see JComponent#getUIClassID
  137. * @see UIDefaults#getUI
  138. */
  139. public String getUIClassID() {
  140. return uiClassID;
  141. }
  142. /**
  143. * Returns the L&F object that renders this component.
  144. *
  145. * @return the ComboBoxUI object that renders this component
  146. */
  147. public ComboBoxUI getUI() {
  148. return(ComboBoxUI)ui;
  149. }
  150. /**
  151. * Sets the data model that the JComboBox uses to obtain the list of items.
  152. *
  153. * @param aModel the ComboBoxModel that provides the displayed list of items
  154. *
  155. * @beaninfo
  156. * bound: true
  157. * description: Model that the combo box uses to get data to display.
  158. */
  159. public void setModel(ComboBoxModel aModel) {
  160. ComboBoxModel oldModel = dataModel;
  161. if ( dataModel != null )
  162. dataModel.removeListDataListener(this);
  163. dataModel = aModel;
  164. firePropertyChange( "model", oldModel, dataModel);
  165. dataModel.addListDataListener(this);
  166. invalidate();
  167. }
  168. /**
  169. * Returns the data model currently used by the JComboBox.
  170. *
  171. * @return the ComboBoxModel that provides the displayed list of items
  172. */
  173. public ComboBoxModel getModel() {
  174. return dataModel;
  175. }
  176. /*
  177. * Properties
  178. */
  179. /**
  180. * When displaying the popup, JComboBox choose to use a light weight popup if
  181. * it fits. This method allows you to disable this feature. You have to do disable
  182. * it if your application mixes light weight and heavy weights components.
  183. *
  184. * @beaninfo
  185. * expert: true
  186. * description: When set, disables using light weight popups.
  187. */
  188. public void setLightWeightPopupEnabled(boolean aFlag) {
  189. lightWeightPopupEnabled = aFlag;
  190. }
  191. /**
  192. * Returns true if lightweight (all-Java) popups are in use,
  193. * or false if heavyweight (native peer) popups are being used.
  194. *
  195. * @return true if lightweight popups are in use
  196. */
  197. public boolean isLightWeightPopupEnabled() {
  198. return lightWeightPopupEnabled;
  199. }
  200. /**
  201. * Determines whether the JComboBox field is editable. An editable JComboBox
  202. * allows the user to type into the field or selected an item from the list
  203. * to initialize the field, after which it can be edited. (The editing affects
  204. * only the field, the list item remains intact.) A non editable JComboBox
  205. * displays the selected item inthe field, but the selection cannot be modified.
  206. *
  207. * @param aFlag a boolean value, where true indicates that the field is editable
  208. *
  209. * @beaninfo
  210. * preferred: true
  211. * description: If true, the user can type a new value in the combo box.
  212. */
  213. public void setEditable(boolean aFlag) {
  214. boolean didChange = aFlag != isEditable;
  215. isEditable = aFlag;
  216. if ( didChange ) {
  217. firePropertyChange( "editable", !isEditable, isEditable );
  218. }
  219. }
  220. /**
  221. * Returns true if the JComboBox is editable.
  222. *
  223. * @return true if the JComboBox is editable, else false
  224. */
  225. public boolean isEditable() {
  226. return isEditable;
  227. }
  228. /**
  229. * Sets the maximum number of rows the JComboBox displays.
  230. * If the number of objects in the model is greater than count,
  231. * the combo box uses a scrollbar.
  232. *
  233. * @param count an int specifying the maximum number of items to display
  234. * in the list before using a scrollbar
  235. * @beaninfo
  236. * preferred: true
  237. * description: The maximum number of rows the popup should have
  238. */
  239. public void setMaximumRowCount(int count) {
  240. int oldCount = maximumRowCount;
  241. maximumRowCount = count;
  242. firePropertyChange( "maximumRowCount", oldCount, maximumRowCount );
  243. }
  244. /**
  245. * Returns the maximum number of items the combo box can display
  246. * without a scrollbar
  247. *
  248. * @return an int specifying the maximum number of items that are displayed
  249. * in the list before using a scrollbar
  250. */
  251. public int getMaximumRowCount() {
  252. return maximumRowCount;
  253. }
  254. /**
  255. * Sets the renderer that paints the item selected from the list in
  256. * the JComboBox field. The renderer is used if the JComboBox is not
  257. * editable. If it is editable, the editor is used to render and edit
  258. * the selected item.
  259. * <p>
  260. * The default renderer displays a string, obtained
  261. * by calling the selected object's <code>toString</code> method.
  262. * Other renderers can handle graphic images and composite items.
  263. * <p>
  264. * To display the selected item, <code>aRenderer.getListCellRendererComponent</code>
  265. * is called, passing the list object and an index of -1.
  266. *
  267. * @param aRenderer the ListCellRenderer that displays the selected item.
  268. * @see #setEditor
  269. * @beaninfo
  270. * expert: true
  271. * description: The renderer that paints the item selected in the list.
  272. */
  273. public void setRenderer(ListCellRenderer aRenderer) {
  274. ListCellRenderer oldRenderer = renderer;
  275. renderer = aRenderer;
  276. firePropertyChange( "renderer", oldRenderer, renderer );
  277. invalidate();
  278. }
  279. /**
  280. * Returns the renderer used to display the selected item in the JComboBox
  281. * field.
  282. *
  283. * @return the ListCellRenderer that displays the selected item.
  284. */
  285. public ListCellRenderer getRenderer() {
  286. return renderer;
  287. }
  288. /**
  289. * Sets the editor used to paint and edit the selected item in the JComboBox
  290. * field. The editor is used only if the receiving JComboBox is editable.
  291. * If not editable, the combo box uses the renderer to paint the selected item.
  292. *
  293. * @param anEditor the ComboBoxEditor that displays the selected item
  294. * @see #setRenderer
  295. * @beaninfo
  296. * expert: true
  297. * description: The editor that combo box uses to edit the current value
  298. */
  299. public void setEditor(ComboBoxEditor anEditor) {
  300. ComboBoxEditor oldEditor = editor;
  301. if ( editor != null )
  302. editor.removeActionListener(this);
  303. editor = anEditor;
  304. if ( editor != null ) {
  305. editor.addActionListener(this);
  306. }
  307. firePropertyChange( "editor", oldEditor, editor );
  308. }
  309. /**
  310. * Returns the editor used to paint and edit the selected item in the JComboBox
  311. * field.
  312. *
  313. * @return the ComboBoxEditor that displays the selected item
  314. */
  315. public ComboBoxEditor getEditor() {
  316. return editor;
  317. }
  318. /*
  319. * Selection
  320. */
  321. /**
  322. * Sets the selected item in the JComboBox by specifying the object in the list.
  323. * If <code>anObject</code> is in the list, the list displays with
  324. * <code>anObject</code> selected. If the object does not exist in the list,
  325. * the default data model selects the first item in the list.
  326. *
  327. * @param anObject the list object to select
  328. * @beaninfo
  329. * preferred: true
  330. * description: Sets the selected item in the JComboBox.
  331. */
  332. public void setSelectedItem(Object anObject) {
  333. firedActionEventOnContentsChanged = false;
  334. dataModel.setSelectedItem(anObject);
  335. if ( !firedActionEventOnContentsChanged ) {
  336. fireActionEvent();
  337. }
  338. else {
  339. firedActionEventOnContentsChanged = false;
  340. }
  341. }
  342. /**
  343. * Returns the currently selected item.
  344. *
  345. * @return the currently selected list object from the data model
  346. */
  347. public Object getSelectedItem() {
  348. return dataModel.getSelectedItem();
  349. }
  350. /**
  351. * Selects the item at index <code>anIndex</code>.
  352. *
  353. * @param anIndex an int specifying the list item to select, where 0 specifies
  354. * the first item in the list
  355. * @beaninfo
  356. * preferred: true
  357. * description: The item at index is selected.
  358. */
  359. public void setSelectedIndex(int anIndex) {
  360. int size = dataModel.getSize();
  361. if ( anIndex == -1 ) {
  362. setSelectedItem( null );
  363. }
  364. else if ( anIndex < -1 || anIndex >= size ) {
  365. throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
  366. }
  367. else {
  368. setSelectedItem(dataModel.getElementAt(anIndex));
  369. }
  370. }
  371. /**
  372. * Returns the index of the currently selected item in the list. The result is not
  373. * always defined if the JComboBox box allows selected items that are not in the
  374. * list.
  375. * Returns -1 if there is no selected item or if the user specified an item
  376. * which is not in the list.
  377. * @return an int specifying the currently selected list item, where 0 specifies
  378. * the first item in the list, or -1 if no item is selected or if
  379. * the currently selected item is not in the list
  380. */
  381. public int getSelectedIndex() {
  382. Object sObject = dataModel.getSelectedItem();
  383. int i,c;
  384. Object obj;
  385. for ( i=0,c=dataModel.getSize();i<c;i++ ) {
  386. obj = dataModel.getElementAt(i);
  387. if ( obj.equals(sObject) )
  388. return i;
  389. }
  390. return -1;
  391. }
  392. /**
  393. * Adds an item to the item list.
  394. * This method works only if the JComboBox uses the default data model.
  395. * JComboBox uses the default data model when created with the
  396. * empty constructor and no other model has been set.
  397. *
  398. * @param anObject the Object to add to the list
  399. */
  400. public void addItem(Object anObject) {
  401. checkMutableComboBoxModel();
  402. ((MutableComboBoxModel)dataModel).addElement(anObject);
  403. }
  404. /**
  405. * Inserts an item into the item list at a given index.
  406. * This method works only if the JComboBox uses the default data model.
  407. * JComboBox uses the default data model when created with the
  408. * empty constructor and no other model has been set.
  409. *
  410. * @param anObject the Object to add to the list
  411. * @param index an int specifying the position at which to add the item
  412. */
  413. public void insertItemAt(Object anObject, int index) {
  414. checkMutableComboBoxModel();
  415. ((MutableComboBoxModel)dataModel).insertElementAt(anObject,index);
  416. }
  417. /**
  418. * Removes an item from the item list.
  419. * This method works only if the JComboBox uses the default data model.
  420. * JComboBox uses the default data model when created with the empty constructor
  421. * and no other model has been set.
  422. *
  423. * @param anObject the object to remove from the item list
  424. */
  425. public void removeItem(Object anObject) {
  426. checkMutableComboBoxModel();
  427. ((MutableComboBoxModel)dataModel).removeElement(anObject);
  428. }
  429. /**
  430. * Removes the item at <code>anIndex</code>
  431. * This method works only if the JComboBox uses the default data model.
  432. * JComboBox uses the default data model when created with the
  433. * empty constructor and no other model has been set.
  434. *
  435. * @param anIndex an int specifying the idex of the item to remove, where 0
  436. * indicates the first item in the list
  437. */
  438. public void removeItemAt(int anIndex) {
  439. checkMutableComboBoxModel();
  440. ((MutableComboBoxModel)dataModel).removeElementAt( anIndex );
  441. }
  442. /**
  443. * Removes all items from the item list.
  444. * This method works only if the JComboBox uses the default data model.
  445. * JComboBox uses the default data model when created with the empty constructor
  446. * and no other model has been set.
  447. */
  448. public void removeAllItems() {
  449. checkMutableComboBoxModel();
  450. MutableComboBoxModel model = (MutableComboBoxModel)dataModel;
  451. int size = model.getSize();
  452. if ( model instanceof DefaultComboBoxModel ) {
  453. ((DefaultComboBoxModel)model).removeAllElements();
  454. }
  455. else {
  456. for ( int i = 0; i < size; ++i ) {
  457. Object element = model.getElementAt( 0 );
  458. model.removeElement( element );
  459. }
  460. }
  461. }
  462. void checkMutableComboBoxModel() {
  463. if ( !(dataModel instanceof MutableComboBoxModel) )
  464. throw new RuntimeException("Cannot use this method with a non-Mutable data model.");
  465. }
  466. /**
  467. * Causes the combo box to display its popup window
  468. * @see #setPopupVisible
  469. */
  470. public void showPopup() {
  471. setPopupVisible(true);
  472. }
  473. /**
  474. * Causes the combo box to close its popup window
  475. * @see #setPopupVisible
  476. */
  477. public void hidePopup() {
  478. setPopupVisible(false);
  479. }
  480. /**
  481. * Set the visiblity of the popup
  482. */
  483. public void setPopupVisible(boolean v) {
  484. getUI().setPopupVisible(this, v);
  485. }
  486. /**
  487. * Determine the visibility of the popup
  488. */
  489. public boolean isPopupVisible() {
  490. return getUI().isPopupVisible(this);
  491. }
  492. /** Selection **/
  493. /**
  494. * Adds an ItemListener. <code>aListener</code> will receive an event when
  495. * the selected item changes.
  496. *
  497. * @param aListener the ItemListener that is to be notified
  498. */
  499. public void addItemListener(ItemListener aListener) {
  500. listenerList.add(ItemListener.class,aListener);
  501. }
  502. /** Removes an ItemListener
  503. *
  504. * @param aListener the ItemListener to remove
  505. */
  506. public void removeItemListener(ItemListener aListener) {
  507. listenerList.remove(ItemListener.class,aListener);
  508. }
  509. /**
  510. * Adds an ActionListener. The listener will receive an action event
  511. * the user finishes making a selection.
  512. *
  513. * @param l the ActionListener that is to be notified
  514. */
  515. public void addActionListener(ActionListener l) {
  516. listenerList.add(ActionListener.class,l);
  517. }
  518. /** Removes an ActionListener
  519. *
  520. * @param l the ActionListener to remove
  521. */
  522. public void removeActionListener(ActionListener l) {
  523. listenerList.remove(ActionListener.class,l);
  524. }
  525. /**
  526. * Sets the action commnand that should be included in the event
  527. * sent to action listeners.
  528. *
  529. * @param aCommand a string containing the "command" that is sent
  530. * to action listeners. The same listener can then
  531. * do different things depending on the command it
  532. * receives.
  533. */
  534. public void setActionCommand(String aCommand) {
  535. actionCommand = aCommand;
  536. }
  537. /**
  538. * Returns the action commnand that is included in the event sent to
  539. * action listeners.
  540. *
  541. * @return the string containing the "command" that is sent
  542. * to action listeners.
  543. */
  544. public String getActionCommand() {
  545. return actionCommand;
  546. }
  547. /**
  548. * Notify all listeners that have registered interest for
  549. * notification on this event type.
  550. *
  551. * @see EventListenerList
  552. */
  553. protected void fireItemStateChanged(ItemEvent e) {
  554. // Guaranteed to return a non-null array
  555. Object[] listeners = listenerList.getListenerList();
  556. // Process the listeners last to first, notifying
  557. // those that are interested in this event
  558. for ( int i = listeners.length-2; i>=0; i-=2 ) {
  559. if ( listeners[i]==ItemListener.class ) {
  560. // Lazily create the event:
  561. // if (changeEvent == null)
  562. // changeEvent = new ChangeEvent(this);
  563. ((ItemListener)listeners[i+1]).itemStateChanged(e);
  564. }
  565. }
  566. }
  567. /**
  568. * Notify all listeners that have registered interest for
  569. * notification on this event type.
  570. *
  571. * @see EventListenerList
  572. */
  573. protected void fireActionEvent() {
  574. if ( !firingActionEvent ) {
  575. firingActionEvent = true;
  576. ActionEvent e = null;
  577. // Guaranteed to return a non-null array
  578. Object[] listeners = listenerList.getListenerList();
  579. // Process the listeners last to first, notifying
  580. // those that are interested in this event
  581. for ( int i = listeners.length-2; i>=0; i-=2 ) {
  582. if ( listeners[i]==ActionListener.class ) {
  583. if ( e == null )
  584. e = new ActionEvent(this,ActionEvent.ACTION_PERFORMED,getActionCommand());
  585. ((ActionListener)listeners[i+1]).actionPerformed(e);
  586. }
  587. }
  588. firingActionEvent = false;
  589. }
  590. }
  591. /**
  592. * This method is called when the selected item changes. Its default implementation
  593. * notifies the item listeners
  594. */
  595. protected void selectedItemChanged() {
  596. if ( selectedItemReminder != null ) {
  597. fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  598. selectedItemReminder,
  599. ItemEvent.DESELECTED));
  600. }
  601. selectedItemReminder = getModel().getSelectedItem();
  602. if ( selectedItemReminder != null )
  603. fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  604. selectedItemReminder,
  605. ItemEvent.SELECTED));
  606. fireActionEvent();
  607. firedActionEventOnContentsChanged = true;
  608. }
  609. /**
  610. * Returns an array containing the selected item. This method is implemented for
  611. * compatibility with ItemSelectable.
  612. *
  613. * @returns an array of Objects containing one element -- the selected item
  614. */
  615. public Object[] getSelectedObjects() {
  616. Object selectedObject = getSelectedItem();
  617. if ( selectedObject == null )
  618. return new Object[0];
  619. else {
  620. Object result[] = new Object[1];
  621. result[0] = selectedObject;
  622. return result;
  623. }
  624. }
  625. /** This method is public as an implementation side effect.
  626. * do not call or override.
  627. */
  628. public void actionPerformed(ActionEvent e) {
  629. Object newItem = getEditor().getItem();
  630. firedActionEventOnContentsChanged = false;
  631. getUI().setPopupVisible(this, false);
  632. getModel().setSelectedItem(newItem);
  633. if ( !firedActionEventOnContentsChanged ) {
  634. fireActionEvent();
  635. }
  636. else {
  637. firedActionEventOnContentsChanged = false;
  638. }
  639. }
  640. /** This method is public as an implementation side effect.
  641. * do not call or override.
  642. *
  643. * @see javax.swing.event.ListDataListener
  644. */
  645. public void contentsChanged(ListDataEvent e) {
  646. ComboBoxModel mod = getModel();
  647. Object newSelectedItem = mod.getSelectedItem();
  648. if ( selectedItemReminder == null ) {
  649. if ( newSelectedItem != null )
  650. selectedItemChanged();
  651. }
  652. else {
  653. if ( !selectedItemReminder.equals(newSelectedItem) ) {
  654. selectedItemChanged();
  655. }
  656. }
  657. if ( !isEditable() && newSelectedItem != null ) {
  658. int i,c;
  659. boolean shouldResetSelectedItem = true;
  660. Object o;
  661. Object selectedItem = mod.getSelectedItem();
  662. for ( i=0,c=mod.getSize();i<c;i++ ) {
  663. o = mod.getElementAt(i);
  664. if ( o.equals(selectedItem) ) {
  665. shouldResetSelectedItem = false;
  666. break;
  667. }
  668. }
  669. if ( shouldResetSelectedItem ) {
  670. if ( mod.getSize() > 0 )
  671. setSelectedIndex(0);
  672. else
  673. setSelectedItem(null);
  674. }
  675. }
  676. }
  677. /**
  678. * Selects the list item that correponds to the specified keyboard character
  679. * and returns true, if there is an item corresponding to that character.
  680. * Otherwise, returns false.
  681. *
  682. * @param keyChar a char, typically this is a keyboard key typed by the user
  683. */
  684. public boolean selectWithKeyChar(char keyChar) {
  685. int index;
  686. if ( keySelectionManager == null )
  687. keySelectionManager = createDefaultKeySelectionManager();
  688. index = keySelectionManager.selectionForKey(keyChar,getModel());
  689. if ( index != -1 ) {
  690. setSelectedIndex(index);
  691. return true;
  692. }
  693. else
  694. return false;
  695. }
  696. /**
  697. * Invoked items have been added to the internal data model.
  698. * The "interval" includes the first and last values added.
  699. *
  700. * @see javax.swing.event.ListDataListener
  701. */
  702. public void intervalAdded(ListDataEvent e) {
  703. contentsChanged(e);
  704. }
  705. /**
  706. * Invoked when values have been removed from the data model.
  707. * The"interval" includes the first and last values removed.
  708. *
  709. * @see javax.swing.event.ListDataListener
  710. */
  711. public void intervalRemoved(ListDataEvent e) {
  712. contentsChanged(e);
  713. }
  714. /**
  715. * Enables the combo box so that items can be selected. When the
  716. * combo box is disabled, items cannot be selected and values
  717. * cannot be typed into its field (if it is editable).
  718. *
  719. * @param b a boolean value, where true enables the component and
  720. * false disables it
  721. * @beaninfo
  722. * preferred: true
  723. * description: Whether the combo box is enabled.
  724. */
  725. public void setEnabled(boolean b) {
  726. super.setEnabled(b);
  727. firePropertyChange( "enabled", !isEnabled(), isEnabled() );
  728. }
  729. /**
  730. * Initializes the editor with the specified item.
  731. *
  732. * @param anEditor the ComboBoxEditor that displays the list item in the
  733. * combo box field and allows it to be edited
  734. * @param anItem the object to display and edit in the field
  735. */
  736. public void configureEditor(ComboBoxEditor anEditor,Object anItem) {
  737. anEditor.setItem(anItem);
  738. }
  739. /**
  740. * Handles KeyEvents, looking for the Tab key. If the Tab key is found,
  741. * the popup window is closed.
  742. *
  743. * @param e the KeyEvent containing the keyboard key that was pressed
  744. */
  745. public void processKeyEvent(KeyEvent e) {
  746. if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
  747. hidePopup();
  748. }
  749. super.processKeyEvent(e);
  750. }
  751. /**
  752. * Returns true if the component can receive the focus. In this case,
  753. * the component returns false if it is editable, so that the Editor
  754. * object receives the focus, instead of the component.
  755. *
  756. * @return true if the component can receive the focus, else false.
  757. */
  758. public boolean isFocusTraversable() {
  759. return getUI().isFocusTraversable(this);
  760. }
  761. /**
  762. * Sets the object that translates a keyboard character into a list
  763. * selection. Typically, the first selection with a matching first
  764. * character becomes the selected item.
  765. *
  766. * @beaninfo
  767. * expert: true
  768. * description: The objects that changes the selection when a key is pressed.
  769. */
  770. public void setKeySelectionManager(KeySelectionManager aManager) {
  771. keySelectionManager = aManager;
  772. }
  773. /**
  774. * Returns the list's key-selection manager.
  775. *
  776. * @return the KeySelectionManager currently in use
  777. */
  778. public KeySelectionManager getKeySelectionManager() {
  779. return keySelectionManager;
  780. }
  781. /* Accessing the model */
  782. /**
  783. * Returns the number of items in the list.
  784. *
  785. * @return an int equal to the number of items in the list
  786. */
  787. public int getItemCount() {
  788. return dataModel.getSize();
  789. }
  790. /**
  791. * Returns the list item at the specified index.
  792. *
  793. * @param index an int indicating the list position, where the first
  794. * item starts at zero
  795. * @return the Object at that list position
  796. */
  797. public Object getItemAt(int index) {
  798. return dataModel.getElementAt(index);
  799. }
  800. /**
  801. * Returns an instance of the default key-selection manager.
  802. *
  803. * @return the KeySelectionManager currently used by the list
  804. * @see #setKeySelectionManager
  805. */
  806. protected KeySelectionManager createDefaultKeySelectionManager() {
  807. return new DefaultKeySelectionManager();
  808. }
  809. /**
  810. * The interface that defines a KeySelectionManager. To qualify as
  811. * a KeySelectionManager, the class needs to implement the method
  812. * that identifies the list index given a character and the
  813. * combo box data model.
  814. */
  815. public interface KeySelectionManager {
  816. /** Given <code>aKey</code> and the model, returns the row
  817. * that should become selected. Return -1 if no match was
  818. * found.
  819. *
  820. * @param aKey a char value, usually indicating a keyboard key that
  821. * was pressed
  822. * @param aModel a ComboBoxModel -- the component's data model, containing
  823. * the list of selectable items
  824. * @return an int equal to the selected row, where 0 is the
  825. * first item and -1 is none.
  826. */
  827. int selectionForKey(char aKey,ComboBoxModel aModel);
  828. }
  829. class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
  830. public int selectionForKey(char aKey,ComboBoxModel aModel) {
  831. int i,c;
  832. int currentSelection = -1;
  833. Object selectedItem = aModel.getSelectedItem();
  834. String v;
  835. String pattern;
  836. if ( selectedItem != null ) {
  837. selectedItem = selectedItem.toString();
  838. for ( i=0,c=aModel.getSize();i<c;i++ ) {
  839. if ( selectedItem.equals(aModel.getElementAt(i).toString()) ) {
  840. currentSelection = i;
  841. break;
  842. }
  843. }
  844. }
  845. pattern = ("" + aKey).toLowerCase();
  846. aKey = pattern.charAt(0);
  847. for ( i = ++currentSelection, c = aModel.getSize() ; i < c ; i++ ) {
  848. v = aModel.getElementAt(i).toString().toLowerCase();
  849. if ( v.length() > 0 && v.charAt(0) == aKey )
  850. return i;
  851. }
  852. for ( i = 0 ; i < currentSelection ; i ++ ) {
  853. v = aModel.getElementAt(i).toString().toLowerCase();
  854. if ( v.length() > 0 && v.charAt(0) == aKey )
  855. return i;
  856. }
  857. return -1;
  858. }
  859. }
  860. /**
  861. * See readObject() and writeObject() in JComponent for more
  862. * information about serialization in Swing.
  863. */
  864. private void writeObject(ObjectOutputStream s) throws IOException {
  865. s.defaultWriteObject();
  866. if ((ui != null) && (getUIClassID().equals(uiClassID))) {
  867. ui.installUI(this);
  868. }
  869. }
  870. /**
  871. * Returns a string representation of this JComboBox. This method
  872. * is intended to be used only for debugging purposes, and the
  873. * content and format of the returned string may vary between
  874. * implementations. The returned string may be empty but may not
  875. * be <code>null</code>.
  876. *
  877. * @return a string representation of this JComboBox.
  878. */
  879. protected String paramString() {
  880. String selectedItemReminderString = (selectedItemReminder != null ?
  881. selectedItemReminder.toString() :
  882. "");
  883. String isEditableString = (isEditable ? "true" : "false");
  884. String lightWeightPopupEnabledString = (lightWeightPopupEnabled ?
  885. "true" : "false");
  886. return super.paramString() +
  887. ",isEditable=" + isEditableString +
  888. ",lightWeightPopupEnabled=" + lightWeightPopupEnabledString +
  889. ",maximumRowCount=" + maximumRowCount +
  890. ",selectedItemReminder=" + selectedItemReminderString;
  891. }
  892. ///////////////////
  893. // Accessiblity support
  894. ///////////////////
  895. /**
  896. * Get the AccessibleContext associated with this JComponent
  897. *
  898. * @return the AccessibleContext of this JComponent
  899. */
  900. public AccessibleContext getAccessibleContext() {
  901. if ( accessibleContext == null ) {
  902. accessibleContext = new AccessibleJComboBox();
  903. }
  904. return accessibleContext;
  905. }
  906. /**
  907. * The class used to obtain the accessible role for this object.
  908. * <p>
  909. * <strong>Warning:</strong>
  910. * Serialized objects of this class will not be compatible with
  911. * future Swing releases. The current serialization support is appropriate
  912. * for short term storage or RMI between applications running the same
  913. * version of Swing. A future release of Swing will provide support for
  914. * long term persistence.
  915. */
  916. protected class AccessibleJComboBox extends AccessibleJComponent
  917. implements AccessibleAction {
  918. /**
  919. * Get the role of this object.
  920. *
  921. * @return an instance of AccessibleRole describing the role of the
  922. * object
  923. * @see AccessibleRole
  924. */
  925. public AccessibleRole getAccessibleRole() {
  926. return AccessibleRole.COMBO_BOX;
  927. }
  928. /**
  929. * Get the AccessibleAction associated with this object if one
  930. * exists. Otherwise return null.
  931. */
  932. public AccessibleAction getAccessibleAction() {
  933. return this;
  934. }
  935. /**
  936. * Return a description of the specified action of the object.
  937. *
  938. * @param i zero-based index of the actions
  939. */
  940. public String getAccessibleActionDescription(int i) {
  941. if (i == 0) {
  942. // [[[PENDING: WDW -- need to provide a localized string]]]
  943. return new String("togglePopup");
  944. }
  945. else {
  946. return null;
  947. }
  948. }
  949. /**
  950. * Returns the number of Actions available in this object.
  951. * If there is more than one, the first one is the "default"
  952. * action.
  953. *
  954. * @return the number of Actions in this object
  955. */
  956. public int getAccessibleActionCount() {
  957. return 1;
  958. }
  959. /**
  960. * Perform the specified Action on the object
  961. *
  962. * @param i zero-based index of actions
  963. * @return true if the the action was performed; else false.
  964. */
  965. public boolean doAccessibleAction(int i) {
  966. if (i == 0) {
  967. setPopupVisible(!isPopupVisible());
  968. return true;
  969. }
  970. else {
  971. return false;
  972. }
  973. }
  974. // public Accessible getAccessibleAt(Point p) {
  975. // Accessible a = getAccessibleChild(1);
  976. // if ( a != null ) {
  977. // return a; // the editor
  978. // }
  979. // else {
  980. // return getAccessibleChild(0); // the list
  981. // }
  982. // }
  983. } // innerclass AccessibleJComboBox
  984. }