1. /*
  2. * @(#)Choice.java 1.64 00/03/14
  3. *
  4. * Copyright 1995-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.awt;
  11. import java.util.*;
  12. import java.awt.peer.ChoicePeer;
  13. import java.awt.event.*;
  14. import java.util.EventListener;
  15. import java.io.ObjectOutputStream;
  16. import java.io.ObjectInputStream;
  17. import java.io.IOException;
  18. import javax.accessibility.*;
  19. /**
  20. * The <code>Choice</code> class presents a pop-up menu of choices.
  21. * The current choice is displayed as the title of the menu.
  22. * <p>
  23. * The following code example produces a pop-up menu:
  24. * <p>
  25. * <hr><blockquote><pre>
  26. * Choice ColorChooser = new Choice();
  27. * ColorChooser.add("Green");
  28. * ColorChooser.add("Red");
  29. * ColorChooser.add("Blue");
  30. * </pre></blockquote><hr>
  31. * <p>
  32. * After this choice menu has been added to a panel,
  33. * it appears as follows in its normal state:
  34. * <p>
  35. * <img src="doc-files/Choice-1.gif"
  36. * ALIGN=center HSPACE=10 VSPACE=7>
  37. * <p>
  38. * In the picture, <code>"Green"</code> is the current choice.
  39. * Pushing the mouse button down on the object causes a menu to
  40. * appear with the current choice highlighted.
  41. * <p>
  42. * Some native platforms do not support arbitrary resizing of Choice
  43. * components and the behavior of setSize()/getSize() is bound by
  44. * such limitations.
  45. * Native GUI Choice components' size are often bound by such
  46. * attributes as font size and length of items contained within
  47. * the Choice.
  48. * <p>
  49. * @version 1.64 03/14/00
  50. * @author Sami Shaio
  51. * @author Arthur van Hoff
  52. * @since JDK1.0
  53. */
  54. public class Choice extends Component implements ItemSelectable, Accessible {
  55. /**
  56. * The items for the Choice.
  57. * This can be a null value.
  58. * @serial
  59. * @see add()
  60. * @see addItem()
  61. * @see getItem()
  62. * @see getItemCount()
  63. * @see insert()
  64. * @see remove()
  65. */
  66. Vector pItems;
  67. /**
  68. * The index of the current choice for this Choice.
  69. * @serial
  70. * @see getSelectedItem
  71. * @see select()
  72. */
  73. int selectedIndex = -1;
  74. transient ItemListener itemListener;
  75. private static final String base = "choice";
  76. private static int nameCounter = 0;
  77. /*
  78. * JDK 1.1 serialVersionUID
  79. */
  80. private static final long serialVersionUID = -4075310674757313071L;
  81. /**
  82. * Creates a new choice menu. The menu initially has no items in it.
  83. * <p>
  84. * By default, the first item added to the choice menu becomes the
  85. * selected item, until a different selection is made by the user
  86. * by calling one of the <code>select</code> methods.
  87. * @see java.awt.Choice#select(int)
  88. * @see java.awt.Choice#select(java.lang.String)
  89. */
  90. public Choice() {
  91. pItems = new Vector();
  92. }
  93. /**
  94. * Construct a name for this component. Called by getName() when the
  95. * name is null.
  96. */
  97. String constructComponentName() {
  98. synchronized (getClass()) {
  99. return base + nameCounter++;
  100. }
  101. }
  102. /**
  103. * Creates the Choice's peer. This peer allows us to change the look
  104. * of the Choice without changing its functionality.
  105. * @see java.awt.Toolkit#createChoice(java.awt.Choice)
  106. * @see java.awt.Component#getToolkit()
  107. */
  108. public void addNotify() {
  109. synchronized (getTreeLock()) {
  110. if (peer == null)
  111. peer = getToolkit().createChoice(this);
  112. super.addNotify();
  113. }
  114. }
  115. /**
  116. * Returns the number of items in this <code>Choice</code> menu.
  117. * @see java.awt.Choice#getItem
  118. * @since JDK1.1
  119. */
  120. public int getItemCount() {
  121. return countItems();
  122. }
  123. /**
  124. * @deprecated As of JDK version 1.1,
  125. * replaced by <code>getItemCount()</code>.
  126. */
  127. public int countItems() {
  128. return pItems.size();
  129. }
  130. /**
  131. * Gets the string at the specified index in this
  132. * <code>Choice</code> menu.
  133. * @param index the index at which to begin.
  134. * @see java.awt.Choice#getItemCount
  135. */
  136. public String getItem(int index) {
  137. return getItemImpl(index);
  138. }
  139. /*
  140. * This is called by the native code, so client code can't
  141. * be called on the toolkit thread.
  142. */
  143. final String getItemImpl(int index) {
  144. return (String)pItems.elementAt(index);
  145. }
  146. /**
  147. * Adds an item to this <code>Choice</code> menu.
  148. * @param item the item to be added
  149. * @exception NullPointerException if the item's value is <code>null</code>.
  150. * @since JDK1.1
  151. */
  152. public void add(String item) {
  153. addItem(item);
  154. }
  155. /**
  156. * Adds an item to this Choice.
  157. * @param item the item to be added
  158. * @exception NullPointerException If the item's value is equal to null.
  159. */
  160. public void addItem(String item) {
  161. synchronized (this) {
  162. addItemNoInvalidate(item);
  163. }
  164. // This could change the preferred size of the Component.
  165. if (valid) {
  166. invalidate();
  167. }
  168. }
  169. /**
  170. * Adds an item to this Choice, but does not invalidate the Choice.
  171. * Client methods must provide their own synchronization before
  172. * invoking this method.
  173. * @param item the item to be added
  174. * @exception NullPointerException If the item's value is equal to null.
  175. */
  176. private void addItemNoInvalidate(String item) {
  177. if (item == null) {
  178. throw new
  179. NullPointerException("cannot add null item to Choice");
  180. }
  181. pItems.addElement(item);
  182. ChoicePeer peer = (ChoicePeer)this.peer;
  183. if (peer != null) {
  184. peer.addItem(item, pItems.size() - 1);
  185. }
  186. if (selectedIndex < 0) {
  187. select(0);
  188. }
  189. }
  190. /**
  191. * Inserts the item into this choice at the specified position.
  192. * @param item the item to be inserted
  193. * @param index the position at which the item should be inserted
  194. * @exception IllegalArgumentException if index is less than 0.
  195. */
  196. public void insert(String item, int index) {
  197. synchronized (this) {
  198. if (index < 0) {
  199. throw new IllegalArgumentException("index less than zero.");
  200. }
  201. int nitems = getItemCount();
  202. Vector tempItems = new Vector();
  203. /* Remove the item at index, nitems-index times
  204. storing them in a temporary vector in the
  205. order they appear on the choice menu.
  206. */
  207. for (int i = index ; i < nitems; i++) {
  208. tempItems.addElement(getItem(index));
  209. removeNoInvalidate(index);
  210. }
  211. addItemNoInvalidate(item);
  212. /* Add the removed items back to the choice menu, they
  213. are already in the correct order in the temp vector.
  214. */
  215. for (int i = 0; i < tempItems.size() ; i++) {
  216. addItemNoInvalidate((String)tempItems.elementAt(i));
  217. }
  218. }
  219. // This could change the preferred size of the Component.
  220. if (valid) {
  221. invalidate();
  222. }
  223. }
  224. /**
  225. * Remove the first occurrence of <code>item</code>
  226. * from the <code>Choice</code> menu.
  227. * @param item the item to remove from this <code>Choice</code> menu.
  228. * @exception IllegalArgumentException if the item doesn't
  229. * exist in the choice menu.
  230. * @since JDK1.1
  231. */
  232. public void remove(String item) {
  233. synchronized (this) {
  234. int index = pItems.indexOf(item);
  235. if (index < 0) {
  236. throw new IllegalArgumentException("item " + item +
  237. " not found in choice");
  238. } else {
  239. removeNoInvalidate(index);
  240. }
  241. }
  242. // This could change the preferred size of the Component.
  243. if (valid) {
  244. invalidate();
  245. }
  246. }
  247. /**
  248. * Removes an item from the choice menu
  249. * at the specified position.
  250. * @param position the position of the item.
  251. * @since JDK1.1
  252. */
  253. public void remove(int position) {
  254. synchronized (this) {
  255. removeNoInvalidate(position);
  256. }
  257. // This could change the preferred size of the Component.
  258. if (valid) {
  259. invalidate();
  260. }
  261. }
  262. /**
  263. * Removes an item from the Choice at the specified position, but
  264. * does not invalidate the Choice. Client methods must provide their
  265. * own synchronization before invoking this method.
  266. * @param position the position of the item.
  267. */
  268. private void removeNoInvalidate(int position) {
  269. pItems.removeElementAt(position);
  270. ChoicePeer peer = (ChoicePeer)this.peer;
  271. if (peer != null) {
  272. peer.remove(position);
  273. }
  274. /* Adjust selectedIndex if selected item was removed. */
  275. if (pItems.size() == 0) {
  276. selectedIndex = -1;
  277. } else if (selectedIndex == position) {
  278. select(0);
  279. } else if (selectedIndex > position) {
  280. select(selectedIndex-1);
  281. }
  282. }
  283. /**
  284. * Removes all items from the choice menu.
  285. * @see java.awt.Choice#remove
  286. * @since JDK1.1
  287. */
  288. public void removeAll() {
  289. synchronized (this) {
  290. if (peer != null) {
  291. ((ChoicePeer)peer).removeAll();
  292. }
  293. pItems.removeAllElements();
  294. selectedIndex = -1;
  295. }
  296. // This could change the preferred size of the Component.
  297. if (valid) {
  298. invalidate();
  299. }
  300. }
  301. /**
  302. * Gets a representation of the current choice as a string.
  303. * @return a string representation of the currently
  304. * selected item in this choice menu.
  305. * @see java.awt.Choice#getSelectedIndex
  306. */
  307. public synchronized String getSelectedItem() {
  308. return (selectedIndex >= 0) ? getItem(selectedIndex) : null;
  309. }
  310. /**
  311. * Returns an array (length 1) containing the currently selected
  312. * item. If this choice has no items, returns null.
  313. * @see ItemSelectable
  314. */
  315. public synchronized Object[] getSelectedObjects() {
  316. if (selectedIndex >= 0) {
  317. Object[] items = new Object[1];
  318. items[0] = getItem(selectedIndex);
  319. return items;
  320. }
  321. return null;
  322. }
  323. /**
  324. * Returns the index of the currently selected item.
  325. * @see #getSelectedItem
  326. */
  327. public int getSelectedIndex() {
  328. return selectedIndex;
  329. }
  330. /**
  331. * Sets the selected item in this <code>Choice</code> menu to be the
  332. * item at the specified position.
  333. * @param pos the positon of the selected item.
  334. * @exception IllegalArgumentException if the specified
  335. * position is invalid.
  336. * @see java.awt.Choice#getSelectedItem
  337. * @see java.awt.Choice#getSelectedIndex
  338. */
  339. public synchronized void select(int pos) {
  340. if ((pos >= pItems.size()) || (pos < 0)) {
  341. throw new IllegalArgumentException("illegal Choice item position: " + pos);
  342. }
  343. if (pItems.size() > 0) {
  344. selectedIndex = pos;
  345. ChoicePeer peer = (ChoicePeer)this.peer;
  346. if (peer != null) {
  347. peer.select(pos);
  348. }
  349. }
  350. }
  351. /**
  352. * Sets the selected item in this <code>Choice</code> menu
  353. * to be the item whose name is equal to the specified string.
  354. * If more than one item matches (is equal to) the specified string,
  355. * the one with the smallest index is selected.
  356. * @param str the specified string
  357. * @see java.awt.Choice#getSelectedItem
  358. * @see java.awt.Choice#getSelectedIndex
  359. */
  360. public synchronized void select(String str) {
  361. int index = pItems.indexOf(str);
  362. if (index >= 0) {
  363. select(index);
  364. }
  365. }
  366. /**
  367. * Adds the specified item listener to receive item events from
  368. * this <code>Choice</code> menu.
  369. * If l is null, no exception is thrown and no action is performed.
  370. * @param l the item listener.
  371. * @see java.awt.event.ItemEvent
  372. * @see java.awt.event.ItemListener
  373. * @see java.awt.Choice#removeItemListener
  374. * @since JDK1.1
  375. */
  376. public synchronized void addItemListener(ItemListener l) {
  377. if (l == null) {
  378. return;
  379. }
  380. itemListener = AWTEventMulticaster.add(itemListener, l);
  381. newEventsOnly = true;
  382. }
  383. /**
  384. * Removes the specified item listener so that it no longer receives
  385. * item events from this <code>Choice</code> menu.
  386. * If l is null, no exception is thrown and no action is performed.
  387. * @param l the item listener.
  388. * @see java.awt.event.ItemEvent
  389. * @see java.awt.event.ItemListener
  390. * @see java.awt.Choice#addItemListener
  391. * @since JDK1.1
  392. */
  393. public synchronized void removeItemListener(ItemListener l) {
  394. if (l == null) {
  395. return;
  396. }
  397. itemListener = AWTEventMulticaster.remove(itemListener, l);
  398. }
  399. /**
  400. * Return an array of all the listeners that were added to the Choice
  401. * with addXXXListener(), where XXX is the name of the <code>listenerType</code>
  402. * argument. For example, to get all of the ItemListener(s) for the
  403. * given Choice <code>c</code>, one would write:
  404. * <pre>
  405. * ItemListener[] ils = (ItemListener[])(c.getListeners(ItemListener.class))
  406. * </pre>
  407. * If no such listener list exists, then an empty array is returned.
  408. *
  409. * @param listenerType Type of listeners requested
  410. * @return all of the listeners of the specified type supported by this choice
  411. * @since 1.3
  412. */
  413. public EventListener[] getListeners(Class listenerType) {
  414. EventListener l = null;
  415. if (listenerType == ItemListener.class) {
  416. l = itemListener;
  417. } else {
  418. return super.getListeners(listenerType);
  419. }
  420. return AWTEventMulticaster.getListeners(l, listenerType);
  421. }
  422. // REMIND: remove when filtering is done at lower level
  423. boolean eventEnabled(AWTEvent e) {
  424. if (e.id == ItemEvent.ITEM_STATE_CHANGED) {
  425. if ((eventMask & AWTEvent.ITEM_EVENT_MASK) != 0 ||
  426. itemListener != null) {
  427. return true;
  428. }
  429. return false;
  430. }
  431. return super.eventEnabled(e);
  432. }
  433. /**
  434. * Processes events on this choice. If the event is an
  435. * instance of <code>ItemEvent</code>, it invokes the
  436. * <code>processItemEvent</code> method. Otherwise, it calls its
  437. * superclass's <code>processEvent</code> method.
  438. * @param e the event.
  439. * @see java.awt.event.ItemEvent
  440. * @see java.awt.Choice#processItemEvent
  441. * @since JDK1.1
  442. */
  443. protected void processEvent(AWTEvent e) {
  444. if (e instanceof ItemEvent) {
  445. processItemEvent((ItemEvent)e);
  446. return;
  447. }
  448. super.processEvent(e);
  449. }
  450. /**
  451. * Processes item events occurring on this <code>Choice</code>
  452. * menu by dispatching them to any registered
  453. * <code>ItemListener</code> objects.
  454. * <p>
  455. * This method is not called unless item events are
  456. * enabled for this component. Item events are enabled
  457. * when one of the following occurs:
  458. * <p><ul>
  459. * <li>An <code>ItemListener</code> object is registered
  460. * via <code>addItemListener</code>.
  461. * <li>Item events are enabled via <code>enableEvents</code>.
  462. * </ul>
  463. * @param e the item event.
  464. * @see java.awt.event.ItemEvent
  465. * @see java.awt.event.ItemListener
  466. * @see java.awt.Choice#addItemListener
  467. * @see java.awt.Component#enableEvents
  468. * @since JDK1.1
  469. */
  470. protected void processItemEvent(ItemEvent e) {
  471. if (itemListener != null) {
  472. itemListener.itemStateChanged(e);
  473. }
  474. }
  475. /**
  476. * Returns the parameter string representing the state of this
  477. * choice menu. This string is useful for debugging.
  478. * @return the parameter string of this <code>Choice</code> menu.
  479. */
  480. protected String paramString() {
  481. return super.paramString() + ",current=" + getSelectedItem();
  482. }
  483. /* Serialization support.
  484. */
  485. /*
  486. * Choice Serial Data Version.
  487. * @serial
  488. */
  489. private int choiceSerializedDataVersion = 1;
  490. /**
  491. * Writes default serializable fields to stream. Writes
  492. * a list of serializable ItemListener(s) as optional data.
  493. * The non-serializable ItemListner(s) are detected and
  494. * no attempt is made to serialize them.
  495. *
  496. * @serialData Null terminated sequence of 0 or more pairs.
  497. * The pair consists of a String and Object.
  498. * The String indicates the type of object and
  499. * is one of the following :
  500. * itemListenerK indicating and ItemListener object.
  501. *
  502. * @see AWTEventMulticaster.save(ObjectOutputStream, String, EventListener)
  503. * @see java.awt.Component.itemListenerK
  504. */
  505. private void writeObject(ObjectOutputStream s)
  506. throws java.io.IOException
  507. {
  508. s.defaultWriteObject();
  509. AWTEventMulticaster.save(s, itemListenerK, itemListener);
  510. s.writeObject(null);
  511. }
  512. /*
  513. * Read the ObjectInputStream and if it isnt null
  514. * add a listener to receive item events fired
  515. * by the Choice item.
  516. * Unrecognised keys or values will be Ignored.
  517. * @serial
  518. * @see removeActionListener()
  519. * @see addActionListener()
  520. */
  521. private void readObject(ObjectInputStream s)
  522. throws ClassNotFoundException, IOException
  523. {
  524. s.defaultReadObject();
  525. Object keyOrNull;
  526. while(null != (keyOrNull = s.readObject())) {
  527. String key = ((String)keyOrNull).intern();
  528. if (itemListenerK == key)
  529. addItemListener((ItemListener)(s.readObject()));
  530. else // skip value for unrecognized key
  531. s.readObject();
  532. }
  533. }
  534. /////////////////
  535. // Accessibility support
  536. ////////////////
  537. /**
  538. * Gets the AccessibleContext associated with this Choice.
  539. * For Choice components, the AccessibleContext takes the form of an
  540. * AccessibleAWTChoice.
  541. * A new AccessibleAWTChoice instance is created if necessary.
  542. *
  543. * @return an AccessibleAWTChoice that serves as the
  544. * AccessibleContext of this Choice
  545. */
  546. public AccessibleContext getAccessibleContext() {
  547. if (accessibleContext == null) {
  548. accessibleContext = new AccessibleAWTChoice();
  549. }
  550. return accessibleContext;
  551. }
  552. /**
  553. * This class implements accessibility support for the
  554. * <code>Choice</code> class. It provides an implementation of the
  555. * Java Accessibility API appropriate to choice user-interface elements.
  556. */
  557. protected class AccessibleAWTChoice extends AccessibleAWTComponent
  558. implements AccessibleAction {
  559. public AccessibleAWTChoice() {
  560. super();
  561. }
  562. /**
  563. * Get the AccessibleAction associated with this object. In the
  564. * implementation of the Java Accessibility API for this class,
  565. * return this object, which is responsible for implementing the
  566. * AccessibleAction interface on behalf of itself.
  567. *
  568. * @return this object
  569. * @see AccessibleAction
  570. */
  571. public AccessibleAction getAccessibleAction() {
  572. return this;
  573. }
  574. /**
  575. * Get the role of this object.
  576. *
  577. * @return an instance of AccessibleRole describing the role of the
  578. * object
  579. * @see AccessibleRole
  580. */
  581. public AccessibleRole getAccessibleRole() {
  582. return AccessibleRole.COMBO_BOX;
  583. }
  584. /**
  585. * Returns the number of accessible actions available in this object
  586. * If there are more than one, the first one is considered the "default"
  587. * action of the object.
  588. *
  589. * @return the zero-based number of Actions in this object
  590. */
  591. public int getAccessibleActionCount() {
  592. return 0; // To be fully implemented in a future release
  593. }
  594. /**
  595. * Returns a description of the specified action of the object.
  596. *
  597. * @param i zero-based index of the actions
  598. * @return a String description of the action
  599. * @see #getAccessibleActionCount
  600. */
  601. public String getAccessibleActionDescription(int i) {
  602. return null; // To be fully implemented in a future release
  603. }
  604. /**
  605. * Perform the specified Action on the object
  606. *
  607. * @param i zero-based index of actions
  608. * @return true if the action was performed; otherwise false.
  609. * @see #getAccessibleActionCount
  610. */
  611. public boolean doAccessibleAction(int i) {
  612. return false; // To be fully implemented in a future release
  613. }
  614. } // inner class AccessibleAWTChoice
  615. }