1. /*
  2. * @(#)MenuItem.java 1.60 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.awt.peer.MenuItemPeer;
  9. import java.awt.event.*;
  10. import java.io.ObjectOutputStream;
  11. import java.io.ObjectInputStream;
  12. import java.io.IOException;
  13. /**
  14. * All items in a menu must belong to the class
  15. * <code>MenuItem</code>, or one of its subclasses.
  16. * <p>
  17. * The default <code>MenuItem</code> object embodies
  18. * a simple labeled menu item.
  19. * <p>
  20. * This picture of a menu bar shows five menu items:
  21. * <IMG SRC="doc-files/MenuBar-1.gif"
  22. * ALIGN=CENTER HSPACE=10 VSPACE=7>
  23. * <br CLEAR=LEFT>
  24. * The first two items are simple menu items, labeled
  25. * <code>"Basic"</code> and <code>"Simple"</code>.
  26. * Following these two items is a separator, which is itself
  27. * a menu item, created with the label <code>"-"</code>.
  28. * Next is an instance of <code>CheckboxMenuItem</code>
  29. * labeled <code>"Check"</code>. The final menu item is a
  30. * submenu labeled <code>"More Examples"</code>,
  31. * and this submenu is an instance of <code>Menu</code>.
  32. * <p>
  33. * When a menu item is selected, AWT sends an action event to
  34. * the menu item. Since the event is an
  35. * instance of <code>ActionEvent</code>, the <code>processEvent</code>
  36. * method examines the event and passes it along to
  37. * <code>processActionEvent</code>. The latter method redirects the
  38. * event to any <code>ActionListener</code> objects that have
  39. * registered an interest in action events generated by this
  40. * menu item.
  41. * <P>
  42. * Note that the subclass <code>Menu</code> overrides this behavior and
  43. * does not send any event to the frame until one of its subitems is
  44. * selected.
  45. *
  46. * @version 1.60, 11/29/01
  47. * @author Sami Shaio
  48. */
  49. public class MenuItem extends MenuComponent {
  50. static {
  51. /* ensure that the necessary native libraries are loaded */
  52. Toolkit.loadLibraries();
  53. initIDs();
  54. }
  55. /**
  56. * A value to indicate whether a menu item is enabled
  57. * or not. If it is enabled, <code>enabled</code> will
  58. * be set to true. Else <code>enabled</code> will
  59. * be set to false.
  60. *
  61. * @serial
  62. * @see isEnabled()
  63. * @see setEnabled()
  64. */
  65. boolean enabled = true;
  66. /**
  67. * <code>label</code> is the label of a menu item.
  68. * It can be any string.
  69. *
  70. * @serial
  71. * @see getLabel()
  72. * @see setLabel()
  73. */
  74. String label;
  75. /**
  76. * This field indicates the command tha has been issued
  77. * by a particular menu item.
  78. * By default the <code>actionCommand</code>
  79. * is the label of the menu item, unless it has been
  80. * set using setActionCommand.
  81. *
  82. * @serial
  83. * @see setActionCommand()
  84. * @see getActionCommand()
  85. */
  86. String actionCommand;
  87. /**
  88. * The eventMask is ONLY set by subclasses via enableEvents.
  89. * The mask should NOT be set when listeners are registered
  90. * so that we can distinguish the difference between when
  91. * listeners request events and subclasses request them.
  92. *
  93. * @serial
  94. */
  95. long eventMask;
  96. transient ActionListener actionListener;
  97. /**
  98. * A sequence of key stokes that ia associated with
  99. * a menu item.
  100. * Note :in 1.1.2 you must use setActionCommand()
  101. * on a menu item in order for its shortcut to
  102. * work.
  103. *
  104. * @serial
  105. * @see getShortcut()
  106. * @see setShortcut()
  107. * @see deleteShortcut()
  108. */
  109. private MenuShortcut shortcut = null;
  110. private static final String base = "menuitem";
  111. private static int nameCounter = 0;
  112. /*
  113. * JDK 1.1 serialVersionUID
  114. */
  115. private static final long serialVersionUID = -21757335363267194L;
  116. /**
  117. * Constructs a new MenuItem with an empty label and no keyboard
  118. * shortcut.
  119. * @since JDK1.1
  120. */
  121. public MenuItem() {
  122. this("", null);
  123. }
  124. /**
  125. * Constructs a new MenuItem with the specified label
  126. * and no keyboard shortcut. Note that use of "-" in
  127. * a label is reserved to indicate a separator between
  128. * menu items. By default, all menu items except for
  129. * separators are enabled.
  130. * @param label the label for this menu item.
  131. * @since JDK1.0
  132. */
  133. public MenuItem(String label) {
  134. this(label, null);
  135. }
  136. /**
  137. * Create a menu item with an associated keyboard shortcut.
  138. * Note that use of "-" in a label is reserved to indicate
  139. * a separator between menu items. By default, all menu
  140. * items except for separators are enabled.
  141. * @param label the label for this menu item.
  142. * @param s the instance of <code>MenuShortcut</code>
  143. * associated with this menu item.
  144. * @since JDK1.1
  145. */
  146. public MenuItem(String label, MenuShortcut s) {
  147. this.label = label;
  148. this.shortcut = s;
  149. }
  150. /**
  151. * Construct a name for this MenuComponent. Called by getName() when
  152. * the name is null.
  153. */
  154. String constructComponentName() {
  155. synchronized (getClass()) {
  156. return base + nameCounter++;
  157. }
  158. }
  159. /**
  160. * Creates the menu item's peer. The peer allows us to modify the
  161. * appearance of the menu item without changing its functionality.
  162. */
  163. public void addNotify() {
  164. synchronized (getTreeLock()) {
  165. if (peer == null)
  166. peer = Toolkit.getDefaultToolkit().createMenuItem(this);
  167. }
  168. }
  169. /**
  170. * Gets the label for this menu item.
  171. * @return the label of this menu item, or <code>null</code>
  172. if this menu item has no label.
  173. * @see java.awt.MenuItem#setLabel
  174. * @since JDK1.0
  175. */
  176. public String getLabel() {
  177. return label;
  178. }
  179. /**
  180. * Sets the label for this menu item to the specified label.
  181. * @param label the new label, or <code>null</code> for no label.
  182. * @see java.awt.MenuItem#getLabel
  183. * @since JDK1.0
  184. */
  185. public synchronized void setLabel(String label) {
  186. this.label = label;
  187. MenuItemPeer peer = (MenuItemPeer)this.peer;
  188. if (peer != null) {
  189. peer.setLabel(label);
  190. }
  191. }
  192. /**
  193. * Checks whether this menu item is enabled.
  194. * @see java.awt.MenuItem#setEnabled
  195. * @since JDK1.0
  196. */
  197. public boolean isEnabled() {
  198. return enabled;
  199. }
  200. /**
  201. * Sets whether or not this menu item can be chosen.
  202. * @param b if <code>true</code>, enables this menu item;
  203. * if <code>false</code>, disables it.
  204. * @see java.awt.MenuItem#isEnabled
  205. * @since JDK1.1
  206. */
  207. public synchronized void setEnabled(boolean b) {
  208. enable(b);
  209. }
  210. /**
  211. * @deprecated As of JDK version 1.1,
  212. * replaced by <code>setEnabled(boolean)</code>.
  213. */
  214. public synchronized void enable() {
  215. enabled = true;
  216. MenuItemPeer peer = (MenuItemPeer)this.peer;
  217. if (peer != null) {
  218. peer.enable();
  219. }
  220. }
  221. /**
  222. * @deprecated As of JDK version 1.1,
  223. * replaced by <code>setEnabled(boolean)</code>.
  224. */
  225. public void enable(boolean b) {
  226. if (b) {
  227. enable();
  228. } else {
  229. disable();
  230. }
  231. }
  232. /**
  233. * @deprecated As of JDK version 1.1,
  234. * replaced by <code>setEnabled(boolean)</code>.
  235. */
  236. public synchronized void disable() {
  237. enabled = false;
  238. MenuItemPeer peer = (MenuItemPeer)this.peer;
  239. if (peer != null) {
  240. peer.disable();
  241. }
  242. }
  243. /**
  244. * Get the <code>MenuShortcut</code> object associated with this
  245. * menu item,
  246. * @return the menu shortcut associated with this menu item,
  247. * or <code>null</code> if none has been specified.
  248. * @see java.awt.MenuItem#setShortcut
  249. * @since JDK1.1
  250. */
  251. public MenuShortcut getShortcut() {
  252. return shortcut;
  253. }
  254. /**
  255. * Set the <code>MenuShortcut</code> object associated with this
  256. * menu item. If a menu shortcut is already associated with
  257. * this menu item, it is replaced.
  258. * @param s the menu shortcut to associate
  259. * with this menu item.
  260. * @see java.awt.MenuItem#getShortcut
  261. * @since JDK1.1
  262. */
  263. public void setShortcut(MenuShortcut s) {
  264. shortcut = s;
  265. MenuItemPeer peer = (MenuItemPeer)this.peer;
  266. if (peer != null) {
  267. peer.setLabel(label);
  268. }
  269. }
  270. /**
  271. * Delete any <code>MenuShortcut</code> object associated
  272. * with this menu item.
  273. * @since JDK1.1
  274. */
  275. public void deleteShortcut() {
  276. shortcut = null;
  277. MenuItemPeer peer = (MenuItemPeer)this.peer;
  278. if (peer != null) {
  279. peer.setLabel(label);
  280. }
  281. }
  282. /*
  283. * Delete a matching MenuShortcut associated with this MenuItem.
  284. * Used when iterating Menus.
  285. */
  286. void deleteShortcut(MenuShortcut s) {
  287. if (s.equals(shortcut)) {
  288. shortcut = null;
  289. MenuItemPeer peer = (MenuItemPeer)this.peer;
  290. if (peer != null) {
  291. peer.setLabel(label);
  292. }
  293. }
  294. }
  295. /*
  296. * Post an ActionEvent to the target (on
  297. * keydown). Returns true if there is an associated
  298. * shortcut.
  299. */
  300. boolean handleShortcut(KeyEvent e) {
  301. MenuShortcut s = new MenuShortcut(e.getKeyCode(),
  302. (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
  303. if (s.equals(shortcut) && enabled) {
  304. // MenuShortcut match -- issue an event on keydown.
  305. if (e.getID() == KeyEvent.KEY_PRESSED) {
  306. Toolkit.getEventQueue().postEvent(
  307. new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
  308. getActionCommand()));
  309. } else {
  310. // silently eat key release.
  311. }
  312. return true;
  313. }
  314. return false;
  315. }
  316. MenuItem getShortcutMenuItem(MenuShortcut s) {
  317. return (s.equals(shortcut)) ? this : null;
  318. }
  319. /**
  320. * Enables event delivery to this menu item for events
  321. * to be defined by the specified event mask parameter
  322. * <p>
  323. * Since event types are automatically enabled when a listener for
  324. * that type is added to the menu item, this method only needs
  325. * to be invoked by subclasses of <code>MenuItem</code> which desire to
  326. * have the specified event types delivered to <code>processEvent</code>
  327. * regardless of whether a listener is registered.
  328. * @param eventsToEnable the event mask defining the event types.
  329. * @see java.awt.MenuItem#processEvent
  330. * @see java.awt.MenuItem#disableEvents
  331. * @see java.awt.Component#enableEvents
  332. * @since JDK1.1
  333. */
  334. protected final void enableEvents(long eventsToEnable) {
  335. eventMask |= eventsToEnable;
  336. newEventsOnly = true;
  337. }
  338. /**
  339. * Disables event delivery to this menu item for events
  340. * defined by the specified event mask parameter.
  341. * @param eventsToDisable the event mask defining the event types.
  342. * @see java.awt.MenuItem#processEvent
  343. * @see java.awt.MenuItem#enableEvents
  344. * @see java.awt.Component#disableEvents
  345. * @since JDK1.1
  346. */
  347. protected final void disableEvents(long eventsToDisable) {
  348. eventMask &= ~eventsToDisable;
  349. }
  350. /**
  351. * Sets the command name of the action event that is fired
  352. * by this menu item.
  353. * <p>
  354. * By default, the action command is set to the label of
  355. * the menu item.
  356. * @param command the action command to be set
  357. * for this menu item.
  358. * @see java.awt.MenuItem#getActionCommand
  359. * @since JDK1.1
  360. */
  361. public void setActionCommand(String command) {
  362. actionCommand = command;
  363. }
  364. /**
  365. * Gets the command name of the action event that is fired
  366. * by this menu item.
  367. * @see java.awt.MenuItem#setActionCommand
  368. * @since JDK1.1
  369. */
  370. public String getActionCommand() {
  371. return (actionCommand == null? label : actionCommand);
  372. }
  373. /**
  374. * Adds the specified action listener to receive action events
  375. * from this menu item.
  376. * If l is null, no exception is thrown and no action is performed.
  377. *
  378. * @param l the action listener.
  379. * @see java.awt.event.ActionEvent
  380. * @see java.awt.event.ActionListener
  381. * @see java.awt.MenuItem#removeActionListener
  382. * @since JDK1.1
  383. */
  384. public synchronized void addActionListener(ActionListener l) {
  385. if (l == null) {
  386. return;
  387. }
  388. actionListener = AWTEventMulticaster.add(actionListener, l);
  389. newEventsOnly = true;
  390. }
  391. /**
  392. * Removes the specified action listener so it no longer receives
  393. * action events from this menu item.
  394. * If l is null, no exception is thrown and no action is performed.
  395. *
  396. * @param l the action listener.
  397. * @see java.awt.event.ActionEvent
  398. * @see java.awt.event.ActionListener
  399. * @see java.awt.MenuItem#addActionListener
  400. * @since JDK1.1
  401. */
  402. public synchronized void removeActionListener(ActionListener l) {
  403. if (l == null) {
  404. return;
  405. }
  406. actionListener = AWTEventMulticaster.remove(actionListener, l);
  407. }
  408. /**
  409. * Processes events on this menu item. If the event is an
  410. * instance of <code>ActionEvent</code>, it invokes
  411. * <code>processActionEvent</code>, another method
  412. * defined by <code>MenuItem</code>.
  413. * <p>
  414. * Currently, menu items only support action events.
  415. * @param e the event.
  416. * @see java.awt.MenuItem#processActionEvent
  417. * @since JDK1.1
  418. */
  419. protected void processEvent(AWTEvent e) {
  420. if (e instanceof ActionEvent) {
  421. processActionEvent((ActionEvent)e);
  422. }
  423. }
  424. // REMIND: remove when filtering is done at lower level
  425. boolean eventEnabled(AWTEvent e) {
  426. if (e.id == ActionEvent.ACTION_PERFORMED) {
  427. if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0 ||
  428. actionListener != null) {
  429. return true;
  430. }
  431. return false;
  432. }
  433. return super.eventEnabled(e);
  434. }
  435. /**
  436. * Processes action events occurring on this menu item,
  437. * by dispatching them to any registered
  438. * <code>ActionListener</code> objects.
  439. * This method is not called unless action events are
  440. * enabled for this component. Action events are enabled
  441. * when one of the following occurs:
  442. * <p><ul>
  443. * <li>An <code>ActionListener</code> object is registered
  444. * via <code>addActionListener</code>.
  445. * <li>Action events are enabled via <code>enableEvents</code>.
  446. * </ul>
  447. * @param e the action event.
  448. * @see java.awt.event.ActionEvent
  449. * @see java.awt.event.ActionListener
  450. * @see java.awt.MenuItem#enableEvents
  451. * @since JDK1.1
  452. */
  453. protected void processActionEvent(ActionEvent e) {
  454. if (actionListener != null) {
  455. actionListener.actionPerformed(e);
  456. }
  457. }
  458. /**
  459. * Returns the parameter string representing the state of this menu
  460. * item. This string is useful for debugging.
  461. * @return the parameter string of this menu item.
  462. * @since JDK1.0
  463. */
  464. public String paramString() {
  465. String str = ",label=" + label;
  466. if (shortcut != null) {
  467. str += ",shortcut=" + shortcut;
  468. }
  469. return super.paramString() + str;
  470. }
  471. /* Serialization support.
  472. */
  473. /**
  474. * Menu item serialized data version.
  475. *
  476. * @serial
  477. */
  478. private int menuItemSerializedDataVersion = 1;
  479. /**
  480. * Writes default serializable fields to stream. Writes
  481. * a list of serializable ItemListener(s) as optional data.
  482. * The non-serializable ItemListner(s) are detected and
  483. * no attempt is made to serialize them.
  484. *
  485. * @serialData Null terminated sequence of 0 or more pairs.
  486. * The pair consists of a String and Object.
  487. * The String indicates the type of object and
  488. * is one of the following :
  489. * itemListenerK indicating and ItemListener object.
  490. *
  491. * @see AWTEventMulticaster.save(ObjectOutputStream, String, EventListener)
  492. * @see java.awt.Component.itemListenerK
  493. */
  494. private void writeObject(ObjectOutputStream s)
  495. throws IOException
  496. {
  497. s.defaultWriteObject();
  498. AWTEventMulticaster.save(s, actionListenerK, actionListener);
  499. s.writeObject(null);
  500. }
  501. /**
  502. * Read the ObjectInputStream and if it isnt null
  503. * add a listener to receive item events fired
  504. * by the Menu Item.
  505. * Unrecognised keys or values will be Ignored.
  506. *
  507. * @see removeActionListener()
  508. * @see addActionListener()
  509. */
  510. private void readObject(ObjectInputStream s)
  511. throws ClassNotFoundException, IOException
  512. {
  513. s.defaultReadObject();
  514. Object keyOrNull;
  515. while(null != (keyOrNull = s.readObject())) {
  516. String key = ((String)keyOrNull).intern();
  517. if (actionListenerK == key)
  518. addActionListener((ActionListener)(s.readObject()));
  519. else // skip value for unrecognized key
  520. s.readObject();
  521. }
  522. }
  523. /**
  524. * Initialize JNI field and method IDs
  525. */
  526. private static native void initIDs();
  527. }