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