1. /*
  2. * @(#)JMenuItem.java 1.110 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 javax.swing;
  8. import java.util.EventListener;
  9. import java.awt.*;
  10. import java.awt.event.*;
  11. import java.awt.image.*;
  12. import java.beans.PropertyChangeEvent;
  13. import java.beans.PropertyChangeListener;
  14. import java.io.Serializable;
  15. import java.io.ObjectOutputStream;
  16. import java.io.ObjectInputStream;
  17. import java.io.IOException;
  18. import javax.swing.plaf.*;
  19. import javax.swing.plaf.basic.*;
  20. import javax.swing.event.*;
  21. import javax.accessibility.*;
  22. /**
  23. * An implementation of an item in a menu. A menu item is essentially a button
  24. * sitting in a list. When the user selects the "button", the action
  25. * associated with the menu item is performed. A <code>JMenuItem</code>
  26. * contained in a <code>JPopupMenu</code> performs exactly that function.
  27. * <p>
  28. * For further documentation and for examples, see
  29. * <a
  30. href="http://java.sun.com/docs/books/tutorial/uiswing/components/menu.html">How to Use Menus</a>
  31. * in <em>The Java Tutorial.</em>
  32. * For the keyboard keys used by this component in the standard Look and
  33. * Feel (L&F) renditions, see the
  34. * <a href="doc-files/Key-Index.html#JMenuItem"><code>JMenuItem</code> key assignments</a>.
  35. * <p>
  36. * <strong>Warning:</strong>
  37. * Serialized objects of this class will not be compatible with
  38. * future Swing releases. The current serialization support is
  39. * appropriate for short term storage or RMI between applications running
  40. * the same version of Swing. As of 1.4, support for long term storage
  41. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  42. * has been added to the <code>java.beans</code> package.
  43. * Please see {@link java.beans.XMLEncoder}.
  44. *
  45. * @beaninfo
  46. * attribute: isContainer false
  47. * description: An item which can be selected in a menu.
  48. *
  49. * @version 1.110 01/23/03
  50. * @author Georges Saab
  51. * @author David Karlton
  52. * @see JPopupMenu
  53. * @see JMenu
  54. * @see JCheckBoxMenuItem
  55. * @see JRadioButtonMenuItem
  56. */
  57. public class JMenuItem extends AbstractButton implements Accessible,MenuElement {
  58. /**
  59. * @see #getUIClassID
  60. * @see #readObject
  61. */
  62. private static final String uiClassID = "MenuItemUI";
  63. /* diagnostic aids -- should be false for production builds. */
  64. private static final boolean TRACE = false; // trace creates and disposes
  65. private static final boolean VERBOSE = false; // show reuse hits/misses
  66. private static final boolean DEBUG = false; // show bad params, misc.
  67. private boolean isMouseDragged = false;
  68. /**
  69. * Creates a <code>JMenuItem</code> with no set text or icon.
  70. */
  71. public JMenuItem() {
  72. this(null, (Icon)null);
  73. }
  74. /**
  75. * Creates a <code>JMenuItem</code> with the specified icon.
  76. *
  77. * @param icon the icon of the <code>JMenuItem</code>
  78. */
  79. public JMenuItem(Icon icon) {
  80. this(null, icon);
  81. }
  82. /**
  83. * Creates a <code>JMenuItem</code> with the specified text.
  84. *
  85. * @param text the text of the <code>JMenuItem</code>
  86. */
  87. public JMenuItem(String text) {
  88. this(text, (Icon)null);
  89. }
  90. /**
  91. * Creates a menu item whose properties are taken from the
  92. * specified <code>Action</code>.
  93. *
  94. * @param a the action of the <code>JMenuItem</code>
  95. * @since 1.3
  96. */
  97. public JMenuItem(Action a) {
  98. this();
  99. setAction(a);
  100. }
  101. /**
  102. * Creates a <code>JMenuItem</code> with the specified text and icon.
  103. *
  104. * @param text the text of the <code>JMenuItem</code>
  105. * @param icon the icon of the <code>JMenuItem</code>
  106. */
  107. public JMenuItem(String text, Icon icon) {
  108. setModel(new DefaultButtonModel());
  109. init(text, icon);
  110. initFocusability();
  111. }
  112. /**
  113. * Creates a <code>JMenuItem</code> with the specified text and
  114. * keyboard mnemonic.
  115. *
  116. * @param text the text of the <code>JMenuItem</code>
  117. * @param mnemonic the keyboard mnemonic for the <code>JMenuItem</code>
  118. */
  119. public JMenuItem(String text, int mnemonic) {
  120. setModel(new DefaultButtonModel());
  121. init(text, null);
  122. setMnemonic(mnemonic);
  123. initFocusability();
  124. }
  125. /**
  126. * Inititalizes the focusability of the the <code>JMenuItem</code>.
  127. * <code>JMenuItem</code>'s are focusable, but subclasses may
  128. * want to be, this provides them the opportunity to override this
  129. * and invoke something else, or nothing at all. Refer to
  130. * {@link javax.swing.JMenu#initFocusability} for the motivation of
  131. * this.
  132. */
  133. void initFocusability() {
  134. setFocusable(false);
  135. }
  136. /**
  137. * Initializes the menu item with the specified text and icon.
  138. *
  139. * @param text the text of the <code>JMenuItem</code>
  140. * @param icon the icon of the <code>JMenuItem</code>
  141. */
  142. protected void init(String text, Icon icon) {
  143. if(text != null) {
  144. setText(text);
  145. }
  146. if(icon != null) {
  147. setIcon(icon);
  148. }
  149. // Listen for Focus events
  150. addFocusListener(new MenuItemFocusListener());
  151. setBorderPainted(false);
  152. setFocusPainted(false);
  153. setHorizontalTextPosition(JButton.TRAILING);
  154. setHorizontalAlignment(JButton.LEADING);
  155. updateUI();
  156. }
  157. private static class MenuItemFocusListener implements FocusListener,
  158. Serializable {
  159. public void focusGained(FocusEvent event) {}
  160. public void focusLost(FocusEvent event) {
  161. // When focus is lost, repaint if
  162. // the focus information is painted
  163. JMenuItem mi = (JMenuItem)event.getSource();
  164. if(mi.isFocusPainted()) {
  165. mi.repaint();
  166. }
  167. }
  168. }
  169. /**
  170. * Sets the look and feel object that renders this component.
  171. *
  172. * @param ui the <code>JMenuItemUI</code> L&F object
  173. * @see UIDefaults#getUI
  174. * @beaninfo
  175. * bound: true
  176. * hidden: true
  177. * attribute: visualUpdate true
  178. * description: The UI object that implements the Component's LookAndFeel.
  179. */
  180. public void setUI(MenuItemUI ui) {
  181. super.setUI(ui);
  182. }
  183. /**
  184. * Resets the UI property with a value from the current look and feel.
  185. *
  186. * @see JComponent#updateUI
  187. */
  188. public void updateUI() {
  189. setUI((MenuItemUI)UIManager.getUI(this));
  190. }
  191. /**
  192. * Returns the suffix used to construct the name of the L&F class used to
  193. * render this component.
  194. *
  195. * @return the string "MenuItemUI"
  196. * @see JComponent#getUIClassID
  197. * @see UIDefaults#getUI
  198. */
  199. public String getUIClassID() {
  200. return uiClassID;
  201. }
  202. /**
  203. * Identifies the menu item as "armed". If the mouse button is
  204. * released while it is over this item, the menu's action event
  205. * will fire. If the mouse button is released elsewhere, the
  206. * event will not fire and the menu item will be disarmed.
  207. *
  208. * @param b true to arm the menu item so it can be selected
  209. * @beaninfo
  210. * description: Mouse release will fire an action event
  211. * hidden: true
  212. */
  213. public void setArmed(boolean b) {
  214. ButtonModel model = (ButtonModel) getModel();
  215. boolean oldValue = model.isArmed();
  216. if ((accessibleContext != null) && (oldValue != b)) {
  217. if (b) {
  218. accessibleContext.firePropertyChange(
  219. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  220. null,
  221. AccessibleState.ARMED);
  222. } else {
  223. accessibleContext.firePropertyChange(
  224. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  225. AccessibleState.ARMED,
  226. null);
  227. }
  228. }
  229. if(model.isArmed() != b) {
  230. model.setArmed(b);
  231. }
  232. }
  233. /**
  234. * Returns whether the menu item is "armed".
  235. *
  236. * @return true if the menu item is armed, and it can be selected
  237. * @see #setArmed
  238. */
  239. public boolean isArmed() {
  240. ButtonModel model = (ButtonModel) getModel();
  241. return model.isArmed();
  242. }
  243. /**
  244. * Enables or disables the menu item.
  245. *
  246. * @param b true to enable the item
  247. * @beaninfo
  248. * description: Does the component react to user interaction
  249. * bound: true
  250. * preferred: true
  251. */
  252. public void setEnabled(boolean b) {
  253. // Make sure we aren't armed!
  254. if (b == false)
  255. setArmed(false);
  256. super.setEnabled(b);
  257. }
  258. /**
  259. * Returns true since <code>Menu</code>s, by definition,
  260. * should always be on top of all other windows. If the menu is
  261. * in an internal frame false is returned due to the rollover effect
  262. * for windows laf where the menu is not always on top.
  263. */
  264. // package private
  265. boolean alwaysOnTop() {
  266. // Fix for bug #4482165
  267. if (SwingUtilities.getAncestorOfClass(JInternalFrame.class, this) !=
  268. null) {
  269. return false;
  270. }
  271. return true;
  272. }
  273. /* The keystroke which acts as the menu item's accelerator
  274. */
  275. private KeyStroke accelerator;
  276. /**
  277. * Sets the key combination which invokes the menu item's
  278. * action listeners without navigating the menu hierarchy. It is the
  279. * UI's responsibility to install the correct action. Note that
  280. * when the keyboard accelerator is typed, it will work whether or
  281. * not the menu is currently displayed.
  282. *
  283. * @param keyStroke the <code>KeyStroke</code> which will
  284. * serve as an accelerator
  285. * @beaninfo
  286. * description: The keystroke combination which will invoke the
  287. * JMenuItem's actionlisteners without navigating the
  288. * menu hierarchy
  289. * bound: true
  290. * preferred: true
  291. */
  292. public void setAccelerator(KeyStroke keyStroke) {
  293. KeyStroke oldAccelerator = accelerator;
  294. this.accelerator = keyStroke;
  295. firePropertyChange("accelerator", oldAccelerator, accelerator);
  296. }
  297. /**
  298. * Returns the <code>KeyStroke</code> which serves as an accelerator
  299. * for the menu item.
  300. * @return a <code>KeyStroke</code> object identifying the
  301. * accelerator key
  302. */
  303. public KeyStroke getAccelerator() {
  304. return this.accelerator;
  305. }
  306. /**
  307. * Factory method which sets the <code>ActionEvent</code> source's
  308. * properties according to values from the <code>Action</code> instance.
  309. * The properties which are set may differ for subclasses.
  310. * By default, this method sets the same properties as
  311. * <code>AbstractButton.configurePropertiesFromAction()</code>, plus
  312. * <code>Accelerator</code>.
  313. *
  314. * @param a the <code>Action</code> from which to get the properties,
  315. * or <code>null</code>
  316. * @since 1.3
  317. * @see Action
  318. */
  319. protected void configurePropertiesFromAction(Action a) {
  320. super.configurePropertiesFromAction(a);
  321. KeyStroke ks = (a==null) ? null :
  322. (KeyStroke)a.getValue(Action.ACCELERATOR_KEY);
  323. setAccelerator(ks==null ? null : ks);
  324. }
  325. /**
  326. * Factory method which creates the <code>PropertyChangeListener</code>
  327. * used to update the <code>ActionEvent</code> source as properties
  328. * change on its <code>Action</code> instance.
  329. * Subclasses may override this in order to provide their own
  330. * <code>PropertyChangeListener</code> if the set of
  331. * properties which should be kept up to date differs.
  332. * <p>
  333. * Note that <code>PropertyChangeListeners</code> should avoid holding
  334. * strong references to the <code>ActionEvent</code> source,
  335. * as this may hinder garbage collection of the <code>ActionEvent</code>
  336. * source and all components in its containment hierarchy.
  337. *
  338. * @param a the <code>Action</code> from which to get the properties,
  339. * or <code>null</code>
  340. *
  341. * @since 1.3
  342. * @see Action
  343. */
  344. protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
  345. return new AbstractActionPropertyChangeListener(this, a){
  346. public void propertyChange(PropertyChangeEvent e) {
  347. String propertyName = e.getPropertyName();
  348. JMenuItem mi = (JMenuItem)getTarget();
  349. if (mi == null) { //WeakRef GC'ed in 1.2
  350. Action action = (Action)e.getSource();
  351. action.removePropertyChangeListener(this);
  352. } else {
  353. if (e.getPropertyName().equals(Action.NAME)) {
  354. String text = (String) e.getNewValue();
  355. mi.setText(text);
  356. mi.repaint();
  357. } else if (propertyName.equals("enabled")) {
  358. Boolean enabledState = (Boolean) e.getNewValue();
  359. mi.setEnabled(enabledState.booleanValue());
  360. mi.repaint();
  361. } else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
  362. Icon icon = (Icon) e.getNewValue();
  363. mi.setIcon(icon);
  364. mi.invalidate();
  365. mi.repaint();
  366. } else if (e.getPropertyName().equals(Action.MNEMONIC_KEY)) {
  367. Integer mn = (Integer) e.getNewValue();
  368. mi.setMnemonic(mn.intValue());
  369. mi.invalidate();
  370. mi.repaint();
  371. }
  372. }
  373. }
  374. };
  375. }
  376. /**
  377. * Processes a mouse event forwarded from the
  378. * <code>MenuSelectionManager</code> and changes the menu
  379. * selection, if necessary, by using the
  380. * <code>MenuSelectionManager</code>'s API.
  381. * <p>
  382. * Note: you do not have to forward the event to sub-components.
  383. * This is done automatically by the <code>MenuSelectionManager</code>.
  384. *
  385. * @param e a <code>MouseEvent</code>
  386. * @param path the <code>MenuElement</code> path array
  387. * @param manager the <code>MenuSelectionManager</code>
  388. */
  389. public void processMouseEvent(MouseEvent e,MenuElement path[],MenuSelectionManager manager) {
  390. processMenuDragMouseEvent(
  391. new MenuDragMouseEvent(e.getComponent(), e.getID(),
  392. e.getWhen(),
  393. e.getModifiers(), e.getX(), e.getY(),
  394. e.getClickCount(), e.isPopupTrigger(),
  395. path, manager));
  396. }
  397. /**
  398. * Processes a key event forwarded from the
  399. * <code>MenuSelectionManager</code> and changes the menu selection,
  400. * if necessary, by using <code>MenuSelectionManager</code>'s API.
  401. * <p>
  402. * Note: you do not have to forward the event to sub-components.
  403. * This is done automatically by the <code>MenuSelectionManager</code>.
  404. *
  405. * @param e a <code>KeyEvent</code>
  406. * @param path the <code>MenuElement</code> path array
  407. * @param manager the <code>MenuSelectionManager</code>
  408. */
  409. public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
  410. if (DEBUG) {
  411. System.out.println("in JMenuItem.processKeyEvent/3 for " + getText() +
  412. " " + KeyStroke.getKeyStrokeForEvent(e));
  413. }
  414. MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(),
  415. e.getWhen(), e.getModifiers(),
  416. e.getKeyCode(), e.getKeyChar(),
  417. path, manager);
  418. processMenuKeyEvent(mke);
  419. if (mke.isConsumed()) {
  420. e.consume();
  421. }
  422. }
  423. /**
  424. * Handles mouse drag in a menu.
  425. *
  426. * @param e a <code>MenuDragMouseEvent</code> object
  427. */
  428. public void processMenuDragMouseEvent(MenuDragMouseEvent e) {
  429. switch (e.getID()) {
  430. case MouseEvent.MOUSE_ENTERED:
  431. isMouseDragged = false; fireMenuDragMouseEntered(e); break;
  432. case MouseEvent.MOUSE_EXITED:
  433. isMouseDragged = false; fireMenuDragMouseExited(e); break;
  434. case MouseEvent.MOUSE_DRAGGED:
  435. isMouseDragged = true; fireMenuDragMouseDragged(e); break;
  436. case MouseEvent.MOUSE_RELEASED:
  437. if(isMouseDragged) fireMenuDragMouseReleased(e); break;
  438. default:
  439. break;
  440. }
  441. }
  442. /**
  443. * Handles a keystroke in a menu.
  444. *
  445. * @param e a <code>MenuKeyEvent</code> object
  446. */
  447. public void processMenuKeyEvent(MenuKeyEvent e) {
  448. if (DEBUG) {
  449. System.out.println("in JMenuItem.processMenuKeyEvent for " + getText()+
  450. " " + KeyStroke.getKeyStrokeForEvent(e));
  451. }
  452. switch (e.getID()) {
  453. case KeyEvent.KEY_PRESSED:
  454. fireMenuKeyPressed(e); break;
  455. case KeyEvent.KEY_RELEASED:
  456. fireMenuKeyReleased(e); break;
  457. case KeyEvent.KEY_TYPED:
  458. fireMenuKeyTyped(e); break;
  459. default:
  460. break;
  461. }
  462. }
  463. /**
  464. * Notifies all listeners that have registered interest for
  465. * notification on this event type.
  466. *
  467. * @param event a <code>MenuMouseDragEvent</code>
  468. * @see EventListenerList
  469. */
  470. protected void fireMenuDragMouseEntered(MenuDragMouseEvent event) {
  471. // Guaranteed to return a non-null array
  472. Object[] listeners = listenerList.getListenerList();
  473. // Process the listeners last to first, notifying
  474. // those that are interested in this event
  475. for (int i = listeners.length-2; i>=0; i-=2) {
  476. if (listeners[i]==MenuDragMouseListener.class) {
  477. // Lazily create the event:
  478. ((MenuDragMouseListener)listeners[i+1]).menuDragMouseEntered(event);
  479. }
  480. }
  481. }
  482. /**
  483. * Notifies all listeners that have registered interest for
  484. * notification on this event type.
  485. *
  486. * @param event a <code>MenuDragMouseEvent</code>
  487. * @see EventListenerList
  488. */
  489. protected void fireMenuDragMouseExited(MenuDragMouseEvent event) {
  490. // Guaranteed to return a non-null array
  491. Object[] listeners = listenerList.getListenerList();
  492. // Process the listeners last to first, notifying
  493. // those that are interested in this event
  494. for (int i = listeners.length-2; i>=0; i-=2) {
  495. if (listeners[i]==MenuDragMouseListener.class) {
  496. // Lazily create the event:
  497. ((MenuDragMouseListener)listeners[i+1]).menuDragMouseExited(event);
  498. }
  499. }
  500. }
  501. /**
  502. * Notifies all listeners that have registered interest for
  503. * notification on this event type.
  504. *
  505. * @param event a <code>MenuDragMouseEvent</code>
  506. * @see EventListenerList
  507. */
  508. protected void fireMenuDragMouseDragged(MenuDragMouseEvent event) {
  509. // Guaranteed to return a non-null array
  510. Object[] listeners = listenerList.getListenerList();
  511. // Process the listeners last to first, notifying
  512. // those that are interested in this event
  513. for (int i = listeners.length-2; i>=0; i-=2) {
  514. if (listeners[i]==MenuDragMouseListener.class) {
  515. // Lazily create the event:
  516. ((MenuDragMouseListener)listeners[i+1]).menuDragMouseDragged(event);
  517. }
  518. }
  519. }
  520. /**
  521. * Notifies all listeners that have registered interest for
  522. * notification on this event type.
  523. *
  524. * @param event a <code>MenuDragMouseEvent</code>
  525. * @see EventListenerList
  526. */
  527. protected void fireMenuDragMouseReleased(MenuDragMouseEvent event) {
  528. // Guaranteed to return a non-null array
  529. Object[] listeners = listenerList.getListenerList();
  530. // Process the listeners last to first, notifying
  531. // those that are interested in this event
  532. for (int i = listeners.length-2; i>=0; i-=2) {
  533. if (listeners[i]==MenuDragMouseListener.class) {
  534. // Lazily create the event:
  535. ((MenuDragMouseListener)listeners[i+1]).menuDragMouseReleased(event);
  536. }
  537. }
  538. }
  539. /**
  540. * Notifies all listeners that have registered interest for
  541. * notification on this event type.
  542. *
  543. * @param event a <code>MenuKeyEvent</code>
  544. * @see EventListenerList
  545. */
  546. protected void fireMenuKeyPressed(MenuKeyEvent event) {
  547. if (DEBUG) {
  548. System.out.println("in JMenuItem.fireMenuKeyPressed for " + getText()+
  549. " " + KeyStroke.getKeyStrokeForEvent(event));
  550. }
  551. // Guaranteed to return a non-null array
  552. Object[] listeners = listenerList.getListenerList();
  553. // Process the listeners last to first, notifying
  554. // those that are interested in this event
  555. for (int i = listeners.length-2; i>=0; i-=2) {
  556. if (listeners[i]==MenuKeyListener.class) {
  557. // Lazily create the event:
  558. ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event);
  559. }
  560. }
  561. }
  562. /**
  563. * Notifies all listeners that have registered interest for
  564. * notification on this event type.
  565. *
  566. * @param event a <code>MenuKeyEvent</code>
  567. * @see EventListenerList
  568. */
  569. protected void fireMenuKeyReleased(MenuKeyEvent event) {
  570. if (DEBUG) {
  571. System.out.println("in JMenuItem.fireMenuKeyReleased for " + getText()+
  572. " " + KeyStroke.getKeyStrokeForEvent(event));
  573. }
  574. // Guaranteed to return a non-null array
  575. Object[] listeners = listenerList.getListenerList();
  576. // Process the listeners last to first, notifying
  577. // those that are interested in this event
  578. for (int i = listeners.length-2; i>=0; i-=2) {
  579. if (listeners[i]==MenuKeyListener.class) {
  580. // Lazily create the event:
  581. ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event);
  582. }
  583. }
  584. }
  585. /**
  586. * Notifies all listeners that have registered interest for
  587. * notification on this event type.
  588. *
  589. * @param event a <code>MenuKeyEvent</code>
  590. * @see EventListenerList
  591. */
  592. protected void fireMenuKeyTyped(MenuKeyEvent event) {
  593. if (DEBUG) {
  594. System.out.println("in JMenuItem.fireMenuKeyTyped for " + getText()+
  595. " " + KeyStroke.getKeyStrokeForEvent(event));
  596. }
  597. // Guaranteed to return a non-null array
  598. Object[] listeners = listenerList.getListenerList();
  599. // Process the listeners last to first, notifying
  600. // those that are interested in this event
  601. for (int i = listeners.length-2; i>=0; i-=2) {
  602. if (listeners[i]==MenuKeyListener.class) {
  603. // Lazily create the event:
  604. ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event);
  605. }
  606. }
  607. }
  608. /**
  609. * Called by the <code>MenuSelectionManager</code> when the
  610. * <code>MenuElement</code> is selected or unselected.
  611. *
  612. * @param isIncluded true if this menu item is on the part of the menu
  613. * path that changed, false if this menu is part of the
  614. * a menu path that changed, but this particular part of
  615. * that path is still the same
  616. * @see MenuSelectionManager#setSelectedPath(MenuElement[])
  617. */
  618. public void menuSelectionChanged(boolean isIncluded) {
  619. setArmed(isIncluded);
  620. }
  621. /**
  622. * This method returns an array containing the sub-menu
  623. * components for this menu component.
  624. *
  625. * @return an array of <code>MenuElement</code>s
  626. */
  627. public MenuElement[] getSubElements() {
  628. return new MenuElement[0];
  629. }
  630. /**
  631. * Returns the <code>java.awt.Component</code> used to paint
  632. * this object. The returned component will be used to convert
  633. * events and detect if an event is inside a menu component.
  634. *
  635. * @return the <code>Component</code> that paints this menu item
  636. */
  637. public Component getComponent() {
  638. return this;
  639. }
  640. /**
  641. * Adds a <code>MenuDragMouseListener</code> to the menu item.
  642. *
  643. * @param l the <code>MenuDragMouseListener</code> to be added
  644. */
  645. public void addMenuDragMouseListener(MenuDragMouseListener l) {
  646. listenerList.add(MenuDragMouseListener.class, l);
  647. }
  648. /**
  649. * Removes a <code>MenuDragMouseListener</code> from the menu item.
  650. *
  651. * @param l the <code>MenuDragMouseListener</code> to be removed
  652. */
  653. public void removeMenuDragMouseListener(MenuDragMouseListener l) {
  654. listenerList.remove(MenuDragMouseListener.class, l);
  655. }
  656. /**
  657. * Returns an array of all the <code>MenuDragMouseListener</code>s added
  658. * to this JMenuItem with addMenuDragMouseListener().
  659. *
  660. * @return all of the <code>MenuDragMouseListener</code>s added or an empty
  661. * array if no listeners have been added
  662. * @since 1.4
  663. */
  664. public MenuDragMouseListener[] getMenuDragMouseListeners() {
  665. return (MenuDragMouseListener[])listenerList.getListeners(
  666. MenuDragMouseListener.class);
  667. }
  668. /**
  669. * Adds a <code>MenuKeyListener</code> to the menu item.
  670. *
  671. * @param l the <code>MenuKeyListener</code> to be added
  672. */
  673. public void addMenuKeyListener(MenuKeyListener l) {
  674. listenerList.add(MenuKeyListener.class, l);
  675. }
  676. /**
  677. * Removes a <code>MenuKeyListener</code> from the menu item.
  678. *
  679. * @param l the <code>MenuKeyListener</code> to be removed
  680. */
  681. public void removeMenuKeyListener(MenuKeyListener l) {
  682. listenerList.remove(MenuKeyListener.class, l);
  683. }
  684. /**
  685. * Returns an array of all the <code>MenuKeyListener</code>s added
  686. * to this JMenuItem with addMenuKeyListener().
  687. *
  688. * @return all of the <code>MenuKeyListener</code>s added or an empty
  689. * array if no listeners have been added
  690. * @since 1.4
  691. */
  692. public MenuKeyListener[] getMenuKeyListeners() {
  693. return (MenuKeyListener[])listenerList.getListeners(
  694. MenuKeyListener.class);
  695. }
  696. /**
  697. * See JComponent.readObject() for information about serialization
  698. * in Swing.
  699. */
  700. private void readObject(ObjectInputStream s)
  701. throws IOException, ClassNotFoundException
  702. {
  703. s.defaultReadObject();
  704. if (getUIClassID().equals(uiClassID)) {
  705. updateUI();
  706. }
  707. }
  708. private void writeObject(ObjectOutputStream s) throws IOException {
  709. s.defaultWriteObject();
  710. if (getUIClassID().equals(uiClassID)) {
  711. byte count = JComponent.getWriteObjCounter(this);
  712. JComponent.setWriteObjCounter(this, --count);
  713. if (count == 0 && ui != null) {
  714. ui.installUI(this);
  715. }
  716. }
  717. }
  718. /**
  719. * Returns a string representation of this <code>JMenuItem</code>.
  720. * This method is intended to be used only for debugging purposes,
  721. * and the content and format of the returned string may vary between
  722. * implementations. The returned string may be empty but may not
  723. * be <code>null</code>.
  724. *
  725. * @return a string representation of this <code>JMenuItem</code>
  726. */
  727. protected String paramString() {
  728. return super.paramString();
  729. }
  730. /////////////////
  731. // Accessibility support
  732. ////////////////
  733. /**
  734. * Returns the <code>AccessibleContext</code> associated with this
  735. * <code>JMenuItem</code>. For <code>JMenuItem</code>s,
  736. * the <code>AccessibleContext</code> takes the form of an
  737. * <code>AccessibleJMenuItem</code>.
  738. * A new AccessibleJMenuItme instance is created if necessary.
  739. *
  740. * @return an <code>AccessibleJMenuItem</code> that serves as the
  741. * <code>AccessibleContext</code> of this <code>JMenuItem</code>
  742. */
  743. public AccessibleContext getAccessibleContext() {
  744. if (accessibleContext == null) {
  745. accessibleContext = new AccessibleJMenuItem();
  746. }
  747. return accessibleContext;
  748. }
  749. /**
  750. * This class implements accessibility support for the
  751. * <code>JMenuItem</code> class. It provides an implementation of the
  752. * Java Accessibility API appropriate to menu item user-interface
  753. * elements.
  754. * <p>
  755. * <strong>Warning:</strong>
  756. * Serialized objects of this class will not be compatible with
  757. * future Swing releases. The current serialization support is
  758. * appropriate for short term storage or RMI between applications running
  759. * the same version of Swing. As of 1.4, support for long term storage
  760. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  761. * has been added to the <code>java.beans</code> package.
  762. * Please see {@link java.beans.XMLEncoder}.
  763. */
  764. protected class AccessibleJMenuItem extends AccessibleAbstractButton implements ChangeListener {
  765. private boolean isArmed = false;
  766. private boolean hasFocus = false;
  767. private boolean isPressed = false;
  768. private boolean isSelected = false;
  769. AccessibleJMenuItem() {
  770. super();
  771. JMenuItem.this.addChangeListener(this);
  772. }
  773. /**
  774. * Get the role of this object.
  775. *
  776. * @return an instance of AccessibleRole describing the role of the
  777. * object
  778. */
  779. public AccessibleRole getAccessibleRole() {
  780. return AccessibleRole.MENU_ITEM;
  781. }
  782. /**
  783. * Supports the change listener interface and fires property changes.
  784. */
  785. public void stateChanged(ChangeEvent e) {
  786. firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
  787. Boolean.valueOf(false), Boolean.valueOf(true));
  788. if (JMenuItem.this.getModel().isArmed()) {
  789. if (!isArmed) {
  790. isArmed = true;
  791. firePropertyChange(
  792. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  793. null, AccessibleState.ARMED);
  794. }
  795. } else {
  796. if (isArmed) {
  797. isArmed = false;
  798. firePropertyChange(
  799. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  800. AccessibleState.ARMED, null);
  801. }
  802. }
  803. if (JMenuItem.this.isFocusOwner()) {
  804. if (!hasFocus) {
  805. hasFocus = true;
  806. firePropertyChange(
  807. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  808. null, AccessibleState.FOCUSED);
  809. }
  810. } else {
  811. if (hasFocus) {
  812. hasFocus = false;
  813. firePropertyChange(
  814. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  815. AccessibleState.FOCUSED, null);
  816. }
  817. }
  818. if (JMenuItem.this.getModel().isPressed()) {
  819. if (!isPressed) {
  820. isPressed = true;
  821. firePropertyChange(
  822. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  823. null, AccessibleState.PRESSED);
  824. }
  825. } else {
  826. if (isPressed) {
  827. isPressed = false;
  828. firePropertyChange(
  829. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  830. AccessibleState.PRESSED, null);
  831. }
  832. }
  833. if (JMenuItem.this.getModel().isSelected()) {
  834. if (!isSelected) {
  835. isSelected = true;
  836. firePropertyChange(
  837. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  838. null, AccessibleState.CHECKED);
  839. }
  840. } else {
  841. if (isSelected) {
  842. isSelected = false;
  843. firePropertyChange(
  844. AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
  845. AccessibleState.CHECKED, null);
  846. }
  847. }
  848. }
  849. } // inner class AccessibleJMenuItem
  850. }