1. /*
  2. * @(#)MenuItem.java 1.68 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.awt.peer.MenuItemPeer;
  12. import java.awt.event.*;
  13. import java.util.EventListener;
  14. import java.io.ObjectOutputStream;
  15. import java.io.ObjectInputStream;
  16. import java.io.IOException;
  17. import javax.accessibility.*;
  18. /**
  19. * All items in a menu must belong to the class
  20. * <code>MenuItem</code>, or one of its subclasses.
  21. * <p>
  22. * The default <code>MenuItem</code> object embodies
  23. * a simple labeled menu item.
  24. * <p>
  25. * This picture of a menu bar shows five menu items:
  26. * <IMG SRC="doc-files/MenuBar-1.gif"
  27. * ALIGN=CENTER HSPACE=10 VSPACE=7>
  28. * <br CLEAR=LEFT>
  29. * The first two items are simple menu items, labeled
  30. * <code>"Basic"</code> and <code>"Simple"</code>.
  31. * Following these two items is a separator, which is itself
  32. * a menu item, created with the label <code>"-"</code>.
  33. * Next is an instance of <code>CheckboxMenuItem</code>
  34. * labeled <code>"Check"</code>. The final menu item is a
  35. * submenu labeled <code>"More Examples"</code>,
  36. * and this submenu is an instance of <code>Menu</code>.
  37. * <p>
  38. * When a menu item is selected, AWT sends an action event to
  39. * the menu item. Since the event is an
  40. * instance of <code>ActionEvent</code>, the <code>processEvent</code>
  41. * method examines the event and passes it along to
  42. * <code>processActionEvent</code>. The latter method redirects the
  43. * event to any <code>ActionListener</code> objects that have
  44. * registered an interest in action events generated by this
  45. * menu item.
  46. * <P>
  47. * Note that the subclass <code>Menu</code> overrides this behavior and
  48. * does not send any event to the frame until one of its subitems is
  49. * selected.
  50. *
  51. * @version 1.68, 04/06/00
  52. * @author Sami Shaio
  53. */
  54. public class MenuItem extends MenuComponent implements Accessible {
  55. static {
  56. /* ensure that the necessary native libraries are loaded */
  57. Toolkit.loadLibraries();
  58. initIDs();
  59. }
  60. /**
  61. * A value to indicate whether a menu item is enabled
  62. * or not. If it is enabled, <code>enabled</code> will
  63. * be set to true. Else <code>enabled</code> will
  64. * be set to false.
  65. *
  66. * @serial
  67. * @see isEnabled()
  68. * @see setEnabled()
  69. */
  70. boolean enabled = true;
  71. /**
  72. * <code>label</code> is the label of a menu item.
  73. * It can be any string.
  74. *
  75. * @serial
  76. * @see getLabel()
  77. * @see setLabel()
  78. */
  79. String label;
  80. /**
  81. * This field indicates the command tha has been issued
  82. * by a particular menu item.
  83. * By default the <code>actionCommand</code>
  84. * is the label of the menu item, unless it has been
  85. * set using setActionCommand.
  86. *
  87. * @serial
  88. * @see setActionCommand()
  89. * @see getActionCommand()
  90. */
  91. String actionCommand;
  92. /**
  93. * The eventMask is ONLY set by subclasses via enableEvents.
  94. * The mask should NOT be set when listeners are registered
  95. * so that we can distinguish the difference between when
  96. * listeners request events and subclasses request them.
  97. *
  98. * @serial
  99. */
  100. long eventMask;
  101. transient ActionListener actionListener;
  102. /**
  103. * A sequence of key stokes that ia associated with
  104. * a menu item.
  105. * Note :in 1.1.2 you must use setActionCommand()
  106. * on a menu item in order for its shortcut to
  107. * work.
  108. *
  109. * @serial
  110. * @see getShortcut()
  111. * @see setShortcut()
  112. * @see deleteShortcut()
  113. */
  114. private MenuShortcut shortcut = null;
  115. private static final String base = "menuitem";
  116. private static int nameCounter = 0;
  117. /*
  118. * JDK 1.1 serialVersionUID
  119. */
  120. private static final long serialVersionUID = -21757335363267194L;
  121. /**
  122. * Constructs a new MenuItem with an empty label and no keyboard
  123. * shortcut.
  124. * @since JDK1.1
  125. */
  126. public MenuItem() {
  127. this("", null);
  128. }
  129. /**
  130. * Constructs a new MenuItem with the specified label
  131. * and no keyboard shortcut. Note that use of "-" in
  132. * a label is reserved to indicate a separator between
  133. * menu items. By default, all menu items except for
  134. * separators are enabled.
  135. * @param label the label for this menu item.
  136. * @since JDK1.0
  137. */
  138. public MenuItem(String label) {
  139. this(label, null);
  140. }
  141. /**
  142. * Create a menu item with an associated keyboard shortcut.
  143. * Note that use of "-" in a label is reserved to indicate
  144. * a separator between menu items. By default, all menu
  145. * items except for separators are enabled.
  146. * @param label the label for this menu item.
  147. * @param s the instance of <code>MenuShortcut</code>
  148. * associated with this menu item.
  149. * @since JDK1.1
  150. */
  151. public MenuItem(String label, MenuShortcut s) {
  152. this.label = label;
  153. this.shortcut = s;
  154. }
  155. /**
  156. * Construct a name for this MenuComponent. Called by getName() when
  157. * the name is null.
  158. */
  159. String constructComponentName() {
  160. synchronized (getClass()) {
  161. return base + nameCounter++;
  162. }
  163. }
  164. /**
  165. * Creates the menu item's peer. The peer allows us to modify the
  166. * appearance of the menu item without changing its functionality.
  167. */
  168. public void addNotify() {
  169. synchronized (getTreeLock()) {
  170. if (peer == null)
  171. peer = Toolkit.getDefaultToolkit().createMenuItem(this);
  172. }
  173. }
  174. /**
  175. * Gets the label for this menu item.
  176. * @return the label of this menu item, or <code>null</code>
  177. if this menu item has no label.
  178. * @see java.awt.MenuItem#setLabel
  179. * @since JDK1.0
  180. */
  181. public String getLabel() {
  182. return label;
  183. }
  184. /**
  185. * Sets the label for this menu item to the specified label.
  186. * @param label the new label, or <code>null</code> for no label.
  187. * @see java.awt.MenuItem#getLabel
  188. * @since JDK1.0
  189. */
  190. public synchronized void setLabel(String label) {
  191. this.label = label;
  192. MenuItemPeer peer = (MenuItemPeer)this.peer;
  193. if (peer != null) {
  194. peer.setLabel(label);
  195. }
  196. }
  197. /**
  198. * Checks whether this menu item is enabled.
  199. * @see java.awt.MenuItem#setEnabled
  200. * @since JDK1.0
  201. */
  202. public boolean isEnabled() {
  203. return enabled;
  204. }
  205. /**
  206. * Sets whether or not this menu item can be chosen.
  207. * @param b if <code>true</code>, enables this menu item;
  208. * if <code>false</code>, disables it.
  209. * @see java.awt.MenuItem#isEnabled
  210. * @since JDK1.1
  211. */
  212. public synchronized void setEnabled(boolean b) {
  213. enable(b);
  214. }
  215. /**
  216. * @deprecated As of JDK version 1.1,
  217. * replaced by <code>setEnabled(boolean)</code>.
  218. */
  219. public synchronized void enable() {
  220. enabled = true;
  221. MenuItemPeer peer = (MenuItemPeer)this.peer;
  222. if (peer != null) {
  223. peer.enable();
  224. }
  225. }
  226. /**
  227. * @deprecated As of JDK version 1.1,
  228. * replaced by <code>setEnabled(boolean)</code>.
  229. */
  230. public void enable(boolean b) {
  231. if (b) {
  232. enable();
  233. } else {
  234. disable();
  235. }
  236. }
  237. /**
  238. * @deprecated As of JDK version 1.1,
  239. * replaced by <code>setEnabled(boolean)</code>.
  240. */
  241. public synchronized void disable() {
  242. enabled = false;
  243. MenuItemPeer peer = (MenuItemPeer)this.peer;
  244. if (peer != null) {
  245. peer.disable();
  246. }
  247. }
  248. /**
  249. * Get the <code>MenuShortcut</code> object associated with this
  250. * menu item,
  251. * @return the menu shortcut associated with this menu item,
  252. * or <code>null</code> if none has been specified.
  253. * @see java.awt.MenuItem#setShortcut
  254. * @since JDK1.1
  255. */
  256. public MenuShortcut getShortcut() {
  257. return shortcut;
  258. }
  259. /**
  260. * Set the <code>MenuShortcut</code> object associated with this
  261. * menu item. If a menu shortcut is already associated with
  262. * this menu item, it is replaced.
  263. * @param s the menu shortcut to associate
  264. * with this menu item.
  265. * @see java.awt.MenuItem#getShortcut
  266. * @since JDK1.1
  267. */
  268. public void setShortcut(MenuShortcut s) {
  269. shortcut = s;
  270. MenuItemPeer peer = (MenuItemPeer)this.peer;
  271. if (peer != null) {
  272. peer.setLabel(label);
  273. }
  274. }
  275. /**
  276. * Delete any <code>MenuShortcut</code> object associated
  277. * with this menu item.
  278. * @since JDK1.1
  279. */
  280. public void deleteShortcut() {
  281. shortcut = null;
  282. MenuItemPeer peer = (MenuItemPeer)this.peer;
  283. if (peer != null) {
  284. peer.setLabel(label);
  285. }
  286. }
  287. /*
  288. * Delete a matching MenuShortcut associated with this MenuItem.
  289. * Used when iterating Menus.
  290. */
  291. void deleteShortcut(MenuShortcut s) {
  292. if (s.equals(shortcut)) {
  293. shortcut = null;
  294. MenuItemPeer peer = (MenuItemPeer)this.peer;
  295. if (peer != null) {
  296. peer.setLabel(label);
  297. }
  298. }
  299. }
  300. /*
  301. * The main goal of this method is to post an appropriate event
  302. * to the event queue when menu shortcut is pressed. However,
  303. * in subclasses this method may do more than just posting
  304. * an event.
  305. */
  306. void doMenuEvent() {
  307. Toolkit.getEventQueue().postEvent(
  308. new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
  309. getActionCommand()));
  310. }
  311. /*
  312. * Post an ActionEvent to the target (on
  313. * keydown). Returns true if there is an associated
  314. * shortcut.
  315. */
  316. boolean handleShortcut(KeyEvent e) {
  317. MenuShortcut s = new MenuShortcut(e.getKeyCode(),
  318. (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
  319. if (s.equals(shortcut) && enabled) {
  320. // MenuShortcut match -- issue an event on keydown.
  321. if (e.getID() == KeyEvent.KEY_PRESSED) {
  322. doMenuEvent();
  323. } else {
  324. // silently eat key release.
  325. }
  326. return true;
  327. }
  328. return false;
  329. }
  330. MenuItem getShortcutMenuItem(MenuShortcut s) {
  331. return (s.equals(shortcut)) ? this : null;
  332. }
  333. /**
  334. * Enables event delivery to this menu item for events
  335. * to be defined by the specified event mask parameter
  336. * <p>
  337. * Since event types are automatically enabled when a listener for
  338. * that type is added to the menu item, this method only needs
  339. * to be invoked by subclasses of <code>MenuItem</code> which desire to
  340. * have the specified event types delivered to <code>processEvent</code>
  341. * regardless of whether a listener is registered.
  342. * @param eventsToEnable the event mask defining the event types.
  343. * @see java.awt.MenuItem#processEvent
  344. * @see java.awt.MenuItem#disableEvents
  345. * @see java.awt.Component#enableEvents
  346. * @since JDK1.1
  347. */
  348. protected final void enableEvents(long eventsToEnable) {
  349. eventMask |= eventsToEnable;
  350. newEventsOnly = true;
  351. }
  352. /**
  353. * Disables event delivery to this menu item for events
  354. * defined by the specified event mask parameter.
  355. * @param eventsToDisable the event mask defining the event types.
  356. * @see java.awt.MenuItem#processEvent
  357. * @see java.awt.MenuItem#enableEvents
  358. * @see java.awt.Component#disableEvents
  359. * @since JDK1.1
  360. */
  361. protected final void disableEvents(long eventsToDisable) {
  362. eventMask &= ~eventsToDisable;
  363. }
  364. /**
  365. * Sets the command name of the action event that is fired
  366. * by this menu item.
  367. * <p>
  368. * By default, the action command is set to the label of
  369. * the menu item.
  370. * @param command the action command to be set
  371. * for this menu item.
  372. * @see java.awt.MenuItem#getActionCommand
  373. * @since JDK1.1
  374. */
  375. public void setActionCommand(String command) {
  376. actionCommand = command;
  377. }
  378. /**
  379. * Gets the command name of the action event that is fired
  380. * by this menu item.
  381. * @see java.awt.MenuItem#setActionCommand
  382. * @since JDK1.1
  383. */
  384. public String getActionCommand() {
  385. return (actionCommand == null? label : actionCommand);
  386. }
  387. /**
  388. * Adds the specified action listener to receive action events
  389. * from this menu item.
  390. * If l is null, no exception is thrown and no action is performed.
  391. *
  392. * @param l the action listener.
  393. * @see java.awt.event.ActionEvent
  394. * @see java.awt.event.ActionListener
  395. * @see java.awt.MenuItem#removeActionListener
  396. * @since JDK1.1
  397. */
  398. public synchronized void addActionListener(ActionListener l) {
  399. if (l == null) {
  400. return;
  401. }
  402. actionListener = AWTEventMulticaster.add(actionListener, l);
  403. newEventsOnly = true;
  404. }
  405. /**
  406. * Removes the specified action listener so it no longer receives
  407. * action events from this menu item.
  408. * If l is null, no exception is thrown and no action is performed.
  409. *
  410. * @param l the action listener.
  411. * @see java.awt.event.ActionEvent
  412. * @see java.awt.event.ActionListener
  413. * @see java.awt.MenuItem#addActionListener
  414. * @since JDK1.1
  415. */
  416. public synchronized void removeActionListener(ActionListener l) {
  417. if (l == null) {
  418. return;
  419. }
  420. actionListener = AWTEventMulticaster.remove(actionListener, l);
  421. }
  422. /**
  423. * Return an array of all the listeners that were added to the MenuItem
  424. * with addXXXListener(), where XXX is the name of the <code>listenerType</code>
  425. * argument. For example, to get all of the ActionListener(s) for the
  426. * given MenuItem <code>m</code>, one would write:
  427. * <pre>
  428. * ActionListener[] als = (ActionListener[])(m.getListeners(ActionListener.class))
  429. * </pre>
  430. * If no such listener list exists, then an empty array is returned.
  431. *
  432. * @param listenerType Type of listeners requested
  433. * @return all of the listeners of the specified type supported by this menu item
  434. * @since 1.3
  435. */
  436. public EventListener[] getListeners(Class listenerType) {
  437. EventListener l = null;
  438. if (listenerType == ActionListener.class) {
  439. l = actionListener;
  440. }
  441. return AWTEventMulticaster.getListeners(l, listenerType);
  442. }
  443. /**
  444. * Processes events on this menu item. If the event is an
  445. * instance of <code>ActionEvent</code>, it invokes
  446. * <code>processActionEvent</code>, another method
  447. * defined by <code>MenuItem</code>.
  448. * <p>
  449. * Currently, menu items only support action events.
  450. * @param e the event.
  451. * @see java.awt.MenuItem#processActionEvent
  452. * @since JDK1.1
  453. */
  454. protected void processEvent(AWTEvent e) {
  455. if (e instanceof ActionEvent) {
  456. processActionEvent((ActionEvent)e);
  457. }
  458. }
  459. // REMIND: remove when filtering is done at lower level
  460. boolean eventEnabled(AWTEvent e) {
  461. if (e.id == ActionEvent.ACTION_PERFORMED) {
  462. if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0 ||
  463. actionListener != null) {
  464. return true;
  465. }
  466. return false;
  467. }
  468. return super.eventEnabled(e);
  469. }
  470. /**
  471. * Processes action events occurring on this menu item,
  472. * by dispatching them to any registered
  473. * <code>ActionListener</code> objects.
  474. * This method is not called unless action events are
  475. * enabled for this component. Action events are enabled
  476. * when one of the following occurs:
  477. * <p><ul>
  478. * <li>An <code>ActionListener</code> object is registered
  479. * via <code>addActionListener</code>.
  480. * <li>Action events are enabled via <code>enableEvents</code>.
  481. * </ul>
  482. * @param e the action event.
  483. * @see java.awt.event.ActionEvent
  484. * @see java.awt.event.ActionListener
  485. * @see java.awt.MenuItem#enableEvents
  486. * @since JDK1.1
  487. */
  488. protected void processActionEvent(ActionEvent e) {
  489. if (actionListener != null) {
  490. actionListener.actionPerformed(e);
  491. }
  492. }
  493. /**
  494. * Returns the parameter string representing the state of this menu
  495. * item. This string is useful for debugging.
  496. * @return the parameter string of this menu item.
  497. * @since JDK1.0
  498. */
  499. public String paramString() {
  500. String str = ",label=" + label;
  501. if (shortcut != null) {
  502. str += ",shortcut=" + shortcut;
  503. }
  504. return super.paramString() + str;
  505. }
  506. /* Serialization support.
  507. */
  508. /**
  509. * Menu item serialized data version.
  510. *
  511. * @serial
  512. */
  513. private int menuItemSerializedDataVersion = 1;
  514. /**
  515. * Writes default serializable fields to stream. Writes
  516. * a list of serializable ItemListener(s) as optional data.
  517. * The non-serializable ItemListner(s) are detected and
  518. * no attempt is made to serialize them.
  519. *
  520. * @serialData Null terminated sequence of 0 or more pairs.
  521. * The pair consists of a String and Object.
  522. * The String indicates the type of object and
  523. * is one of the following :
  524. * itemListenerK indicating and ItemListener object.
  525. *
  526. * @see AWTEventMulticaster.save(ObjectOutputStream, String, EventListener)
  527. * @see java.awt.Component.itemListenerK
  528. */
  529. private void writeObject(ObjectOutputStream s)
  530. throws IOException
  531. {
  532. s.defaultWriteObject();
  533. AWTEventMulticaster.save(s, actionListenerK, actionListener);
  534. s.writeObject(null);
  535. }
  536. /**
  537. * Read the ObjectInputStream and if it isnt null
  538. * add a listener to receive item events fired
  539. * by the Menu Item.
  540. * Unrecognised keys or values will be Ignored.
  541. *
  542. * @see removeActionListener()
  543. * @see addActionListener()
  544. */
  545. private void readObject(ObjectInputStream s)
  546. throws ClassNotFoundException, IOException
  547. {
  548. s.defaultReadObject();
  549. Object keyOrNull;
  550. while(null != (keyOrNull = s.readObject())) {
  551. String key = ((String)keyOrNull).intern();
  552. if (actionListenerK == key)
  553. addActionListener((ActionListener)(s.readObject()));
  554. else // skip value for unrecognized key
  555. s.readObject();
  556. }
  557. }
  558. /**
  559. * Initialize JNI field and method IDs
  560. */
  561. private static native void initIDs();
  562. /////////////////
  563. // Accessibility support
  564. ////////////////
  565. /**
  566. * Gets the AccessibleContext associated with this MenuItem.
  567. * For menu items, the AccessibleContext takes the form of an
  568. * AccessibleAWTMenuItem.
  569. * A new AccessibleAWTMenuItem instance is created if necessary.
  570. *
  571. * @return an AccessibleAWTMenuItem that serves as the
  572. * AccessibleContext of this MenuItem
  573. */
  574. public AccessibleContext getAccessibleContext() {
  575. if (accessibleContext == null) {
  576. accessibleContext = new AccessibleAWTMenuItem();
  577. }
  578. return accessibleContext;
  579. }
  580. /**
  581. * Inner class of MenuItem used to provide default support for
  582. * accessibility. This class is not meant to be used directly by
  583. * application developers, but is instead meant only to be
  584. * subclassed by menu component developers.
  585. * <p>
  586. * This class implements accessibility support for the
  587. * <code>MenuItem</code> class. It provides an implementation of the
  588. * Java Accessibility API appropriate to menu item user-interface elements.
  589. */
  590. protected class AccessibleAWTMenuItem extends AccessibleAWTMenuComponent
  591. implements AccessibleAction, AccessibleValue {
  592. /**
  593. * Get the accessible name of this object.
  594. *
  595. * @return the localized name of the object -- can be null if this
  596. * object does not have a name
  597. */
  598. public String getAccessibleName() {
  599. if (accessibleName != null) {
  600. return accessibleName;
  601. } else {
  602. if (getLabel() == null) {
  603. return super.getAccessibleName();
  604. } else {
  605. return getLabel();
  606. }
  607. }
  608. }
  609. /**
  610. * Get the role of this object.
  611. *
  612. * @return an instance of AccessibleRole describing the role of the
  613. * object
  614. */
  615. public AccessibleRole getAccessibleRole() {
  616. return AccessibleRole.MENU_ITEM;
  617. }
  618. /**
  619. * Get the AccessibleAction associated with this object. In the
  620. * implementation of the Java Accessibility API for this class,
  621. * return this object, which is responsible for implementing the
  622. * AccessibleAction interface on behalf of itself.
  623. *
  624. * @return this object
  625. */
  626. public AccessibleAction getAccessibleAction() {
  627. return this;
  628. }
  629. /**
  630. * Get the AccessibleValue associated with this object. In the
  631. * implementation of the Java Accessibility API for this class,
  632. * return this object, which is responsible for implementing the
  633. * AccessibleValue interface on behalf of itself.
  634. *
  635. * @return this object
  636. */
  637. public AccessibleValue getAccessibleValue() {
  638. return this;
  639. }
  640. /**
  641. * Returns the number of Actions available in this object. The
  642. * default behavior of a menu item is to have one action.
  643. *
  644. * @return 1, the number of Actions in this object
  645. */
  646. public int getAccessibleActionCount() {
  647. return 1;
  648. }
  649. /**
  650. * Return a description of the specified action of the object.
  651. *
  652. * @param i zero-based index of the actions
  653. */
  654. public String getAccessibleActionDescription(int i) {
  655. if (i == 0) {
  656. // [[[PENDING: WDW -- need to provide a localized string]]]
  657. return new String("click");
  658. } else {
  659. return null;
  660. }
  661. }
  662. /**
  663. * Perform the specified Action on the object
  664. *
  665. * @param i zero-based index of actions
  666. * @return true if the action was performed; otherwise false.
  667. */
  668. public boolean doAccessibleAction(int i) {
  669. if (i == 0) {
  670. // Simulate a button click
  671. Toolkit.getEventQueue().postEvent(
  672. new ActionEvent(MenuItem.this,
  673. ActionEvent.ACTION_PERFORMED,
  674. MenuItem.this.getActionCommand()));
  675. return true;
  676. } else {
  677. return false;
  678. }
  679. }
  680. /**
  681. * Get the value of this object as a Number.
  682. *
  683. * @return An Integer of 0 if this isn't selected or an Integer of 1 if
  684. * this is selected.
  685. * @see AbstractButton#isSelected
  686. */
  687. public Number getCurrentAccessibleValue() {
  688. return new Integer(0);
  689. }
  690. /**
  691. * Set the value of this object as a Number.
  692. *
  693. * @return True if the value was set.
  694. */
  695. public boolean setCurrentAccessibleValue(Number n) {
  696. return false;
  697. }
  698. /**
  699. * Get the minimum value of this object as a Number.
  700. *
  701. * @return An Integer of 0.
  702. */
  703. public Number getMinimumAccessibleValue() {
  704. return new Integer(0);
  705. }
  706. /**
  707. * Get the maximum value of this object as a Number.
  708. *
  709. * @return An Integer of 0.
  710. */
  711. public Number getMaximumAccessibleValue() {
  712. return new Integer(0);
  713. }
  714. } // class AccessibleAWTMenuItem
  715. }