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