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