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