1 /*
   2  * Copyright (c) 1995, 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 java.awt;
  26 
  27 import java.awt.peer.MenuItemPeer;
  28 import java.awt.event.*;
  29 import java.util.EventListener;
  30 import java.io.ObjectOutputStream;
  31 import java.io.ObjectInputStream;
  32 import java.io.IOException;
  33 import javax.accessibility.*;
  34 import sun.awt.AWTAccessor;
  35 
  36 /**
  37  * All items in a menu must belong to the class
  38  * <code>MenuItem</code>, or one of its subclasses.
  39  * <p>
  40  * The default <code>MenuItem</code> object embodies
  41  * a simple labeled menu item.
  42  * <p>
  43  * This picture of a menu bar shows five menu items:
  44  * <IMG SRC="doc-files/MenuBar-1.gif" alt="The following text describes this graphic."
  45  * style="float:center; margin: 7px 10px;">
  46  * <br style="clear:left;">
  47  * The first two items are simple menu items, labeled
  48  * <code>"Basic"</code> and <code>"Simple"</code>.
  49  * Following these two items is a separator, which is itself
  50  * a menu item, created with the label <code>"-"</code>.
  51  * Next is an instance of <code>CheckboxMenuItem</code>
  52  * labeled <code>"Check"</code>. The final menu item is a
  53  * submenu labeled <code>"More&nbsp;Examples"</code>,
  54  * and this submenu is an instance of <code>Menu</code>.
  55  * <p>
  56  * When a menu item is selected, AWT sends an action event to
  57  * the menu item. Since the event is an
  58  * instance of <code>ActionEvent</code>, the <code>processEvent</code>
  59  * method examines the event and passes it along to
  60  * <code>processActionEvent</code>. The latter method redirects the
  61  * event to any <code>ActionListener</code> objects that have
  62  * registered an interest in action events generated by this
  63  * menu item.
  64  * <P>
  65  * Note that the subclass <code>Menu</code> overrides this behavior and
  66  * does not send any event to the frame until one of its subitems is
  67  * selected.
  68  *
  69  * @author Sami Shaio
  70  */
  71 public class MenuItem extends MenuComponent implements Accessible {
  72 
  73     static {
  74         /* ensure that the necessary native libraries are loaded */
  75         Toolkit.loadLibraries();
  76         if (!GraphicsEnvironment.isHeadless()) {
  77             initIDs();
  78         }
  79 
  80         AWTAccessor.setMenuItemAccessor(
  81             new AWTAccessor.MenuItemAccessor() {
  82                 public boolean isEnabled(MenuItem item) {
  83                     return item.enabled;
  84                 }
  85 
  86                 public String getLabel(MenuItem item) {
  87                     return item.label;
  88                 }
  89 
  90                 public MenuShortcut getShortcut(MenuItem item) {
  91                     return item.shortcut;
  92                 }
  93 
  94                 public String getActionCommandImpl(MenuItem item) {
  95                     return item.getActionCommandImpl();
  96                 }
  97 
  98                 public boolean isItemEnabled(MenuItem item) {
  99                     return item.isItemEnabled();
 100                 }
 101             });
 102     }
 103 
 104     /**
 105      * A value to indicate whether a menu item is enabled
 106      * or not.  If it is enabled, <code>enabled</code> will
 107      * be set to true.  Else <code>enabled</code> will
 108      * be set to false.
 109      *
 110      * @serial
 111      * @see #isEnabled()
 112      * @see #setEnabled(boolean)
 113      */
 114     boolean enabled = true;
 115 
 116     /**
 117      * <code>label</code> is the label of a menu item.
 118      * It can be any string.
 119      *
 120      * @serial
 121      * @see #getLabel()
 122      * @see #setLabel(String)
 123      */
 124     String label;
 125 
 126     /**
 127      * This field indicates the command that has been issued
 128      * by a  particular menu item.
 129      * By default the <code>actionCommand</code>
 130      * is the label of the menu item, unless it has been
 131      * set using setActionCommand.
 132      *
 133      * @serial
 134      * @see #setActionCommand(String)
 135      * @see #getActionCommand()
 136      */
 137     String actionCommand;
 138 
 139     /**
 140      * The eventMask is ONLY set by subclasses via enableEvents.
 141      * The mask should NOT be set when listeners are registered
 142      * so that we can distinguish the difference between when
 143      * listeners request events and subclasses request them.
 144      *
 145      * @serial
 146      */
 147     long eventMask;
 148 
 149     transient ActionListener actionListener;
 150 
 151     /**
 152      * A sequence of key stokes that ia associated with
 153      * a menu item.
 154      * Note :in 1.1.2 you must use setActionCommand()
 155      * on a menu item in order for its shortcut to
 156      * work.
 157      *
 158      * @serial
 159      * @see #getShortcut()
 160      * @see #setShortcut(MenuShortcut)
 161      * @see #deleteShortcut()
 162      */
 163     private MenuShortcut shortcut = null;
 164 
 165     private static final String base = "menuitem";
 166     private static int nameCounter = 0;
 167 
 168     /*
 169      * JDK 1.1 serialVersionUID
 170      */
 171     private static final long serialVersionUID = -21757335363267194L;
 172 
 173     /**
 174      * Constructs a new MenuItem with an empty label and no keyboard
 175      * shortcut.
 176      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 177      * returns true.
 178      * @see java.awt.GraphicsEnvironment#isHeadless
 179      * @since    1.1
 180      */
 181     public MenuItem() throws HeadlessException {
 182         this("", null);
 183     }
 184 
 185     /**
 186      * Constructs a new MenuItem with the specified label
 187      * and no keyboard shortcut. Note that use of "-" in
 188      * a label is reserved to indicate a separator between
 189      * menu items. By default, all menu items except for
 190      * separators are enabled.
 191      * @param       label the label for this menu item.
 192      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 193      * returns true.
 194      * @see java.awt.GraphicsEnvironment#isHeadless
 195      * @since       1.0
 196      */
 197     public MenuItem(String label) throws HeadlessException {
 198         this(label, null);
 199     }
 200 
 201     /**
 202      * Create a menu item with an associated keyboard shortcut.
 203      * Note that use of "-" in a label is reserved to indicate
 204      * a separator between menu items. By default, all menu
 205      * items except for separators are enabled.
 206      * @param       label the label for this menu item.
 207      * @param       s the instance of <code>MenuShortcut</code>
 208      *                       associated with this menu item.
 209      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
 210      * returns true.
 211      * @see java.awt.GraphicsEnvironment#isHeadless
 212      * @since       1.1
 213      */
 214     public MenuItem(String label, MenuShortcut s) throws HeadlessException {
 215         this.label = label;
 216         this.shortcut = s;
 217     }
 218 
 219     /**
 220      * Construct a name for this MenuComponent.  Called by getName() when
 221      * the name is null.
 222      */
 223     String constructComponentName() {
 224         synchronized (MenuItem.class) {
 225             return base + nameCounter++;
 226         }
 227     }
 228 
 229     /**
 230      * Creates the menu item's peer.  The peer allows us to modify the
 231      * appearance of the menu item without changing its functionality.
 232      */
 233     public void addNotify() {
 234         synchronized (getTreeLock()) {
 235             if (peer == null)
 236                 peer = getComponentFactory().createMenuItem(this);
 237         }
 238     }
 239 
 240     /**
 241      * Gets the label for this menu item.
 242      * @return  the label of this menu item, or <code>null</code>
 243                        if this menu item has no label.
 244      * @see     java.awt.MenuItem#setLabel
 245      * @since   1.0
 246      */
 247     public String getLabel() {
 248         return label;
 249     }
 250 
 251     /**
 252      * Sets the label for this menu item to the specified label.
 253      * @param     label   the new label, or <code>null</code> for no label.
 254      * @see       java.awt.MenuItem#getLabel
 255      * @since     1.0
 256      */
 257     public synchronized void setLabel(String label) {
 258         this.label = label;
 259         MenuItemPeer peer = (MenuItemPeer)this.peer;
 260         if (peer != null) {
 261             peer.setLabel(label);
 262         }
 263     }
 264 
 265     /**
 266      * Checks whether this menu item is enabled.
 267      *
 268      * @return {@code true} if the item is enabled;
 269      *         otherwise {@code false}
 270      * @see        java.awt.MenuItem#setEnabled
 271      * @since      1.0
 272      */
 273     public boolean isEnabled() {
 274         return enabled;
 275     }
 276 
 277     /**
 278      * Sets whether or not this menu item can be chosen.
 279      * @param      b  if <code>true</code>, enables this menu item;
 280      *                       if <code>false</code>, disables it.
 281      * @see        java.awt.MenuItem#isEnabled
 282      * @since      1.1
 283      */
 284     public synchronized void setEnabled(boolean b) {
 285         enable(b);
 286     }
 287 
 288     /**
 289      * @deprecated As of JDK version 1.1,
 290      * replaced by <code>setEnabled(boolean)</code>.
 291      */
 292     @Deprecated
 293     public synchronized void enable() {
 294         enabled = true;
 295         MenuItemPeer peer = (MenuItemPeer)this.peer;
 296         if (peer != null) {
 297             peer.setEnabled(true);
 298         }
 299     }
 300 
 301     /**
 302      * Sets whether or not this menu item can be chosen.
 303      *
 304      * @param  b if {@code true}, enables this menu item;
 305      *           otherwise disables
 306      * @deprecated As of JDK version 1.1,
 307      * replaced by <code>setEnabled(boolean)</code>.
 308      */
 309     @Deprecated
 310     public void enable(boolean b) {
 311         if (b) {
 312             enable();
 313         } else {
 314             disable();
 315         }
 316     }
 317 
 318     /**
 319      * @deprecated As of JDK version 1.1,
 320      * replaced by <code>setEnabled(boolean)</code>.
 321      */
 322     @Deprecated
 323     public synchronized void disable() {
 324         enabled = false;
 325         MenuItemPeer peer = (MenuItemPeer)this.peer;
 326         if (peer != null) {
 327             peer.setEnabled(false);
 328         }
 329     }
 330 
 331     /**
 332      * Get the <code>MenuShortcut</code> object associated with this
 333      * menu item,
 334      * @return      the menu shortcut associated with this menu item,
 335      *                   or <code>null</code> if none has been specified.
 336      * @see         java.awt.MenuItem#setShortcut
 337      * @since       1.1
 338      */
 339     public MenuShortcut getShortcut() {
 340         return shortcut;
 341     }
 342 
 343     /**
 344      * Set the <code>MenuShortcut</code> object associated with this
 345      * menu item. If a menu shortcut is already associated with
 346      * this menu item, it is replaced.
 347      * @param       s  the menu shortcut to associate
 348      *                           with this menu item.
 349      * @see         java.awt.MenuItem#getShortcut
 350      * @since       1.1
 351      */
 352     public void setShortcut(MenuShortcut s) {
 353         shortcut = s;
 354         MenuItemPeer peer = (MenuItemPeer)this.peer;
 355         if (peer != null) {
 356             peer.setLabel(label);
 357         }
 358     }
 359 
 360     /**
 361      * Delete any <code>MenuShortcut</code> object associated
 362      * with this menu item.
 363      * @since      1.1
 364      */
 365     public void deleteShortcut() {
 366         shortcut = null;
 367         MenuItemPeer peer = (MenuItemPeer)this.peer;
 368         if (peer != null) {
 369             peer.setLabel(label);
 370         }
 371     }
 372 
 373     /*
 374      * Delete a matching MenuShortcut associated with this MenuItem.
 375      * Used when iterating Menus.
 376      */
 377     void deleteShortcut(MenuShortcut s) {
 378         if (s.equals(shortcut)) {
 379             shortcut = null;
 380             MenuItemPeer peer = (MenuItemPeer)this.peer;
 381             if (peer != null) {
 382                 peer.setLabel(label);
 383             }
 384         }
 385     }
 386 
 387     /*
 388      * The main goal of this method is to post an appropriate event
 389      * to the event queue when menu shortcut is pressed. However,
 390      * in subclasses this method may do more than just posting
 391      * an event.
 392      */
 393     void doMenuEvent(long when, int modifiers) {
 394         Toolkit.getEventQueue().postEvent(
 395             new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
 396                             getActionCommand(), when, modifiers));
 397     }
 398 
 399     /*
 400      * Returns true if the item and all its ancestors are
 401      * enabled, false otherwise
 402      */
 403     private final boolean isItemEnabled() {
 404         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 405         // should be disabled when the menu itself is disabled
 406         if (!isEnabled()) {
 407             return false;
 408         }
 409         MenuContainer container = getParent_NoClientCode();
 410         do {
 411             if (!(container instanceof Menu)) {
 412                 return true;
 413             }
 414             Menu menu = (Menu)container;
 415             if (!menu.isEnabled()) {
 416                 return false;
 417             }
 418             container = menu.getParent_NoClientCode();
 419         } while (container != null);
 420         return true;
 421     }
 422 
 423     /*
 424      * Post an ActionEvent to the target (on
 425      * keydown) and the item is enabled.
 426      * Returns true if there is an associated shortcut.
 427      */
 428     boolean handleShortcut(KeyEvent e) {
 429         MenuShortcut s = new MenuShortcut(e.getKeyCode(),
 430                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 431         MenuShortcut sE = new MenuShortcut(e.getExtendedKeyCode(),
 432                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 433         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 434         // should be disabled when the menu itself is disabled
 435         if ((s.equals(shortcut) || sE.equals(shortcut)) && isItemEnabled()) {
 436             // MenuShortcut match -- issue an event on keydown.
 437             if (e.getID() == KeyEvent.KEY_PRESSED) {
 438                 doMenuEvent(e.getWhen(), e.getModifiers());
 439             } else {
 440                 // silently eat key release.
 441             }
 442             return true;
 443         }
 444         return false;
 445     }
 446 
 447     MenuItem getShortcutMenuItem(MenuShortcut s) {
 448         return (s.equals(shortcut)) ? this : null;
 449     }
 450 
 451     /**
 452      * Enables event delivery to this menu item for events
 453      * to be defined by the specified event mask parameter
 454      * <p>
 455      * Since event types are automatically enabled when a listener for
 456      * that type is added to the menu item, this method only needs
 457      * to be invoked by subclasses of <code>MenuItem</code> which desire to
 458      * have the specified event types delivered to <code>processEvent</code>
 459      * regardless of whether a listener is registered.
 460      *
 461      * @param       eventsToEnable the event mask defining the event types
 462      * @see         java.awt.MenuItem#processEvent
 463      * @see         java.awt.MenuItem#disableEvents
 464      * @see         java.awt.Component#enableEvents
 465      * @since       1.1
 466      */
 467     protected final void enableEvents(long eventsToEnable) {
 468         eventMask |= eventsToEnable;
 469         newEventsOnly = true;
 470     }
 471 
 472     /**
 473      * Disables event delivery to this menu item for events
 474      * defined by the specified event mask parameter.
 475      *
 476      * @param       eventsToDisable the event mask defining the event types
 477      * @see         java.awt.MenuItem#processEvent
 478      * @see         java.awt.MenuItem#enableEvents
 479      * @see         java.awt.Component#disableEvents
 480      * @since       1.1
 481      */
 482     protected final void disableEvents(long eventsToDisable) {
 483         eventMask &= ~eventsToDisable;
 484     }
 485 
 486     /**
 487      * Sets the command name of the action event that is fired
 488      * by this menu item.
 489      * <p>
 490      * By default, the action command is set to the label of
 491      * the menu item.
 492      * @param       command   the action command to be set
 493      *                                for this menu item.
 494      * @see         java.awt.MenuItem#getActionCommand
 495      * @since       1.1
 496      */
 497     public void setActionCommand(String command) {
 498         actionCommand = command;
 499     }
 500 
 501     /**
 502      * Gets the command name of the action event that is fired
 503      * by this menu item.
 504      *
 505      * @return the action command name
 506      * @see java.awt.MenuItem#setActionCommand
 507      * @since 1.1
 508      */
 509     public String getActionCommand() {
 510         return getActionCommandImpl();
 511     }
 512 
 513     // This is final so it can be called on the Toolkit thread.
 514     final String getActionCommandImpl() {
 515         return (actionCommand == null? label : actionCommand);
 516     }
 517 
 518     /**
 519      * Adds the specified action listener to receive action events
 520      * from this menu item.
 521      * If l is null, no exception is thrown and no action is performed.
 522      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 523      * >AWT Threading Issues</a> for details on AWT's threading model.
 524      *
 525      * @param      l the action listener.
 526      * @see        #removeActionListener
 527      * @see        #getActionListeners
 528      * @see        java.awt.event.ActionEvent
 529      * @see        java.awt.event.ActionListener
 530      * @since      1.1
 531      */
 532     public synchronized void addActionListener(ActionListener l) {
 533         if (l == null) {
 534             return;
 535         }
 536         actionListener = AWTEventMulticaster.add(actionListener, l);
 537         newEventsOnly = true;
 538     }
 539 
 540     /**
 541      * Removes the specified action listener so it no longer receives
 542      * action events from this menu item.
 543      * If l is null, no exception is thrown and no action is performed.
 544      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 545      * >AWT Threading Issues</a> for details on AWT's threading model.
 546      *
 547      * @param      l the action listener.
 548      * @see        #addActionListener
 549      * @see        #getActionListeners
 550      * @see        java.awt.event.ActionEvent
 551      * @see        java.awt.event.ActionListener
 552      * @since      1.1
 553      */
 554     public synchronized void removeActionListener(ActionListener l) {
 555         if (l == null) {
 556             return;
 557         }
 558         actionListener = AWTEventMulticaster.remove(actionListener, l);
 559     }
 560 
 561     /**
 562      * Returns an array of all the action listeners
 563      * registered on this menu item.
 564      *
 565      * @return all of this menu item's <code>ActionListener</code>s
 566      *         or an empty array if no action
 567      *         listeners are currently registered
 568      *
 569      * @see        #addActionListener
 570      * @see        #removeActionListener
 571      * @see        java.awt.event.ActionEvent
 572      * @see        java.awt.event.ActionListener
 573      * @since 1.4
 574      */
 575     public synchronized ActionListener[] getActionListeners() {
 576         return getListeners(ActionListener.class);
 577     }
 578 
 579     /**
 580      * Returns an array of all the objects currently registered
 581      * as <code><em>Foo</em>Listener</code>s
 582      * upon this <code>MenuItem</code>.
 583      * <code><em>Foo</em>Listener</code>s are registered using the
 584      * <code>add<em>Foo</em>Listener</code> method.
 585      *
 586      * <p>
 587      * You can specify the <code>listenerType</code> argument
 588      * with a class literal, such as
 589      * <code><em>Foo</em>Listener.class</code>.
 590      * For example, you can query a
 591      * <code>MenuItem</code> <code>m</code>
 592      * for its action listeners with the following code:
 593      *
 594      * <pre>ActionListener[] als = (ActionListener[])(m.getListeners(ActionListener.class));</pre>
 595      *
 596      * If no such listeners exist, this method returns an empty array.
 597      *
 598      * @param <T> the type of the listeners
 599      * @param listenerType the type of listeners requested; this parameter
 600      *          should specify an interface that descends from
 601      *          <code>java.util.EventListener</code>
 602      * @return an array of all objects registered as
 603      *          <code><em>Foo</em>Listener</code>s on this menu item,
 604      *          or an empty array if no such
 605      *          listeners have been added
 606      * @exception ClassCastException if <code>listenerType</code>
 607      *          doesn't specify a class or interface that implements
 608      *          <code>java.util.EventListener</code>
 609      *
 610      * @see #getActionListeners
 611      * @since 1.3
 612      */
 613     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 614         EventListener l = null;
 615         if  (listenerType == ActionListener.class) {
 616             l = actionListener;
 617         }
 618         return AWTEventMulticaster.getListeners(l, listenerType);
 619     }
 620 
 621     /**
 622      * Processes events on this menu item. If the event is an
 623      * instance of <code>ActionEvent</code>, it invokes
 624      * <code>processActionEvent</code>, another method
 625      * defined by <code>MenuItem</code>.
 626      * <p>
 627      * Currently, menu items only support action events.
 628      * <p>Note that if the event parameter is <code>null</code>
 629      * the behavior is unspecified and may result in an
 630      * exception.
 631      *
 632      * @param       e the event
 633      * @see         java.awt.MenuItem#processActionEvent
 634      * @since       1.1
 635      */
 636     protected void processEvent(AWTEvent e) {
 637         if (e instanceof ActionEvent) {
 638             processActionEvent((ActionEvent)e);
 639         }
 640     }
 641 
 642     // REMIND: remove when filtering is done at lower level
 643     boolean eventEnabled(AWTEvent e) {
 644         if (e.id == ActionEvent.ACTION_PERFORMED) {
 645             if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0 ||
 646                 actionListener != null) {
 647                 return true;
 648             }
 649             return false;
 650         }
 651         return super.eventEnabled(e);
 652     }
 653 
 654     /**
 655      * Processes action events occurring on this menu item,
 656      * by dispatching them to any registered
 657      * <code>ActionListener</code> objects.
 658      * This method is not called unless action events are
 659      * enabled for this component. Action events are enabled
 660      * when one of the following occurs:
 661      * <ul>
 662      * <li>An <code>ActionListener</code> object is registered
 663      * via <code>addActionListener</code>.
 664      * <li>Action events are enabled via <code>enableEvents</code>.
 665      * </ul>
 666      * <p>Note that if the event parameter is <code>null</code>
 667      * the behavior is unspecified and may result in an
 668      * exception.
 669      *
 670      * @param       e the action event
 671      * @see         java.awt.event.ActionEvent
 672      * @see         java.awt.event.ActionListener
 673      * @see         java.awt.MenuItem#enableEvents
 674      * @since       1.1
 675      */
 676     protected void processActionEvent(ActionEvent e) {
 677         ActionListener listener = actionListener;
 678         if (listener != null) {
 679             listener.actionPerformed(e);
 680         }
 681     }
 682 
 683     /**
 684      * Returns a string representing the state of this <code>MenuItem</code>.
 685      * This method is intended to be used only for debugging purposes, and the
 686      * content and format of the returned string may vary between
 687      * implementations. The returned string may be empty but may not be
 688      * <code>null</code>.
 689      *
 690      * @return the parameter string of this menu item
 691      */
 692     public String paramString() {
 693         String str = ",label=" + label;
 694         if (shortcut != null) {
 695             str += ",shortcut=" + shortcut;
 696         }
 697         return super.paramString() + str;
 698     }
 699 
 700 
 701     /* Serialization support.
 702      */
 703 
 704     /**
 705      * Menu item serialized data version.
 706      *
 707      * @serial
 708      */
 709     private int menuItemSerializedDataVersion = 1;
 710 
 711     /**
 712      * Writes default serializable fields to stream.  Writes
 713      * a list of serializable <code>ActionListeners</code>
 714      * as optional data. The non-serializable listeners are
 715      * detected and no attempt is made to serialize them.
 716      *
 717      * @param s the <code>ObjectOutputStream</code> to write
 718      * @serialData <code>null</code> terminated sequence of 0
 719      *   or more pairs; the pair consists of a <code>String</code>
 720      *   and an <code>Object</code>; the <code>String</code>
 721      *   indicates the type of object and is one of the following:
 722      *   <code>actionListenerK</code> indicating an
 723      *     <code>ActionListener</code> object
 724      *
 725      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
 726      * @see #readObject(ObjectInputStream)
 727      */
 728     private void writeObject(ObjectOutputStream s)
 729       throws IOException
 730     {
 731       s.defaultWriteObject();
 732 
 733       AWTEventMulticaster.save(s, actionListenerK, actionListener);
 734       s.writeObject(null);
 735     }
 736 
 737     /**
 738      * Reads the <code>ObjectInputStream</code> and if it
 739      * isn't <code>null</code> adds a listener to receive
 740      * action events fired by the <code>Menu</code> Item.
 741      * Unrecognized keys or values will be ignored.
 742      *
 743      * @param s the <code>ObjectInputStream</code> to read
 744      * @exception HeadlessException if
 745      *   <code>GraphicsEnvironment.isHeadless</code> returns
 746      *   <code>true</code>
 747      * @see #removeActionListener(ActionListener)
 748      * @see #addActionListener(ActionListener)
 749      * @see #writeObject(ObjectOutputStream)
 750      */
 751     private void readObject(ObjectInputStream s)
 752       throws ClassNotFoundException, IOException, HeadlessException
 753     {
 754       // HeadlessException will be thrown from MenuComponent's readObject
 755       s.defaultReadObject();
 756 
 757       Object keyOrNull;
 758       while(null != (keyOrNull = s.readObject())) {
 759         String key = ((String)keyOrNull).intern();
 760 
 761         if (actionListenerK == key)
 762           addActionListener((ActionListener)(s.readObject()));
 763 
 764         else // skip value for unrecognized key
 765           s.readObject();
 766       }
 767     }
 768 
 769     /**
 770      * Initialize JNI field and method IDs
 771      */
 772     private static native void initIDs();
 773 
 774 
 775 /////////////////
 776 // Accessibility support
 777 ////////////////
 778 
 779     /**
 780      * Gets the AccessibleContext associated with this MenuItem.
 781      * For menu items, the AccessibleContext takes the form of an
 782      * AccessibleAWTMenuItem.
 783      * A new AccessibleAWTMenuItem instance is created if necessary.
 784      *
 785      * @return an AccessibleAWTMenuItem that serves as the
 786      *         AccessibleContext of this MenuItem
 787      * @since 1.3
 788      */
 789     public AccessibleContext getAccessibleContext() {
 790         if (accessibleContext == null) {
 791             accessibleContext = new AccessibleAWTMenuItem();
 792         }
 793         return accessibleContext;
 794     }
 795 
 796     /**
 797      * Inner class of MenuItem used to provide default support for
 798      * accessibility.  This class is not meant to be used directly by
 799      * application developers, but is instead meant only to be
 800      * subclassed by menu component developers.
 801      * <p>
 802      * This class implements accessibility support for the
 803      * <code>MenuItem</code> class.  It provides an implementation of the
 804      * Java Accessibility API appropriate to menu item user-interface elements.
 805      * @since 1.3
 806      */
 807     protected class AccessibleAWTMenuItem extends AccessibleAWTMenuComponent
 808         implements AccessibleAction, AccessibleValue
 809     {
 810         /*
 811          * JDK 1.3 serialVersionUID
 812          */
 813         private static final long serialVersionUID = -217847831945965825L;
 814 
 815         /**
 816          * Get the accessible name of this object.
 817          *
 818          * @return the localized name of the object -- can be null if this
 819          * object does not have a name
 820          */
 821         public String getAccessibleName() {
 822             if (accessibleName != null) {
 823                 return accessibleName;
 824             } else {
 825                 if (getLabel() == null) {
 826                     return super.getAccessibleName();
 827                 } else {
 828                     return getLabel();
 829                 }
 830             }
 831         }
 832 
 833         /**
 834          * Get the role of this object.
 835          *
 836          * @return an instance of AccessibleRole describing the role of the
 837          * object
 838          */
 839         public AccessibleRole getAccessibleRole() {
 840             return AccessibleRole.MENU_ITEM;
 841         }
 842 
 843         /**
 844          * Get the AccessibleAction associated with this object.  In the
 845          * implementation of the Java Accessibility API for this class,
 846          * return this object, which is responsible for implementing the
 847          * AccessibleAction interface on behalf of itself.
 848          *
 849          * @return this object
 850          */
 851         public AccessibleAction getAccessibleAction() {
 852             return this;
 853         }
 854 
 855         /**
 856          * Get the AccessibleValue associated with this object.  In the
 857          * implementation of the Java Accessibility API for this class,
 858          * return this object, which is responsible for implementing the
 859          * AccessibleValue interface on behalf of itself.
 860          *
 861          * @return this object
 862          */
 863         public AccessibleValue getAccessibleValue() {
 864             return this;
 865         }
 866 
 867         /**
 868          * Returns the number of Actions available in this object.  The
 869          * default behavior of a menu item is to have one action.
 870          *
 871          * @return 1, the number of Actions in this object
 872          */
 873         public int getAccessibleActionCount() {
 874             return 1;
 875         }
 876 
 877         /**
 878          * Return a description of the specified action of the object.
 879          *
 880          * @param i zero-based index of the actions
 881          */
 882         public String getAccessibleActionDescription(int i) {
 883             if (i == 0) {
 884                 // [[[PENDING:  WDW -- need to provide a localized string]]]
 885                 return "click";
 886             } else {
 887                 return null;
 888             }
 889         }
 890 
 891         /**
 892          * Perform the specified Action on the object
 893          *
 894          * @param i zero-based index of actions
 895          * @return true if the action was performed; otherwise false.
 896          */
 897         public boolean doAccessibleAction(int i) {
 898             if (i == 0) {
 899                 // Simulate a button click
 900                 Toolkit.getEventQueue().postEvent(
 901                         new ActionEvent(MenuItem.this,
 902                                         ActionEvent.ACTION_PERFORMED,
 903                                         MenuItem.this.getActionCommand(),
 904                                         EventQueue.getMostRecentEventTime(),
 905                                         0));
 906                 return true;
 907             } else {
 908                 return false;
 909             }
 910         }
 911 
 912         /**
 913          * Get the value of this object as a Number.
 914          *
 915          * @return An Integer of 0 if this isn't selected or an Integer of 1 if
 916          * this is selected.
 917          * @see javax.swing.AbstractButton#isSelected()
 918          */
 919         public Number getCurrentAccessibleValue() {
 920             return Integer.valueOf(0);
 921         }
 922 
 923         /**
 924          * Set the value of this object as a Number.
 925          *
 926          * @return True if the value was set.
 927          */
 928         public boolean setCurrentAccessibleValue(Number n) {
 929             return false;
 930         }
 931 
 932         /**
 933          * Get the minimum value of this object as a Number.
 934          *
 935          * @return An Integer of 0.
 936          */
 937         public Number getMinimumAccessibleValue() {
 938             return Integer.valueOf(0);
 939         }
 940 
 941         /**
 942          * Get the maximum value of this object as a Number.
 943          *
 944          * @return An Integer of 0.
 945          */
 946         public Number getMaximumAccessibleValue() {
 947             return Integer.valueOf(0);
 948         }
 949 
 950     } // class AccessibleAWTMenuItem
 951 
 952 }