1. /*
  2. * @(#)Choice.java 1.54 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 java.awt;
  8. import java.util.*;
  9. import java.awt.peer.ChoicePeer;
  10. import java.awt.event.*;
  11. import java.io.ObjectOutputStream;
  12. import java.io.ObjectInputStream;
  13. import java.io.IOException;
  14. /**
  15. * The <code>Choice</code> class presents a pop-up menu of choices.
  16. * The current choice is displayed as the title of the menu.
  17. * <p>
  18. * The following code example produces a pop-up menu:
  19. * <p>
  20. * <hr><blockquote><pre>
  21. * Choice ColorChooser = new Choice();
  22. * ColorChooser.add("Green");
  23. * ColorChooser.add("Red");
  24. * ColorChooser.add("Blue");
  25. * </pre></blockquote><hr>
  26. * <p>
  27. * After this choice menu has been added to a panel,
  28. * it appears as follows in its normal state:
  29. * <p>
  30. * <img src="doc-files/Choice-1.gif"
  31. * ALIGN=center HSPACE=10 VSPACE=7>
  32. * <p>
  33. * In the picture, <code>"Green"</code> is the current choice.
  34. * Pushing the mouse button down on the object causes a menu to
  35. * appear with the current choice highlighted.
  36. * <p>
  37. * @version 1.54 11/29/01
  38. * @author Sami Shaio
  39. * @author Arthur van Hoff
  40. * @since JDK1.0
  41. */
  42. public class Choice extends Component implements ItemSelectable {
  43. /**
  44. * The items for the Choice.
  45. * This can be a null value.
  46. * @serial
  47. * @see add()
  48. * @see addItem()
  49. * @see getItem()
  50. * @see getItemCount()
  51. * @see insert()
  52. * @see remove()
  53. */
  54. Vector pItems;
  55. /**
  56. * The index of the current choice for this Choice.
  57. * @serial
  58. * @see getSelectedItem
  59. * @see select()
  60. */
  61. int selectedIndex = -1;
  62. transient ItemListener itemListener;
  63. private static final String base = "choice";
  64. private static int nameCounter = 0;
  65. /*
  66. * JDK 1.1 serialVersionUID
  67. */
  68. private static final long serialVersionUID = -4075310674757313071L;
  69. /**
  70. * Creates a new choice menu. The menu initially has no items in it.
  71. * <p>
  72. * By default, the first item added to the choice menu becomes the
  73. * selected item, until a different selection is made by the user
  74. * by calling one of the <code>select</code> methods.
  75. * @see java.awt.Choice#select(int)
  76. * @see java.awt.Choice#select(java.lang.String)
  77. */
  78. public Choice() {
  79. pItems = new Vector();
  80. }
  81. /**
  82. * Construct a name for this component. Called by getName() when the
  83. * name is null.
  84. */
  85. String constructComponentName() {
  86. synchronized (getClass()) {
  87. return base + nameCounter++;
  88. }
  89. }
  90. /**
  91. * Creates the Choice's peer. This peer allows us to change the look
  92. * of the Choice without changing its functionality.
  93. * @see java.awt.Toolkit#createChoice(java.awt.Choice)
  94. * @see java.awt.Component#getToolkit()
  95. */
  96. public void addNotify() {
  97. synchronized (getTreeLock()) {
  98. if (peer == null)
  99. peer = getToolkit().createChoice(this);
  100. super.addNotify();
  101. }
  102. }
  103. /**
  104. * Returns the number of items in this <code>Choice</code> menu.
  105. * @see java.awt.Choice#getItem
  106. * @since JDK1.1
  107. */
  108. public int getItemCount() {
  109. return countItems();
  110. }
  111. /**
  112. * @deprecated As of JDK version 1.1,
  113. * replaced by <code>getItemCount()</code>.
  114. */
  115. public int countItems() {
  116. return pItems.size();
  117. }
  118. /**
  119. * Gets the string at the specified index in this
  120. * <code>Choice</code> menu.
  121. * @param index the index at which to begin.
  122. * @see java.awt.Choice#getItemCount
  123. */
  124. public String getItem(int index) {
  125. return getItemImpl(index);
  126. }
  127. /*
  128. * This is called by the native code, so client code can't
  129. * be called on the toolkit thread.
  130. */
  131. final String getItemImpl(int index) {
  132. return (String)pItems.elementAt(index);
  133. }
  134. /**
  135. * Adds an item to this <code>Choice</code> menu.
  136. * @param item the item to be added
  137. * @exception NullPointerException if the item's value is <code>null</code>.
  138. * @since JDK1.1
  139. */
  140. public void add(String item) {
  141. addItem(item);
  142. }
  143. /**
  144. * Adds an item to this Choice.
  145. * @param item the item to be added
  146. * @exception NullPointerException If the item's value is equal to null.
  147. */
  148. public void addItem(String item) {
  149. synchronized (this) {
  150. addItemNoInvalidate(item);
  151. }
  152. // This could change the preferred size of the Component.
  153. if (valid) {
  154. invalidate();
  155. }
  156. }
  157. /**
  158. * Adds an item to this Choice, but does not invalidate the Choice.
  159. * Client methods must provide their own synchronization before
  160. * invoking this method.
  161. * @param item the item to be added
  162. * @exception NullPointerException If the item's value is equal to null.
  163. */
  164. private void addItemNoInvalidate(String item) {
  165. if (item == null) {
  166. throw new
  167. NullPointerException("cannot add null item to Choice");
  168. }
  169. pItems.addElement(item);
  170. ChoicePeer peer = (ChoicePeer)this.peer;
  171. if (peer != null) {
  172. peer.addItem(item, pItems.size() - 1);
  173. }
  174. if (selectedIndex < 0) {
  175. select(0);
  176. }
  177. }
  178. /**
  179. * Inserts the item into this choice at the specified position.
  180. * @param item the item to be inserted
  181. * @param index the position at which the item should be inserted
  182. * @exception IllegalArgumentException if index is less than 0.
  183. */
  184. public void insert(String item, int index) {
  185. synchronized (this) {
  186. if (index < 0) {
  187. throw new IllegalArgumentException("index less than zero.");
  188. }
  189. int nitems = getItemCount();
  190. Vector tempItems = new Vector();
  191. /* Remove the item at index, nitems-index times
  192. storing them in a temporary vector in the
  193. order they appear on the choice menu.
  194. */
  195. for (int i = index ; i < nitems; i++) {
  196. tempItems.addElement(getItem(index));
  197. removeNoInvalidate(index);
  198. }
  199. addItemNoInvalidate(item);
  200. /* Add the removed items back to the choice menu, they
  201. are already in the correct order in the temp vector.
  202. */
  203. for (int i = 0; i < tempItems.size() ; i++) {
  204. addItemNoInvalidate((String)tempItems.elementAt(i));
  205. }
  206. }
  207. // This could change the preferred size of the Component.
  208. if (valid) {
  209. invalidate();
  210. }
  211. }
  212. /**
  213. * Remove the first occurrence of <code>item</code>
  214. * from the <code>Choice</code> menu.
  215. * @param item the item to remove from this <code>Choice</code> menu.
  216. * @exception IllegalArgumentException if the item doesn't
  217. * exist in the choice menu.
  218. * @since JDK1.1
  219. */
  220. public void remove(String item) {
  221. synchronized (this) {
  222. int index = pItems.indexOf(item);
  223. if (index < 0) {
  224. throw new IllegalArgumentException("item " + item +
  225. " not found in choice");
  226. } else {
  227. removeNoInvalidate(index);
  228. }
  229. }
  230. // This could change the preferred size of the Component.
  231. if (valid) {
  232. invalidate();
  233. }
  234. }
  235. /**
  236. * Removes an item from the choice menu
  237. * at the specified position.
  238. * @param position the position of the item.
  239. * @since JDK1.1
  240. */
  241. public void remove(int position) {
  242. synchronized (this) {
  243. removeNoInvalidate(position);
  244. }
  245. // This could change the preferred size of the Component.
  246. if (valid) {
  247. invalidate();
  248. }
  249. }
  250. /**
  251. * Removes an item from the Choice at the specified position, but
  252. * does not invalidate the Choice. Client methods must provide their
  253. * own synchronization before invoking this method.
  254. * @param position the position of the item.
  255. */
  256. private void removeNoInvalidate(int position) {
  257. pItems.removeElementAt(position);
  258. ChoicePeer peer = (ChoicePeer)this.peer;
  259. if (peer != null) {
  260. peer.remove(position);
  261. }
  262. /* Adjust selectedIndex if selected item was removed. */
  263. if (pItems.size() == 0) {
  264. selectedIndex = -1;
  265. } else if (selectedIndex == position) {
  266. select(0);
  267. } else if (selectedIndex > position) {
  268. select(selectedIndex-1);
  269. }
  270. }
  271. /**
  272. * Removes all items from the choice menu.
  273. * @see java.awt.Choice#remove
  274. * @since JDK1.1
  275. */
  276. public void removeAll() {
  277. synchronized (this) {
  278. int nitems = getItemCount();
  279. for (int i = 0 ; i < nitems ; i++) {
  280. removeNoInvalidate(0);
  281. }
  282. }
  283. // This could change the preferred size of the Component.
  284. if (valid) {
  285. invalidate();
  286. }
  287. }
  288. /**
  289. * Gets a representation of the current choice as a string.
  290. * @return a string representation of the currently
  291. * selected item in this choice menu.
  292. * @see java.awt.Choice#getSelectedIndex
  293. */
  294. public synchronized String getSelectedItem() {
  295. return (selectedIndex >= 0) ? getItem(selectedIndex) : null;
  296. }
  297. /**
  298. * Returns an array (length 1) containing the currently selected
  299. * item. If this choice has no items, returns null.
  300. * @see ItemSelectable
  301. */
  302. public synchronized Object[] getSelectedObjects() {
  303. if (selectedIndex >= 0) {
  304. Object[] items = new Object[1];
  305. items[0] = getItem(selectedIndex);
  306. return items;
  307. }
  308. return null;
  309. }
  310. /**
  311. * Returns the index of the currently selected item.
  312. * @see #getSelectedItem
  313. */
  314. public int getSelectedIndex() {
  315. return selectedIndex;
  316. }
  317. /**
  318. * Sets the selected item in this <code>Choice</code> menu to be the
  319. * item at the specified position.
  320. * @param pos the positon of the selected item.
  321. * @exception IllegalArgumentException if the specified
  322. * position is invalid.
  323. * @see java.awt.Choice#getSelectedItem
  324. * @see java.awt.Choice#getSelectedIndex
  325. */
  326. public synchronized void select(int pos) {
  327. if ((pos >= pItems.size()) || (pos < 0)) {
  328. throw new IllegalArgumentException("illegal Choice item position: " + pos);
  329. }
  330. if (pItems.size() > 0) {
  331. selectedIndex = pos;
  332. ChoicePeer peer = (ChoicePeer)this.peer;
  333. if (peer != null) {
  334. peer.select(pos);
  335. }
  336. }
  337. }
  338. /**
  339. * Sets the selected item in this <code>Choice</code> menu
  340. * to be the item whose name is equal to the specified string.
  341. * If more than one item matches (is equal to) the specified string,
  342. * the one with the smallest index is selected.
  343. * @param str the specified string
  344. * @see java.awt.Choice#getSelectedItem
  345. * @see java.awt.Choice#getSelectedIndex
  346. */
  347. public synchronized void select(String str) {
  348. int index = pItems.indexOf(str);
  349. if (index >= 0) {
  350. select(index);
  351. }
  352. }
  353. /**
  354. * Adds the specified item listener to receive item events from
  355. * this <code>Choice</code> menu.
  356. * If l is null, no exception is thrown and no action is performed.
  357. * @param l the item listener.
  358. * @see java.awt.event.ItemEvent
  359. * @see java.awt.event.ItemListener
  360. * @see java.awt.Choice#removeItemListener
  361. * @since JDK1.1
  362. */
  363. public synchronized void addItemListener(ItemListener l) {
  364. if (l == null) {
  365. return;
  366. }
  367. itemListener = AWTEventMulticaster.add(itemListener, l);
  368. newEventsOnly = true;
  369. }
  370. /**
  371. * Removes the specified item listener so that it no longer receives
  372. * item events from this <code>Choice</code> menu.
  373. * If l is null, no exception is thrown and no action is performed.
  374. * @param l the item listener.
  375. * @see java.awt.event.ItemEvent
  376. * @see java.awt.event.ItemListener
  377. * @see java.awt.Choice#addItemListener
  378. * @since JDK1.1
  379. */
  380. public synchronized void removeItemListener(ItemListener l) {
  381. if (l == null) {
  382. return;
  383. }
  384. itemListener = AWTEventMulticaster.remove(itemListener, l);
  385. }
  386. // REMIND: remove when filtering is done at lower level
  387. boolean eventEnabled(AWTEvent e) {
  388. if (e.id == ItemEvent.ITEM_STATE_CHANGED) {
  389. if ((eventMask & AWTEvent.ITEM_EVENT_MASK) != 0 ||
  390. itemListener != null) {
  391. return true;
  392. }
  393. return false;
  394. }
  395. return super.eventEnabled(e);
  396. }
  397. /**
  398. * Processes events on this choice. If the event is an
  399. * instance of <code>ItemEvent</code>, it invokes the
  400. * <code>processItemEvent</code> method. Otherwise, it calls its
  401. * superclass's <code>processEvent</code> method.
  402. * @param e the event.
  403. * @see java.awt.event.ItemEvent
  404. * @see java.awt.Choice#processItemEvent
  405. * @since JDK1.1
  406. */
  407. protected void processEvent(AWTEvent e) {
  408. if (e instanceof ItemEvent) {
  409. processItemEvent((ItemEvent)e);
  410. return;
  411. }
  412. super.processEvent(e);
  413. }
  414. /**
  415. * Processes item events occurring on this <code>Choice</code>
  416. * menu by dispatching them to any registered
  417. * <code>ItemListener</code> objects.
  418. * <p>
  419. * This method is not called unless item events are
  420. * enabled for this component. Item events are enabled
  421. * when one of the following occurs:
  422. * <p><ul>
  423. * <li>An <code>ItemListener</code> object is registered
  424. * via <code>addItemListener</code>.
  425. * <li>Item events are enabled via <code>enableEvents</code>.
  426. * </ul>
  427. * @param e the item event.
  428. * @see java.awt.event.ItemEvent
  429. * @see java.awt.event.ItemListener
  430. * @see java.awt.Choice#addItemListener
  431. * @see java.awt.Component#enableEvents
  432. * @since JDK1.1
  433. */
  434. protected void processItemEvent(ItemEvent e) {
  435. if (itemListener != null) {
  436. itemListener.itemStateChanged(e);
  437. }
  438. }
  439. /**
  440. * Returns the parameter string representing the state of this
  441. * choice menu. This string is useful for debugging.
  442. * @return the parameter string of this <code>Choice</code> menu.
  443. */
  444. protected String paramString() {
  445. return super.paramString() + ",current=" + getSelectedItem();
  446. }
  447. /* Serialization support.
  448. */
  449. /*
  450. * Choice Serial Data Version.
  451. * @serial
  452. */
  453. private int choiceSerializedDataVersion = 1;
  454. /**
  455. * Writes default serializable fields to stream. Writes
  456. * a list of serializable ItemListener(s) as optional data.
  457. * The non-serializable ItemListner(s) are detected and
  458. * no attempt is made to serialize them.
  459. *
  460. * @serialData Null terminated sequence of 0 or more pairs.
  461. * The pair consists of a String and Object.
  462. * The String indicates the type of object and
  463. * is one of the following :
  464. * itemListenerK indicating and ItemListener object.
  465. *
  466. * @see AWTEventMulticaster.save(ObjectOutputStream, String, EventListener)
  467. * @see java.awt.Component.itemListenerK
  468. */
  469. private void writeObject(ObjectOutputStream s)
  470. throws java.io.IOException
  471. {
  472. s.defaultWriteObject();
  473. AWTEventMulticaster.save(s, itemListenerK, itemListener);
  474. s.writeObject(null);
  475. }
  476. /*
  477. * Read the ObjectInputStream and if it isnt null
  478. * add a listener to receive item events fired
  479. * by the Choice item.
  480. * Unrecognised keys or values will be Ignored.
  481. * @serial
  482. * @see removeActionListener()
  483. * @see addActionListener()
  484. */
  485. private void readObject(ObjectInputStream s)
  486. throws ClassNotFoundException, IOException
  487. {
  488. s.defaultReadObject();
  489. Object keyOrNull;
  490. while(null != (keyOrNull = s.readObject())) {
  491. String key = ((String)keyOrNull).intern();
  492. if (itemListenerK == key)
  493. addItemListener((ItemListener)(s.readObject()));
  494. else // skip value for unrecognized key
  495. s.readObject();
  496. }
  497. }
  498. }