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.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 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     public void processMouseEvent(MouseEvent e,MenuElement path[],MenuSelectionManager manager) {
 416         processMenuDragMouseEvent(
 417                  new MenuDragMouseEvent(e.getComponent(), e.getID(),
 418                                         e.getWhen(),
 419                                         e.getModifiers(), e.getX(), e.getY(),
 420                                         e.getXOnScreen(), e.getYOnScreen(),
 421                                         e.getClickCount(), e.isPopupTrigger(),
 422                                         path, manager));
 423     }
 424 
 425 
 426     /**
 427      * Processes a key event forwarded from the
 428      * <code>MenuSelectionManager</code> and changes the menu selection,
 429      * if necessary, by using <code>MenuSelectionManager</code>'s API.
 430      * <p>
 431      * Note: you do not have to forward the event to sub-components.
 432      * This is done automatically by the <code>MenuSelectionManager</code>.
 433      *
 434      * @param e  a <code>KeyEvent</code>
 435      * @param path the <code>MenuElement</code> path array
 436      * @param manager   the <code>MenuSelectionManager</code>
 437      */
 438     public void processKeyEvent(KeyEvent e,MenuElement path[],MenuSelectionManager manager) {
 439         if (DEBUG) {
 440             System.out.println("in JMenuItem.processKeyEvent/3 for " + getText() +
 441                                    "  " + KeyStroke.getKeyStrokeForEvent(e));
 442         }
 443         MenuKeyEvent mke = new MenuKeyEvent(e.getComponent(), e.getID(),
 444                                              e.getWhen(), e.getModifiers(),
 445                                              e.getKeyCode(), e.getKeyChar(),
 446                                              path, manager);
 447         processMenuKeyEvent(mke);
 448 
 449         if (mke.isConsumed())  {
 450             e.consume();
 451         }
 452     }
 453 
 454 
 455 
 456     /**
 457      * Handles mouse drag in a menu.
 458      *
 459      * @param e  a <code>MenuDragMouseEvent</code> object
 460      */
 461     public void processMenuDragMouseEvent(MenuDragMouseEvent e) {
 462         switch (e.getID()) {
 463         case MouseEvent.MOUSE_ENTERED:
 464             isMouseDragged = false; fireMenuDragMouseEntered(e); break;
 465         case MouseEvent.MOUSE_EXITED:
 466             isMouseDragged = false; fireMenuDragMouseExited(e); break;
 467         case MouseEvent.MOUSE_DRAGGED:
 468             isMouseDragged = true; fireMenuDragMouseDragged(e); break;
 469         case MouseEvent.MOUSE_RELEASED:
 470             if(isMouseDragged) fireMenuDragMouseReleased(e); break;
 471         default:
 472             break;
 473         }
 474     }
 475 
 476     /**
 477      * Handles a keystroke in a menu.
 478      *
 479      * @param e  a <code>MenuKeyEvent</code> object
 480      */
 481     public void processMenuKeyEvent(MenuKeyEvent e) {
 482         if (DEBUG) {
 483             System.out.println("in JMenuItem.processMenuKeyEvent for " + getText()+
 484                                    "  " + KeyStroke.getKeyStrokeForEvent(e));
 485         }
 486         switch (e.getID()) {
 487         case KeyEvent.KEY_PRESSED:
 488             fireMenuKeyPressed(e); break;
 489         case KeyEvent.KEY_RELEASED:
 490             fireMenuKeyReleased(e); break;
 491         case KeyEvent.KEY_TYPED:
 492             fireMenuKeyTyped(e); break;
 493         default:
 494             break;
 495         }
 496     }
 497 
 498     /**
 499      * Notifies all listeners that have registered interest for
 500      * notification on this event type.
 501      *
 502      * @param event a <code>MenuMouseDragEvent</code>
 503      * @see EventListenerList
 504      */
 505     protected void fireMenuDragMouseEntered(MenuDragMouseEvent event) {
 506         // Guaranteed to return a non-null array
 507         Object[] listeners = listenerList.getListenerList();
 508         // Process the listeners last to first, notifying
 509         // those that are interested in this event
 510         for (int i = listeners.length-2; i>=0; i-=2) {
 511             if (listeners[i]==MenuDragMouseListener.class) {
 512                 // Lazily create the event:
 513                 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseEntered(event);
 514             }
 515         }
 516     }
 517 
 518     /**
 519      * Notifies all listeners that have registered interest for
 520      * notification on this event type.
 521      *
 522      * @param event a <code>MenuDragMouseEvent</code>
 523      * @see EventListenerList
 524      */
 525     protected void fireMenuDragMouseExited(MenuDragMouseEvent event) {
 526         // Guaranteed to return a non-null array
 527         Object[] listeners = listenerList.getListenerList();
 528         // Process the listeners last to first, notifying
 529         // those that are interested in this event
 530         for (int i = listeners.length-2; i>=0; i-=2) {
 531             if (listeners[i]==MenuDragMouseListener.class) {
 532                 // Lazily create the event:
 533                 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseExited(event);
 534             }
 535         }
 536     }
 537 
 538     /**
 539      * Notifies all listeners that have registered interest for
 540      * notification on this event type.
 541      *
 542      * @param event a <code>MenuDragMouseEvent</code>
 543      * @see EventListenerList
 544      */
 545     protected void fireMenuDragMouseDragged(MenuDragMouseEvent event) {
 546         // Guaranteed to return a non-null array
 547         Object[] listeners = listenerList.getListenerList();
 548         // Process the listeners last to first, notifying
 549         // those that are interested in this event
 550         for (int i = listeners.length-2; i>=0; i-=2) {
 551             if (listeners[i]==MenuDragMouseListener.class) {
 552                 // Lazily create the event:
 553                 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseDragged(event);
 554             }
 555         }
 556     }
 557 
 558     /**
 559      * Notifies all listeners that have registered interest for
 560      * notification on this event type.
 561      *
 562      * @param event a <code>MenuDragMouseEvent</code>
 563      * @see EventListenerList
 564      */
 565     protected void fireMenuDragMouseReleased(MenuDragMouseEvent event) {
 566         // Guaranteed to return a non-null array
 567         Object[] listeners = listenerList.getListenerList();
 568         // Process the listeners last to first, notifying
 569         // those that are interested in this event
 570         for (int i = listeners.length-2; i>=0; i-=2) {
 571             if (listeners[i]==MenuDragMouseListener.class) {
 572                 // Lazily create the event:
 573                 ((MenuDragMouseListener)listeners[i+1]).menuDragMouseReleased(event);
 574             }
 575         }
 576     }
 577 
 578     /**
 579      * Notifies all listeners that have registered interest for
 580      * notification on this event type.
 581      *
 582      * @param event a <code>MenuKeyEvent</code>
 583      * @see EventListenerList
 584      */
 585     protected void fireMenuKeyPressed(MenuKeyEvent event) {
 586         if (DEBUG) {
 587             System.out.println("in JMenuItem.fireMenuKeyPressed for " + getText()+
 588                                    "  " + KeyStroke.getKeyStrokeForEvent(event));
 589         }
 590         // Guaranteed to return a non-null array
 591         Object[] listeners = listenerList.getListenerList();
 592         // Process the listeners last to first, notifying
 593         // those that are interested in this event
 594         for (int i = listeners.length-2; i>=0; i-=2) {
 595             if (listeners[i]==MenuKeyListener.class) {
 596                 // Lazily create the event:
 597                 ((MenuKeyListener)listeners[i+1]).menuKeyPressed(event);
 598             }
 599         }
 600     }
 601 
 602     /**
 603      * Notifies all listeners that have registered interest for
 604      * notification on this event type.
 605      *
 606      * @param event a <code>MenuKeyEvent</code>
 607      * @see EventListenerList
 608      */
 609     protected void fireMenuKeyReleased(MenuKeyEvent event) {
 610         if (DEBUG) {
 611             System.out.println("in JMenuItem.fireMenuKeyReleased for " + getText()+
 612                                    "  " + KeyStroke.getKeyStrokeForEvent(event));
 613         }
 614         // Guaranteed to return a non-null array
 615         Object[] listeners = listenerList.getListenerList();
 616         // Process the listeners last to first, notifying
 617         // those that are interested in this event
 618         for (int i = listeners.length-2; i>=0; i-=2) {
 619             if (listeners[i]==MenuKeyListener.class) {
 620                 // Lazily create the event:
 621                 ((MenuKeyListener)listeners[i+1]).menuKeyReleased(event);
 622             }
 623         }
 624     }
 625 
 626     /**
 627      * Notifies all listeners that have registered interest for
 628      * notification on this event type.
 629      *
 630      * @param event a <code>MenuKeyEvent</code>
 631      * @see EventListenerList
 632      */
 633     protected void fireMenuKeyTyped(MenuKeyEvent event) {
 634         if (DEBUG) {
 635             System.out.println("in JMenuItem.fireMenuKeyTyped for " + getText()+
 636                                    "  " + KeyStroke.getKeyStrokeForEvent(event));
 637         }
 638         // Guaranteed to return a non-null array
 639         Object[] listeners = listenerList.getListenerList();
 640         // Process the listeners last to first, notifying
 641         // those that are interested in this event
 642         for (int i = listeners.length-2; i>=0; i-=2) {
 643             if (listeners[i]==MenuKeyListener.class) {
 644                 // Lazily create the event:
 645                 ((MenuKeyListener)listeners[i+1]).menuKeyTyped(event);
 646             }
 647         }
 648     }
 649 
 650     /**
 651      * Called by the <code>MenuSelectionManager</code> when the
 652      * <code>MenuElement</code> is selected or unselected.
 653      *
 654      * @param isIncluded  true if this menu item is on the part of the menu
 655      *                    path that changed, false if this menu is part of the
 656      *                    a menu path that changed, but this particular part of
 657      *                    that path is still the same
 658      * @see MenuSelectionManager#setSelectedPath(MenuElement[])
 659      */
 660     public void menuSelectionChanged(boolean isIncluded) {
 661         setArmed(isIncluded);
 662     }
 663 
 664     /**
 665      * This method returns an array containing the sub-menu
 666      * components for this menu component.
 667      *
 668      * @return an array of <code>MenuElement</code>s
 669      */
 670     @BeanProperty(bound = false)
 671     public MenuElement[] getSubElements() {
 672         return new MenuElement[0];
 673     }
 674 
 675     /**
 676      * Returns the <code>java.awt.Component</code> used to paint
 677      * this object. The returned component will be used to convert
 678      * events and detect if an event is inside a menu component.
 679      *
 680      * @return the <code>Component</code> that paints this menu item
 681      */
 682     public Component getComponent() {
 683         return this;
 684     }
 685 
 686     /**
 687      * Adds a <code>MenuDragMouseListener</code> to the menu item.
 688      *
 689      * @param l the <code>MenuDragMouseListener</code> to be added
 690      */
 691     public void addMenuDragMouseListener(MenuDragMouseListener l) {
 692         listenerList.add(MenuDragMouseListener.class, l);
 693     }
 694 
 695     /**
 696      * Removes a <code>MenuDragMouseListener</code> from the menu item.
 697      *
 698      * @param l the <code>MenuDragMouseListener</code> to be removed
 699      */
 700     public void removeMenuDragMouseListener(MenuDragMouseListener l) {
 701         listenerList.remove(MenuDragMouseListener.class, l);
 702     }
 703 
 704     /**
 705      * Returns an array of all the <code>MenuDragMouseListener</code>s added
 706      * to this JMenuItem with addMenuDragMouseListener().
 707      *
 708      * @return all of the <code>MenuDragMouseListener</code>s added or an empty
 709      *         array if no listeners have been added
 710      * @since 1.4
 711      */
 712     @BeanProperty(bound = false)
 713     public MenuDragMouseListener[] getMenuDragMouseListeners() {
 714         return listenerList.getListeners(MenuDragMouseListener.class);
 715     }
 716 
 717     /**
 718      * Adds a <code>MenuKeyListener</code> to the menu item.
 719      *
 720      * @param l the <code>MenuKeyListener</code> to be added
 721      */
 722     public void addMenuKeyListener(MenuKeyListener l) {
 723         listenerList.add(MenuKeyListener.class, l);
 724     }
 725 
 726     /**
 727      * Removes a <code>MenuKeyListener</code> from the menu item.
 728      *
 729      * @param l the <code>MenuKeyListener</code> to be removed
 730      */
 731     public void removeMenuKeyListener(MenuKeyListener l) {
 732         listenerList.remove(MenuKeyListener.class, l);
 733     }
 734 
 735     /**
 736      * Returns an array of all the <code>MenuKeyListener</code>s added
 737      * to this JMenuItem with addMenuKeyListener().
 738      *
 739      * @return all of the <code>MenuKeyListener</code>s added or an empty
 740      *         array if no listeners have been added
 741      * @since 1.4
 742      */
 743     @BeanProperty(bound = false)
 744     public MenuKeyListener[] getMenuKeyListeners() {
 745         return listenerList.getListeners(MenuKeyListener.class);
 746     }
 747 
 748     /**
 749      * See JComponent.readObject() for information about serialization
 750      * in Swing.
 751      */
 752     private void readObject(ObjectInputStream s)
 753         throws IOException, ClassNotFoundException
 754     {
 755         s.defaultReadObject();
 756         if (getUIClassID().equals(uiClassID)) {
 757             updateUI();
 758         }
 759     }
 760 
 761     private void writeObject(ObjectOutputStream s) throws IOException {
 762         s.defaultWriteObject();
 763         if (getUIClassID().equals(uiClassID)) {
 764             byte count = JComponent.getWriteObjCounter(this);
 765             JComponent.setWriteObjCounter(this, --count);
 766             if (count == 0 && ui != null) {
 767                 ui.installUI(this);
 768             }
 769         }
 770     }
 771 
 772 
 773     /**
 774      * Returns a string representation of this <code>JMenuItem</code>.
 775      * This method is intended to be used only for debugging purposes,
 776      * and the content and format of the returned string may vary between
 777      * implementations. The returned string may be empty but may not
 778      * be <code>null</code>.
 779      *
 780      * @return  a string representation of this <code>JMenuItem</code>
 781      */
 782     protected String paramString() {
 783         return super.paramString();
 784     }
 785 
 786 /////////////////
 787 // Accessibility support
 788 ////////////////
 789 
 790     /**
 791      * Returns the <code>AccessibleContext</code> associated with this
 792      * <code>JMenuItem</code>. For <code>JMenuItem</code>s,
 793      * the <code>AccessibleContext</code> takes the form of an
 794      * <code>AccessibleJMenuItem</code>.
 795      * A new AccessibleJMenuItme instance is created if necessary.
 796      *
 797      * @return an <code>AccessibleJMenuItem</code> that serves as the
 798      *         <code>AccessibleContext</code> of this <code>JMenuItem</code>
 799      */
 800     @BeanProperty(bound = false)
 801     public AccessibleContext getAccessibleContext() {
 802         if (accessibleContext == null) {
 803             accessibleContext = new AccessibleJMenuItem();
 804         }
 805         return accessibleContext;
 806     }
 807 
 808 
 809     /**
 810      * This class implements accessibility support for the
 811      * <code>JMenuItem</code> class.  It provides an implementation of the
 812      * Java Accessibility API appropriate to menu item user-interface
 813      * elements.
 814      * <p>
 815      * <strong>Warning:</strong>
 816      * Serialized objects of this class will not be compatible with
 817      * future Swing releases. The current serialization support is
 818      * appropriate for short term storage or RMI between applications running
 819      * the same version of Swing.  As of 1.4, support for long term storage
 820      * of all JavaBeans&trade;
 821      * has been added to the <code>java.beans</code> package.
 822      * Please see {@link java.beans.XMLEncoder}.
 823      */
 824     @SuppressWarnings("serial")
 825     protected class AccessibleJMenuItem extends AccessibleAbstractButton implements ChangeListener {
 826 
 827         private boolean isArmed = false;
 828         private boolean hasFocus = false;
 829         private boolean isPressed = false;
 830         private boolean isSelected = false;
 831 
 832         AccessibleJMenuItem() {
 833             super();
 834             JMenuItem.this.addChangeListener(this);
 835         }
 836 
 837         /**
 838          * Get the role of this object.
 839          *
 840          * @return an instance of AccessibleRole describing the role of the
 841          * object
 842          */
 843         public AccessibleRole getAccessibleRole() {
 844             return AccessibleRole.MENU_ITEM;
 845         }
 846 
 847         private void fireAccessibilityFocusedEvent(JMenuItem toCheck) {
 848             MenuElement [] path =
 849                 MenuSelectionManager.defaultManager().getSelectedPath();
 850             if (path.length > 0) {
 851                 Object menuItem = path[path.length - 1];
 852                 if (toCheck == menuItem) {
 853                     firePropertyChange(
 854                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 855                         null, AccessibleState.FOCUSED);
 856                 }
 857             }
 858         }
 859 
 860         /**
 861          * Supports the change listener interface and fires property changes.
 862          */
 863         public void stateChanged(ChangeEvent e) {
 864             firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
 865                                Boolean.valueOf(false), Boolean.valueOf(true));
 866             if (JMenuItem.this.getModel().isArmed()) {
 867                 if (!isArmed) {
 868                     isArmed = true;
 869                     firePropertyChange(
 870                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 871                         null, AccessibleState.ARMED);
 872                     // Fix for 4848220 moved here to avoid major memory leak
 873                     // Here we will fire the event in case of JMenuItem
 874                     // See bug 4910323 for details [zav]
 875                     fireAccessibilityFocusedEvent(JMenuItem.this);
 876                 }
 877             } else {
 878                 if (isArmed) {
 879                     isArmed = false;
 880                     firePropertyChange(
 881                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 882                         AccessibleState.ARMED, null);
 883                 }
 884             }
 885             if (JMenuItem.this.isFocusOwner()) {
 886                 if (!hasFocus) {
 887                     hasFocus = true;
 888                     firePropertyChange(
 889                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 890                         null, AccessibleState.FOCUSED);
 891                 }
 892             } else {
 893                 if (hasFocus) {
 894                     hasFocus = false;
 895                     firePropertyChange(
 896                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 897                         AccessibleState.FOCUSED, null);
 898                 }
 899             }
 900             if (JMenuItem.this.getModel().isPressed()) {
 901                 if (!isPressed) {
 902                     isPressed = true;
 903                     firePropertyChange(
 904                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 905                         null, AccessibleState.PRESSED);
 906                 }
 907             } else {
 908                 if (isPressed) {
 909                     isPressed = false;
 910                     firePropertyChange(
 911                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 912                         AccessibleState.PRESSED, null);
 913                 }
 914             }
 915             if (JMenuItem.this.getModel().isSelected()) {
 916                 if (!isSelected) {
 917                     isSelected = true;
 918                     firePropertyChange(
 919                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 920                         null, AccessibleState.CHECKED);
 921 
 922                     // Fix for 4848220 moved here to avoid major memory leak
 923                     // Here we will fire the event in case of JMenu
 924                     // See bug 4910323 for details [zav]
 925                     fireAccessibilityFocusedEvent(JMenuItem.this);
 926                 }
 927             } else {
 928                 if (isSelected) {
 929                     isSelected = false;
 930                     firePropertyChange(
 931                         AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
 932                         AccessibleState.CHECKED, null);
 933                 }
 934             }
 935 
 936         }
 937     } // inner class AccessibleJMenuItem
 938 
 939 
 940 }