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