1. /*
  2. * @(#)Menu.java 1.75 04/05/18
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.awt;
  8. import java.io.IOException;
  9. import java.io.ObjectInputStream;
  10. import java.util.Vector;
  11. import java.util.Enumeration;
  12. import java.awt.peer.MenuPeer;
  13. import java.awt.event.KeyEvent;
  14. import javax.accessibility.*;
  15. /**
  16. * A <code>Menu</code> object is a pull-down menu component
  17. * that is deployed from a menu bar.
  18. * <p>
  19. * A menu can optionally be a <i>tear-off</i> menu. A tear-off menu
  20. * can be opened and dragged away from its parent menu bar or menu.
  21. * It remains on the screen after the mouse button has been released.
  22. * The mechanism for tearing off a menu is platform dependent, since
  23. * the look and feel of the tear-off menu is determined by its peer.
  24. * On platforms that do not support tear-off menus, the tear-off
  25. * property is ignored.
  26. * <p>
  27. * Each item in a menu must belong to the <code>MenuItem</code>
  28. * class. It can be an instance of <code>MenuItem</code>, a submenu
  29. * (an instance of <code>Menu</code>), or a check box (an instance of
  30. * <code>CheckboxMenuItem</code>).
  31. *
  32. * @version 1.75, 05/18/04
  33. * @author Sami Shaio
  34. * @see java.awt.MenuItem
  35. * @see java.awt.CheckboxMenuItem
  36. * @since JDK1.0
  37. */
  38. public class Menu extends MenuItem implements MenuContainer, Accessible {
  39. static {
  40. /* ensure that the necessary native libraries are loaded */
  41. Toolkit.loadLibraries();
  42. if (!GraphicsEnvironment.isHeadless()) {
  43. initIDs();
  44. }
  45. }
  46. /**
  47. * A vector of the items that will be part of the Menu.
  48. *
  49. * @serial
  50. * @see #countItems()
  51. */
  52. Vector items = new Vector();
  53. /**
  54. * This field indicates whether the menu has the
  55. * tear of property or not. It will be set to
  56. * <code>true</code> if the menu has the tear off
  57. * property and it will be set to <code>false></code>
  58. * if it does not.
  59. * A torn off menu can be deleted by a user when
  60. * it is no longer needed.
  61. *
  62. * @serial
  63. * @see #isTearOff()
  64. */
  65. boolean tearOff;
  66. /**
  67. * This field will be set to <code>true</code>
  68. * if the Menu in question is actually a help
  69. * menu. Otherwise it will be set to <code>
  70. * false</code>.
  71. *
  72. * @serial
  73. */
  74. boolean isHelpMenu;
  75. private static final String base = "menu";
  76. private static int nameCounter = 0;
  77. /*
  78. * JDK 1.1 serialVersionUID
  79. */
  80. private static final long serialVersionUID = -8809584163345499784L;
  81. /**
  82. * Constructs a new menu with an empty label. This menu is not
  83. * a tear-off menu.
  84. * @exception HeadlessException if GraphicsEnvironment.isHeadless()
  85. * returns true.
  86. * @see java.awt.GraphicsEnvironment#isHeadless
  87. * @since JDK1.1
  88. */
  89. public Menu() throws HeadlessException {
  90. this("", false);
  91. }
  92. /**
  93. * Constructs a new menu with the specified label. This menu is not
  94. * a tear-off menu.
  95. * @param label the menu's label in the menu bar, or in
  96. * another menu of which this menu is a submenu.
  97. * @exception HeadlessException if GraphicsEnvironment.isHeadless()
  98. * returns true.
  99. * @see java.awt.GraphicsEnvironment#isHeadless
  100. */
  101. public Menu(String label) throws HeadlessException {
  102. this(label, false);
  103. }
  104. /**
  105. * Constructs a new menu with the specified label,
  106. * indicating whether the menu can be torn off.
  107. * <p>
  108. * Tear-off functionality may not be supported by all
  109. * implementations of AWT. If a particular implementation doesn't
  110. * support tear-off menus, this value is silently ignored.
  111. * @param label the menu's label in the menu bar, or in
  112. * another menu of which this menu is a submenu.
  113. * @param tearOff if <code>true</code>, the menu
  114. * is a tear-off menu.
  115. * @exception HeadlessException if GraphicsEnvironment.isHeadless()
  116. * returns true.
  117. * @see java.awt.GraphicsEnvironment#isHeadless
  118. * @since JDK1.0.
  119. */
  120. public Menu(String label, boolean tearOff) throws HeadlessException {
  121. super(label);
  122. this.tearOff = tearOff;
  123. }
  124. /**
  125. * Construct a name for this MenuComponent. Called by getName() when
  126. * the name is null.
  127. */
  128. String constructComponentName() {
  129. synchronized (getClass()) {
  130. return base + nameCounter++;
  131. }
  132. }
  133. /**
  134. * Creates the menu's peer. The peer allows us to modify the
  135. * appearance of the menu without changing its functionality.
  136. */
  137. public void addNotify() {
  138. synchronized (getTreeLock()) {
  139. if (peer == null)
  140. peer = Toolkit.getDefaultToolkit().createMenu(this);
  141. int nitems = getItemCount();
  142. for (int i = 0 ; i < nitems ; i++) {
  143. MenuItem mi = getItem(i);
  144. mi.parent = this;
  145. mi.addNotify();
  146. }
  147. }
  148. }
  149. /**
  150. * Removes the menu's peer. The peer allows us to modify the appearance
  151. * of the menu without changing its functionality.
  152. */
  153. public void removeNotify() {
  154. synchronized (getTreeLock()) {
  155. int nitems = getItemCount();
  156. for (int i = 0 ; i < nitems ; i++) {
  157. getItem(i).removeNotify();
  158. }
  159. super.removeNotify();
  160. }
  161. }
  162. /**
  163. * Indicates whether this menu is a tear-off menu.
  164. * <p>
  165. * Tear-off functionality may not be supported by all
  166. * implementations of AWT. If a particular implementation doesn't
  167. * support tear-off menus, this value is silently ignored.
  168. * @return <code>true</code> if this is a tear-off menu;
  169. * <code>false</code> otherwise.
  170. */
  171. public boolean isTearOff() {
  172. return tearOff;
  173. }
  174. /**
  175. * Get the number of items in this menu.
  176. * @return the number of items in this menu.
  177. * @since JDK1.1
  178. */
  179. public int getItemCount() {
  180. return countItems();
  181. }
  182. /**
  183. * @deprecated As of JDK version 1.1,
  184. * replaced by <code>getItemCount()</code>.
  185. */
  186. @Deprecated
  187. public int countItems() {
  188. return countItemsImpl();
  189. }
  190. /*
  191. * This is called by the native code, so client code can't
  192. * be called on the toolkit thread.
  193. */
  194. final int countItemsImpl() {
  195. return items.size();
  196. }
  197. /**
  198. * Gets the item located at the specified index of this menu.
  199. * @param index the position of the item to be returned.
  200. * @return the item located at the specified index.
  201. */
  202. public MenuItem getItem(int index) {
  203. return getItemImpl(index);
  204. }
  205. /*
  206. * This is called by the native code, so client code can't
  207. * be called on the toolkit thread.
  208. */
  209. final MenuItem getItemImpl(int index) {
  210. return (MenuItem)items.elementAt(index);
  211. }
  212. /**
  213. * Adds the specified menu item to this menu. If the
  214. * menu item has been part of another menu, removes it
  215. * from that menu.
  216. *
  217. * @param mi the menu item to be added
  218. * @return the menu item added
  219. * @see java.awt.Menu#insert(java.lang.String, int)
  220. * @see java.awt.Menu#insert(java.awt.MenuItem, int)
  221. */
  222. public MenuItem add(MenuItem mi) {
  223. synchronized (getTreeLock()) {
  224. if (mi.parent != null) {
  225. mi.parent.remove(mi);
  226. }
  227. items.addElement(mi);
  228. mi.parent = this;
  229. MenuPeer peer = (MenuPeer)this.peer;
  230. if (peer != null) {
  231. mi.addNotify();
  232. peer.addItem(mi);
  233. }
  234. return mi;
  235. }
  236. }
  237. /**
  238. * Adds an item with the specified label to this menu.
  239. *
  240. * @param label the text on the item
  241. * @see java.awt.Menu#insert(java.lang.String, int)
  242. * @see java.awt.Menu#insert(java.awt.MenuItem, int)
  243. */
  244. public void add(String label) {
  245. add(new MenuItem(label));
  246. }
  247. /**
  248. * Inserts a menu item into this menu
  249. * at the specified position.
  250. *
  251. * @param menuitem the menu item to be inserted.
  252. * @param index the position at which the menu
  253. * item should be inserted.
  254. * @see java.awt.Menu#add(java.lang.String)
  255. * @see java.awt.Menu#add(java.awt.MenuItem)
  256. * @exception IllegalArgumentException if the value of
  257. * <code>index</code> is less than zero
  258. * @since JDK1.1
  259. */
  260. public void insert(MenuItem menuitem, int index) {
  261. synchronized (getTreeLock()) {
  262. if (index < 0) {
  263. throw new IllegalArgumentException("index less than zero.");
  264. }
  265. int nitems = getItemCount();
  266. Vector tempItems = new Vector();
  267. /* Remove the item at index, nitems-index times
  268. storing them in a temporary vector in the
  269. order they appear on the menu.
  270. */
  271. for (int i = index ; i < nitems; i++) {
  272. tempItems.addElement(getItem(index));
  273. remove(index);
  274. }
  275. add(menuitem);
  276. /* Add the removed items back to the menu, they are
  277. already in the correct order in the temp vector.
  278. */
  279. for (int i = 0; i < tempItems.size() ; i++) {
  280. add((MenuItem)tempItems.elementAt(i));
  281. }
  282. }
  283. }
  284. /**
  285. * Inserts a menu item with the specified label into this menu
  286. * at the specified position. This is a convenience method for
  287. * <code>insert(menuItem, index)</code>.
  288. *
  289. * @param label the text on the item
  290. * @param index the position at which the menu item
  291. * should be inserted
  292. * @see java.awt.Menu#add(java.lang.String)
  293. * @see java.awt.Menu#add(java.awt.MenuItem)
  294. * @exception IllegalArgumentException if the value of
  295. * <code>index</code> is less than zero
  296. * @since JDK1.1
  297. */
  298. public void insert(String label, int index) {
  299. insert(new MenuItem(label), index);
  300. }
  301. /**
  302. * Adds a separator line, or a hypen, to the menu at the current position.
  303. * @see java.awt.Menu#insertSeparator(int)
  304. */
  305. public void addSeparator() {
  306. add("-");
  307. }
  308. /**
  309. * Inserts a separator at the specified position.
  310. * @param index the position at which the
  311. * menu separator should be inserted.
  312. * @exception IllegalArgumentException if the value of
  313. * <code>index</code> is less than 0.
  314. * @see java.awt.Menu#addSeparator
  315. * @since JDK1.1
  316. */
  317. public void insertSeparator(int index) {
  318. synchronized (getTreeLock()) {
  319. if (index < 0) {
  320. throw new IllegalArgumentException("index less than zero.");
  321. }
  322. int nitems = getItemCount();
  323. Vector tempItems = new Vector();
  324. /* Remove the item at index, nitems-index times
  325. storing them in a temporary vector in the
  326. order they appear on the menu.
  327. */
  328. for (int i = index ; i < nitems; i++) {
  329. tempItems.addElement(getItem(index));
  330. remove(index);
  331. }
  332. addSeparator();
  333. /* Add the removed items back to the menu, they are
  334. already in the correct order in the temp vector.
  335. */
  336. for (int i = 0; i < tempItems.size() ; i++) {
  337. add((MenuItem)tempItems.elementAt(i));
  338. }
  339. }
  340. }
  341. /**
  342. * Removes the menu item at the specified index from this menu.
  343. * @param index the position of the item to be removed.
  344. */
  345. public void remove(int index) {
  346. synchronized (getTreeLock()) {
  347. MenuItem mi = getItem(index);
  348. items.removeElementAt(index);
  349. MenuPeer peer = (MenuPeer)this.peer;
  350. if (peer != null) {
  351. mi.removeNotify();
  352. mi.parent = null;
  353. peer.delItem(index);
  354. }
  355. }
  356. }
  357. /**
  358. * Removes the specified menu item from this menu.
  359. * @param item the item to be removed from the menu.
  360. * If <code>item</code> is <code>null</code>
  361. * or is not in this menu, this method does
  362. * nothing.
  363. */
  364. public void remove(MenuComponent item) {
  365. synchronized (getTreeLock()) {
  366. int index = items.indexOf(item);
  367. if (index >= 0) {
  368. remove(index);
  369. }
  370. }
  371. }
  372. /**
  373. * Removes all items from this menu.
  374. * @since JDK1.0.
  375. */
  376. public void removeAll() {
  377. synchronized (getTreeLock()) {
  378. int nitems = getItemCount();
  379. for (int i = nitems-1 ; i >= 0 ; i--) {
  380. remove(i);
  381. }
  382. }
  383. }
  384. /*
  385. * Post an ActionEvent to the target of the MenuPeer
  386. * associated with the specified keyboard event (on
  387. * keydown). Returns true if there is an associated
  388. * keyboard event.
  389. */
  390. boolean handleShortcut(KeyEvent e) {
  391. int nitems = getItemCount();
  392. for (int i = 0 ; i < nitems ; i++) {
  393. MenuItem mi = getItem(i);
  394. if (mi.handleShortcut(e)) {
  395. return true;
  396. }
  397. }
  398. return false;
  399. }
  400. MenuItem getShortcutMenuItem(MenuShortcut s) {
  401. int nitems = getItemCount();
  402. for (int i = 0 ; i < nitems ; i++) {
  403. MenuItem mi = getItem(i).getShortcutMenuItem(s);
  404. if (mi != null) {
  405. return mi;
  406. }
  407. }
  408. return null;
  409. }
  410. synchronized Enumeration shortcuts() {
  411. Vector shortcuts = new Vector();
  412. int nitems = getItemCount();
  413. for (int i = 0 ; i < nitems ; i++) {
  414. MenuItem mi = getItem(i);
  415. if (mi instanceof Menu) {
  416. Enumeration e = ((Menu)mi).shortcuts();
  417. while (e.hasMoreElements()) {
  418. shortcuts.addElement(e.nextElement());
  419. }
  420. } else {
  421. MenuShortcut ms = mi.getShortcut();
  422. if (ms != null) {
  423. shortcuts.addElement(ms);
  424. }
  425. }
  426. }
  427. return shortcuts.elements();
  428. }
  429. void deleteShortcut(MenuShortcut s) {
  430. int nitems = getItemCount();
  431. for (int i = 0 ; i < nitems ; i++) {
  432. getItem(i).deleteShortcut(s);
  433. }
  434. }
  435. /* Serialization support. A MenuContainer is responsible for
  436. * restoring the parent fields of its children.
  437. */
  438. /**
  439. * The menu serialized Data Version.
  440. *
  441. * @serial
  442. */
  443. private int menuSerializedDataVersion = 1;
  444. /**
  445. * Writes default serializable fields to stream.
  446. *
  447. * @param s the <code>ObjectOutputStream</code> to write
  448. * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
  449. * @see #readObject(ObjectInputStream)
  450. */
  451. private void writeObject(java.io.ObjectOutputStream s)
  452. throws java.io.IOException
  453. {
  454. s.defaultWriteObject();
  455. }
  456. /**
  457. * Reads the <code>ObjectInputStream</code>.
  458. * Unrecognized keys or values will be ignored.
  459. *
  460. * @param s the <code>ObjectInputStream</code> to read
  461. * @exception HeadlessException if
  462. * <code>GraphicsEnvironment.isHeadless</code> returns
  463. * <code>true</code>
  464. * @see java.awt.GraphicsEnvironment#isHeadless
  465. * @see #writeObject(ObjectOutputStream)
  466. */
  467. private void readObject(ObjectInputStream s)
  468. throws IOException, ClassNotFoundException, HeadlessException
  469. {
  470. // HeadlessException will be thrown from MenuComponent's readObject
  471. s.defaultReadObject();
  472. for(int i = 0; i < items.size(); i++) {
  473. MenuItem item = (MenuItem)items.elementAt(i);
  474. item.parent = this;
  475. }
  476. }
  477. /**
  478. * Returns a string representing the state of this <code>Menu</code>.
  479. * This method is intended to be used only for debugging purposes, and the
  480. * content and format of the returned string may vary between
  481. * implementations. The returned string may be empty but may not be
  482. * <code>null</code>.
  483. *
  484. * @return the parameter string of this menu
  485. */
  486. public String paramString() {
  487. String str = ",tearOff=" + tearOff+",isHelpMenu=" + isHelpMenu;
  488. return super.paramString() + str;
  489. }
  490. /**
  491. * Initialize JNI field and method IDs
  492. */
  493. private static native void initIDs();
  494. /////////////////
  495. // Accessibility support
  496. ////////////////
  497. /**
  498. * Gets the AccessibleContext associated with this Menu.
  499. * For menus, the AccessibleContext takes the form of an
  500. * AccessibleAWTMenu.
  501. * A new AccessibleAWTMenu instance is created if necessary.
  502. *
  503. * @return an AccessibleAWTMenu that serves as the
  504. * AccessibleContext of this Menu
  505. */
  506. public AccessibleContext getAccessibleContext() {
  507. if (accessibleContext == null) {
  508. accessibleContext = new AccessibleAWTMenu();
  509. }
  510. return accessibleContext;
  511. }
  512. /**
  513. * Defined in MenuComponent. Overridden here.
  514. */
  515. int getAccessibleChildIndex(MenuComponent child) {
  516. return items.indexOf(child);
  517. }
  518. /**
  519. * Inner class of Menu used to provide default support for
  520. * accessibility. This class is not meant to be used directly by
  521. * application developers, but is instead meant only to be
  522. * subclassed by menu component developers.
  523. * <p>
  524. * This class implements accessibility support for the
  525. * <code>Menu</code> class. It provides an implementation of the
  526. * Java Accessibility API appropriate to menu user-interface elements.
  527. */
  528. protected class AccessibleAWTMenu extends AccessibleAWTMenuItem
  529. {
  530. /*
  531. * JDK 1.3 serialVersionUID
  532. */
  533. private static final long serialVersionUID = 5228160894980069094L;
  534. /**
  535. * Get the role of this object.
  536. *
  537. * @return an instance of AccessibleRole describing the role of the
  538. * object
  539. */
  540. public AccessibleRole getAccessibleRole() {
  541. return AccessibleRole.MENU;
  542. }
  543. } // class AccessibleAWTMenu
  544. }