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  */
  25 package javax.swing;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 
  30 import java.beans.JavaBean;
  31 import java.beans.BeanProperty;
  32 
  33 import java.io.Serializable;
  34 import java.io.ObjectOutputStream;
  35 import java.io.ObjectInputStream;
  36 import java.io.IOException;
  37 
  38 import javax.swing.plaf.*;
  39 import javax.swing.event.*;
  40 import 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")
  87 public 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 }