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