1. /*
  2. * @(#)Menu.java 1.71 03/01/23
  3. *
  4. * Copyright 2003 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.71, 01/23/03
  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. public int countItems() {
  187. return countItemsImpl();
  188. }
  189. /*
  190. * This is called by the native code, so client code can't
  191. * be called on the toolkit thread.
  192. */
  193. final int countItemsImpl() {
  194. return items.size();
  195. }
  196. /**
  197. * Gets the item located at the specified index of this menu.
  198. * @param index the position of the item to be returned.
  199. * @return the item located at the specified index.
  200. */
  201. public MenuItem getItem(int index) {
  202. return getItemImpl(index);
  203. }
  204. /*
  205. * This is called by the native code, so client code can't
  206. * be called on the toolkit thread.
  207. */
  208. final MenuItem getItemImpl(int index) {
  209. return (MenuItem)items.elementAt(index);
  210. }
  211. /**
  212. * Adds the specified menu item to this menu. If the
  213. * menu item has been part of another menu, removes it
  214. * from that menu.
  215. *
  216. * @param mi the menu item to be added
  217. * @return the menu item added
  218. * @see java.awt.Menu#insert(java.lang.String, int)
  219. * @see java.awt.Menu#insert(java.awt.MenuItem, int)
  220. */
  221. public MenuItem add(MenuItem mi) {
  222. synchronized (getTreeLock()) {
  223. if (mi.parent != null) {
  224. mi.parent.remove(mi);
  225. }
  226. items.addElement(mi);
  227. mi.parent = this;
  228. MenuPeer peer = (MenuPeer)this.peer;
  229. if (peer != null) {
  230. mi.addNotify();
  231. peer.addItem(mi);
  232. }
  233. return mi;
  234. }
  235. }
  236. /**
  237. * Adds an item with the specified label to this menu.
  238. *
  239. * @param label the text on the item
  240. * @see java.awt.Menu#insert(java.lang.String, int)
  241. * @see java.awt.Menu#insert(java.awt.MenuItem, int)
  242. */
  243. public void add(String label) {
  244. add(new MenuItem(label));
  245. }
  246. /**
  247. * Inserts a menu item into this menu
  248. * at the specified position.
  249. *
  250. * @param menuitem the menu item to be inserted.
  251. * @param index the position at which the menu
  252. * item should be inserted.
  253. * @see java.awt.Menu#add(java.lang.String)
  254. * @see java.awt.Menu#add(java.awt.MenuItem)
  255. * @exception IllegalArgumentException if the value of
  256. * <code>index</code> is less than zero
  257. * @since JDK1.1
  258. */
  259. public void insert(MenuItem menuitem, int index) {
  260. synchronized (getTreeLock()) {
  261. if (index < 0) {
  262. throw new IllegalArgumentException("index less than zero.");
  263. }
  264. int nitems = getItemCount();
  265. Vector tempItems = new Vector();
  266. /* Remove the item at index, nitems-index times
  267. storing them in a temporary vector in the
  268. order they appear on the menu.
  269. */
  270. for (int i = index ; i < nitems; i++) {
  271. tempItems.addElement(getItem(index));
  272. remove(index);
  273. }
  274. add(menuitem);
  275. /* Add the removed items back to the menu, they are
  276. already in the correct order in the temp vector.
  277. */
  278. for (int i = 0; i < tempItems.size() ; i++) {
  279. add((MenuItem)tempItems.elementAt(i));
  280. }
  281. }
  282. }
  283. /**
  284. * Inserts a menu item with the specified label into this menu
  285. * at the specified position. This is a convenience method for
  286. * <code>insert(menuItem, index)</code>.
  287. *
  288. * @param label the text on the item
  289. * @param index the position at which the menu item
  290. * should be inserted
  291. * @see java.awt.Menu#add(java.lang.String)
  292. * @see java.awt.Menu#add(java.awt.MenuItem)
  293. * @exception IllegalArgumentException if the value of
  294. * <code>index</code> is less than zero
  295. * @since JDK1.1
  296. */
  297. public void insert(String label, int index) {
  298. insert(new MenuItem(label), index);
  299. }
  300. /**
  301. * Adds a separator line, or a hypen, to the menu at the current position.
  302. * @see java.awt.Menu#insertSeparator(int)
  303. */
  304. public void addSeparator() {
  305. add("-");
  306. }
  307. /**
  308. * Inserts a separator at the specified position.
  309. * @param index the position at which the
  310. * menu separator should be inserted.
  311. * @exception IllegalArgumentException if the value of
  312. * <code>index</code> is less than 0.
  313. * @see java.awt.Menu#addSeparator
  314. * @since JDK1.1
  315. */
  316. public void insertSeparator(int index) {
  317. synchronized (getTreeLock()) {
  318. if (index < 0) {
  319. throw new IllegalArgumentException("index less than zero.");
  320. }
  321. int nitems = getItemCount();
  322. Vector tempItems = new Vector();
  323. /* Remove the item at index, nitems-index times
  324. storing them in a temporary vector in the
  325. order they appear on the menu.
  326. */
  327. for (int i = index ; i < nitems; i++) {
  328. tempItems.addElement(getItem(index));
  329. remove(index);
  330. }
  331. addSeparator();
  332. /* Add the removed items back to the menu, they are
  333. already in the correct order in the temp vector.
  334. */
  335. for (int i = 0; i < tempItems.size() ; i++) {
  336. add((MenuItem)tempItems.elementAt(i));
  337. }
  338. }
  339. }
  340. /**
  341. * Removes the menu item at the specified index from this menu.
  342. * @param index the position of the item to be removed.
  343. */
  344. public void remove(int index) {
  345. synchronized (getTreeLock()) {
  346. MenuItem mi = getItem(index);
  347. items.removeElementAt(index);
  348. MenuPeer peer = (MenuPeer)this.peer;
  349. if (peer != null) {
  350. mi.removeNotify();
  351. mi.parent = null;
  352. peer.delItem(index);
  353. }
  354. }
  355. }
  356. /**
  357. * Removes the specified menu item from this menu.
  358. * @param item the item to be removed from the menu.
  359. * If <code>item</code> is <code>null</code>
  360. * or is not in this menu, this method does
  361. * nothing.
  362. */
  363. public void remove(MenuComponent item) {
  364. synchronized (getTreeLock()) {
  365. int index = items.indexOf(item);
  366. if (index >= 0) {
  367. remove(index);
  368. }
  369. }
  370. }
  371. /**
  372. * Removes all items from this menu.
  373. * @since JDK1.0.
  374. */
  375. public void removeAll() {
  376. synchronized (getTreeLock()) {
  377. int nitems = getItemCount();
  378. for (int i = nitems-1 ; i >= 0 ; i--) {
  379. remove(i);
  380. }
  381. }
  382. }
  383. /*
  384. * Post an ActionEvent to the target of the MenuPeer
  385. * associated with the specified keyboard event (on
  386. * keydown). Returns true if there is an associated
  387. * keyboard event.
  388. */
  389. boolean handleShortcut(KeyEvent e) {
  390. int nitems = getItemCount();
  391. for (int i = 0 ; i < nitems ; i++) {
  392. MenuItem mi = getItem(i);
  393. if (mi.handleShortcut(e)) {
  394. return true;
  395. }
  396. }
  397. return false;
  398. }
  399. MenuItem getShortcutMenuItem(MenuShortcut s) {
  400. int nitems = getItemCount();
  401. for (int i = 0 ; i < nitems ; i++) {
  402. MenuItem mi = getItem(i).getShortcutMenuItem(s);
  403. if (mi != null) {
  404. return mi;
  405. }
  406. }
  407. return null;
  408. }
  409. synchronized Enumeration shortcuts() {
  410. Vector shortcuts = new Vector();
  411. int nitems = getItemCount();
  412. for (int i = 0 ; i < nitems ; i++) {
  413. MenuItem mi = getItem(i);
  414. if (mi instanceof Menu) {
  415. Enumeration e = ((Menu)mi).shortcuts();
  416. while (e.hasMoreElements()) {
  417. shortcuts.addElement(e.nextElement());
  418. }
  419. } else {
  420. MenuShortcut ms = mi.getShortcut();
  421. if (ms != null) {
  422. shortcuts.addElement(ms);
  423. }
  424. }
  425. }
  426. return shortcuts.elements();
  427. }
  428. void deleteShortcut(MenuShortcut s) {
  429. int nitems = getItemCount();
  430. for (int i = 0 ; i < nitems ; i++) {
  431. getItem(i).deleteShortcut(s);
  432. }
  433. }
  434. /* Serialization support. A MenuContainer is responsible for
  435. * restoring the parent fields of its children.
  436. */
  437. /**
  438. * The menu serialized Data Version.
  439. *
  440. * @serial
  441. */
  442. private int menuSerializedDataVersion = 1;
  443. /**
  444. * Writes default serializable fields to stream.
  445. *
  446. * @param s the <code>ObjectOutputStream</code> to write
  447. * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
  448. * @see #readObject(ObjectInputStream)
  449. */
  450. private void writeObject(java.io.ObjectOutputStream s)
  451. throws java.io.IOException
  452. {
  453. s.defaultWriteObject();
  454. }
  455. /**
  456. * Reads the <code>ObjectInputStream</code>.
  457. * Unrecognized keys or values will be ignored.
  458. *
  459. * @param s the <code>ObjectInputStream</code> to read
  460. * @exception HeadlessException if
  461. * <code>GraphicsEnvironment.isHeadless</code> returns
  462. * <code>true</code>
  463. * @see java.awt.GraphicsEnvironment#isHeadless
  464. * @see #writeObject(ObjectOutputStream)
  465. */
  466. private void readObject(ObjectInputStream s)
  467. throws IOException, ClassNotFoundException, HeadlessException
  468. {
  469. // HeadlessException will be thrown from MenuComponent's readObject
  470. s.defaultReadObject();
  471. for(int i = 0; i < items.size(); i++) {
  472. MenuItem item = (MenuItem)items.elementAt(i);
  473. item.parent = this;
  474. }
  475. }
  476. /**
  477. * Returns a string representing the state of this <code>Menu</code>.
  478. * This method is intended to be used only for debugging purposes, and the
  479. * content and format of the returned string may vary between
  480. * implementations. The returned string may be empty but may not be
  481. * <code>null</code>.
  482. *
  483. * @return the parameter string of this menu
  484. */
  485. public String paramString() {
  486. String str = ",tearOff=" + tearOff+",isHelpMenu=" + isHelpMenu;
  487. return super.paramString() + str;
  488. }
  489. /**
  490. * Initialize JNI field and method IDs
  491. */
  492. private static native void initIDs();
  493. /////////////////
  494. // Accessibility support
  495. ////////////////
  496. /**
  497. * Gets the AccessibleContext associated with this Menu.
  498. * For menus, the AccessibleContext takes the form of an
  499. * AccessibleAWTMenu.
  500. * A new AccessibleAWTMenu instance is created if necessary.
  501. *
  502. * @return an AccessibleAWTMenu that serves as the
  503. * AccessibleContext of this Menu
  504. */
  505. public AccessibleContext getAccessibleContext() {
  506. if (accessibleContext == null) {
  507. accessibleContext = new AccessibleAWTMenu();
  508. }
  509. return accessibleContext;
  510. }
  511. /**
  512. * Inner class of Menu used to provide default support for
  513. * accessibility. This class is not meant to be used directly by
  514. * application developers, but is instead meant only to be
  515. * subclassed by menu component developers.
  516. * <p>
  517. * This class implements accessibility support for the
  518. * <code>Menu</code> class. It provides an implementation of the
  519. * Java Accessibility API appropriate to menu user-interface elements.
  520. */
  521. protected class AccessibleAWTMenu extends AccessibleAWTMenuItem {
  522. /**
  523. * Get the role of this object.
  524. *
  525. * @return an instance of AccessibleRole describing the role of the
  526. * object
  527. */
  528. public AccessibleRole getAccessibleRole() {
  529. return AccessibleRole.MENU;
  530. }
  531. } // class AccessibleAWTMenu
  532. }