1 /*
   2  * Copyright (c) 1997, 2008, 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<sup><font size="-2">TM</font></sup>
  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<sup><font size="-2">TM</font></sup>
 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 }