1 /*
   2  * Copyright (c) 1995, 2014, 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 tha 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    JDK1.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       JDK1.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       JDK1.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 = Toolkit.getDefaultToolkit().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   JDK1.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     JDK1.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      * @return     {@code true} if the item is enabled;
 268      *             otherwise {@code false}
 269      * @see        java.awt.MenuItem#setEnabled
 270      * @since      JDK1.0
 271      */
 272     public boolean isEnabled() {
 273         return enabled;
 274     }
 275 
 276     /**
 277      * Sets whether or not this menu item can be chosen.
 278      * @param      b  if <code>true</code>, enables this menu item;
 279      *                       if <code>false</code>, disables it.
 280      * @see        java.awt.MenuItem#isEnabled
 281      * @since      JDK1.1
 282      */
 283     public synchronized void setEnabled(boolean b) {
 284         enable(b);
 285     }
 286 
 287     /**
 288      * @deprecated As of JDK version 1.1,
 289      * replaced by <code>setEnabled(boolean)</code>.
 290      */
 291     @Deprecated
 292     public synchronized void enable() {
 293         enabled = true;
 294         MenuItemPeer peer = (MenuItemPeer)this.peer;
 295         if (peer != null) {
 296             peer.setEnabled(true);
 297         }
 298     }
 299 
 300     /**
 301      * Sets whether or not this menu item can be chosen.
 302      * @param b  if {@code true}, enables this menu item;
 303      *           otherwise {@code false}
 304      * @deprecated As of JDK version 1.1,
 305      * replaced by <code>setEnabled(boolean)</code>.
 306      */
 307     @Deprecated
 308     public void enable(boolean b) {
 309         if (b) {
 310             enable();
 311         } else {
 312             disable();
 313         }
 314     }
 315 
 316     /**
 317      * @deprecated As of JDK version 1.1,
 318      * replaced by <code>setEnabled(boolean)</code>.
 319      */
 320     @Deprecated
 321     public synchronized void disable() {
 322         enabled = false;
 323         MenuItemPeer peer = (MenuItemPeer)this.peer;
 324         if (peer != null) {
 325             peer.setEnabled(false);
 326         }
 327     }
 328 
 329     /**
 330      * Get the <code>MenuShortcut</code> object associated with this
 331      * menu item,
 332      * @return      the menu shortcut associated with this menu item,
 333      *                   or <code>null</code> if none has been specified.
 334      * @see         java.awt.MenuItem#setShortcut
 335      * @since       JDK1.1
 336      */
 337     public MenuShortcut getShortcut() {
 338         return shortcut;
 339     }
 340 
 341     /**
 342      * Set the <code>MenuShortcut</code> object associated with this
 343      * menu item. If a menu shortcut is already associated with
 344      * this menu item, it is replaced.
 345      * @param       s  the menu shortcut to associate
 346      *                           with this menu item.
 347      * @see         java.awt.MenuItem#getShortcut
 348      * @since       JDK1.1
 349      */
 350     public void setShortcut(MenuShortcut s) {
 351         shortcut = s;
 352         MenuItemPeer peer = (MenuItemPeer)this.peer;
 353         if (peer != null) {
 354             peer.setLabel(label);
 355         }
 356     }
 357 
 358     /**
 359      * Delete any <code>MenuShortcut</code> object associated
 360      * with this menu item.
 361      * @since      JDK1.1
 362      */
 363     public void deleteShortcut() {
 364         shortcut = null;
 365         MenuItemPeer peer = (MenuItemPeer)this.peer;
 366         if (peer != null) {
 367             peer.setLabel(label);
 368         }
 369     }
 370 
 371     /*
 372      * Delete a matching MenuShortcut associated with this MenuItem.
 373      * Used when iterating Menus.
 374      */
 375     void deleteShortcut(MenuShortcut s) {
 376         if (s.equals(shortcut)) {
 377             shortcut = null;
 378             MenuItemPeer peer = (MenuItemPeer)this.peer;
 379             if (peer != null) {
 380                 peer.setLabel(label);
 381             }
 382         }
 383     }
 384 
 385     /*
 386      * The main goal of this method is to post an appropriate event
 387      * to the event queue when menu shortcut is pressed. However,
 388      * in subclasses this method may do more than just posting
 389      * an event.
 390      */
 391     void doMenuEvent(long when, int modifiers) {
 392         Toolkit.getEventQueue().postEvent(
 393             new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
 394                             getActionCommand(), when, modifiers));
 395     }
 396 
 397     /*
 398      * Returns true if the item and all its ancestors are
 399      * enabled, false otherwise
 400      */
 401     private final boolean isItemEnabled() {
 402         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 403         // should be disabled when the menu itself is disabled
 404         if (!isEnabled()) {
 405             return false;
 406         }
 407         MenuContainer container = getParent_NoClientCode();
 408         do {
 409             if (!(container instanceof Menu)) {
 410                 return true;
 411             }
 412             Menu menu = (Menu)container;
 413             if (!menu.isEnabled()) {
 414                 return false;
 415             }
 416             container = menu.getParent_NoClientCode();
 417         } while (container != null);
 418         return true;
 419     }
 420 
 421     /*
 422      * Post an ActionEvent to the target (on
 423      * keydown) and the item is enabled.
 424      * Returns true if there is an associated shortcut.
 425      */
 426     boolean handleShortcut(KeyEvent e) {
 427         MenuShortcut s = new MenuShortcut(e.getKeyCode(),
 428                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 429         MenuShortcut sE = new MenuShortcut(e.getExtendedKeyCode(),
 430                              (e.getModifiers() & InputEvent.SHIFT_MASK) > 0);
 431         // Fix For 6185151: Menu shortcuts of all menuitems within a menu
 432         // should be disabled when the menu itself is disabled
 433         if ((s.equals(shortcut) || sE.equals(shortcut)) && isItemEnabled()) {
 434             // MenuShortcut match -- issue an event on keydown.
 435             if (e.getID() == KeyEvent.KEY_PRESSED) {
 436                 doMenuEvent(e.getWhen(), e.getModifiers());
 437             } else {
 438                 // silently eat key release.
 439             }
 440             return true;
 441         }
 442         return false;
 443     }
 444 
 445     MenuItem getShortcutMenuItem(MenuShortcut s) {
 446         return (s.equals(shortcut)) ? this : null;
 447     }
 448 
 449     /**
 450      * Enables event delivery to this menu item for events
 451      * to be defined by the specified event mask parameter
 452      * <p>
 453      * Since event types are automatically enabled when a listener for
 454      * that type is added to the menu item, this method only needs
 455      * to be invoked by subclasses of <code>MenuItem</code> which desire to
 456      * have the specified event types delivered to <code>processEvent</code>
 457      * regardless of whether a listener is registered.
 458      *
 459      * @param       eventsToEnable the event mask defining the event types
 460      * @see         java.awt.MenuItem#processEvent
 461      * @see         java.awt.MenuItem#disableEvents
 462      * @see         java.awt.Component#enableEvents
 463      * @since       JDK1.1
 464      */
 465     protected final void enableEvents(long eventsToEnable) {
 466         eventMask |= eventsToEnable;
 467         newEventsOnly = true;
 468     }
 469 
 470     /**
 471      * Disables event delivery to this menu item for events
 472      * defined by the specified event mask parameter.
 473      *
 474      * @param       eventsToDisable the event mask defining the event types
 475      * @see         java.awt.MenuItem#processEvent
 476      * @see         java.awt.MenuItem#enableEvents
 477      * @see         java.awt.Component#disableEvents
 478      * @since       JDK1.1
 479      */
 480     protected final void disableEvents(long eventsToDisable) {
 481         eventMask &= ~eventsToDisable;
 482     }
 483 
 484     /**
 485      * Sets the command name of the action event that is fired
 486      * by this menu item.
 487      * <p>
 488      * By default, the action command is set to the label of
 489      * the menu item.
 490      * @param       command   the action command to be set
 491      *                                for this menu item.
 492      * @see         java.awt.MenuItem#getActionCommand
 493      * @since       JDK1.1
 494      */
 495     public void setActionCommand(String command) {
 496         actionCommand = command;
 497     }
 498 
 499     /**
 500      * Gets the command name of the action event that is fired
 501      * by this menu item.
 502      * @return      the action command name
 503      * @see         java.awt.MenuItem#setActionCommand
 504      * @since       JDK1.1
 505      */
 506     public String getActionCommand() {
 507         return getActionCommandImpl();
 508     }
 509 
 510     // This is final so it can be called on the Toolkit thread.
 511     final String getActionCommandImpl() {
 512         return (actionCommand == null? label : actionCommand);
 513     }
 514 
 515     /**
 516      * Adds the specified action listener to receive action events
 517      * from this menu item.
 518      * If l is null, no exception is thrown and no action is performed.
 519      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 520      * >AWT Threading Issues</a> for details on AWT's threading model.
 521      *
 522      * @param      l the action listener.
 523      * @see        #removeActionListener
 524      * @see        #getActionListeners
 525      * @see        java.awt.event.ActionEvent
 526      * @see        java.awt.event.ActionListener
 527      * @since      JDK1.1
 528      */
 529     public synchronized void addActionListener(ActionListener l) {
 530         if (l == null) {
 531             return;
 532         }
 533         actionListener = AWTEventMulticaster.add(actionListener, l);
 534         newEventsOnly = true;
 535     }
 536 
 537     /**
 538      * Removes the specified action listener so it no longer receives
 539      * action events from this menu item.
 540      * If l is null, no exception is thrown and no action is performed.
 541      * <p>Refer to <a href="doc-files/AWTThreadIssues.html#ListenersThreads"
 542      * >AWT Threading Issues</a> for details on AWT's threading model.
 543      *
 544      * @param      l the action listener.
 545      * @see        #addActionListener
 546      * @see        #getActionListeners
 547      * @see        java.awt.event.ActionEvent
 548      * @see        java.awt.event.ActionListener
 549      * @since      JDK1.1
 550      */
 551     public synchronized void removeActionListener(ActionListener l) {
 552         if (l == null) {
 553             return;
 554         }
 555         actionListener = AWTEventMulticaster.remove(actionListener, l);
 556     }
 557 
 558     /**
 559      * Returns an array of all the action listeners
 560      * registered on this menu item.
 561      *
 562      * @return all of this menu item's <code>ActionListener</code>s
 563      *         or an empty array if no action
 564      *         listeners are currently registered
 565      *
 566      * @see        #addActionListener
 567      * @see        #removeActionListener
 568      * @see        java.awt.event.ActionEvent
 569      * @see        java.awt.event.ActionListener
 570      * @since 1.4
 571      */
 572     public synchronized ActionListener[] getActionListeners() {
 573         return getListeners(ActionListener.class);
 574     }
 575 
 576     /**
 577      * Returns an array of all the objects currently registered
 578      * as <code><em>Foo</em>Listener</code>s
 579      * upon this <code>MenuItem</code>.
 580      * <code><em>Foo</em>Listener</code>s are registered using the
 581      * <code>add<em>Foo</em>Listener</code> method.
 582      *
 583      * <p>
 584      * You can specify the <code>listenerType</code> argument
 585      * with a class literal, such as
 586      * <code><em>Foo</em>Listener.class</code>.
 587      * For example, you can query a
 588      * <code>MenuItem</code> <code>m</code>
 589      * for its action listeners with the following code:
 590      *
 591      * <pre>ActionListener[] als = (ActionListener[])(m.getListeners(ActionListener.class));</pre>
 592      *
 593      * If no such listeners exist, this method returns an empty array.
 594      *
 595      * @param listenerType the type of listeners requested; this parameter
 596      *          should specify an interface that descends from
 597      *          <code>java.util.EventListener</code>
 598      * @return an array of all objects registered as
 599      *          <code><em>Foo</em>Listener</code>s on this menu item,
 600      *          or an empty array if no such
 601      *          listeners have been added
 602      * @exception ClassCastException if <code>listenerType</code>
 603      *          doesn't specify a class or interface that implements
 604      *          <code>java.util.EventListener</code>
 605      *
 606      * @see #getActionListeners
 607      * @since 1.3
 608      */
 609     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
 610         EventListener l = null;
 611         if  (listenerType == ActionListener.class) {
 612             l = actionListener;
 613         }
 614         return AWTEventMulticaster.getListeners(l, listenerType);
 615     }
 616 
 617     /**
 618      * Processes events on this menu item. If the event is an
 619      * instance of <code>ActionEvent</code>, it invokes
 620      * <code>processActionEvent</code>, another method
 621      * defined by <code>MenuItem</code>.
 622      * <p>
 623      * Currently, menu items only support action events.
 624      * <p>Note that if the event parameter is <code>null</code>
 625      * the behavior is unspecified and may result in an
 626      * exception.
 627      *
 628      * @param       e the event
 629      * @see         java.awt.MenuItem#processActionEvent
 630      * @since       JDK1.1
 631      */
 632     protected void processEvent(AWTEvent e) {
 633         if (e instanceof ActionEvent) {
 634             processActionEvent((ActionEvent)e);
 635         }
 636     }
 637 
 638     // REMIND: remove when filtering is done at lower level
 639     boolean eventEnabled(AWTEvent e) {
 640         if (e.id == ActionEvent.ACTION_PERFORMED) {
 641             if ((eventMask & AWTEvent.ACTION_EVENT_MASK) != 0 ||
 642                 actionListener != null) {
 643                 return true;
 644             }
 645             return false;
 646         }
 647         return super.eventEnabled(e);
 648     }
 649 
 650     /**
 651      * Processes action events occurring on this menu item,
 652      * by dispatching them to any registered
 653      * <code>ActionListener</code> objects.
 654      * This method is not called unless action events are
 655      * enabled for this component. Action events are enabled
 656      * when one of the following occurs:
 657      * <ul>
 658      * <li>An <code>ActionListener</code> object is registered
 659      * via <code>addActionListener</code>.
 660      * <li>Action events are enabled via <code>enableEvents</code>.
 661      * </ul>
 662      * <p>Note that if the event parameter is <code>null</code>
 663      * the behavior is unspecified and may result in an
 664      * exception.
 665      *
 666      * @param       e the action event
 667      * @see         java.awt.event.ActionEvent
 668      * @see         java.awt.event.ActionListener
 669      * @see         java.awt.MenuItem#enableEvents
 670      * @since       JDK1.1
 671      */
 672     protected void processActionEvent(ActionEvent e) {
 673         ActionListener listener = actionListener;
 674         if (listener != null) {
 675             listener.actionPerformed(e);
 676         }
 677     }
 678 
 679     /**
 680      * Returns a string representing the state of this <code>MenuItem</code>.
 681      * This method is intended to be used only for debugging purposes, and the
 682      * content and format of the returned string may vary between
 683      * implementations. The returned string may be empty but may not be
 684      * <code>null</code>.
 685      *
 686      * @return the parameter string of this menu item
 687      */
 688     public String paramString() {
 689         String str = ",label=" + label;
 690         if (shortcut != null) {
 691             str += ",shortcut=" + shortcut;
 692         }
 693         return super.paramString() + str;
 694     }
 695 
 696 
 697     /* Serialization support.
 698      */
 699 
 700     /**
 701      * Menu item serialized data version.
 702      *
 703      * @serial
 704      */
 705     private int menuItemSerializedDataVersion = 1;
 706 
 707     /**
 708      * Writes default serializable fields to stream.  Writes
 709      * a list of serializable <code>ActionListeners</code>
 710      * as optional data. The non-serializable listeners are
 711      * detected and no attempt is made to serialize them.
 712      *
 713      * @param s the <code>ObjectOutputStream</code> to write
 714      * @serialData <code>null</code> terminated sequence of 0
 715      *   or more pairs; the pair consists of a <code>String</code>
 716      *   and an <code>Object</code>; the <code>String</code>
 717      *   indicates the type of object and is one of the following:
 718      *   <code>actionListenerK</code> indicating an
 719      *     <code>ActionListener</code> object
 720      *
 721      * @see AWTEventMulticaster#save(ObjectOutputStream, String, EventListener)
 722      * @see #readObject(ObjectInputStream)
 723      */
 724     private void writeObject(ObjectOutputStream s)
 725       throws IOException
 726     {
 727       s.defaultWriteObject();
 728 
 729       AWTEventMulticaster.save(s, actionListenerK, actionListener);
 730       s.writeObject(null);
 731     }
 732 
 733     /**
 734      * Reads the <code>ObjectInputStream</code> and if it
 735      * isn't <code>null</code> adds a listener to receive
 736      * action events fired by the <code>Menu</code> Item.
 737      * Unrecognized keys or values will be ignored.
 738      *
 739      * @param s the <code>ObjectInputStream</code> to read
 740      * @exception HeadlessException if
 741      *   <code>GraphicsEnvironment.isHeadless</code> returns
 742      *   <code>true</code>
 743      * @see #removeActionListener(ActionListener)
 744      * @see #addActionListener(ActionListener)
 745      * @see #writeObject(ObjectOutputStream)
 746      */
 747     private void readObject(ObjectInputStream s)
 748       throws ClassNotFoundException, IOException, HeadlessException
 749     {
 750       // HeadlessException will be thrown from MenuComponent's readObject
 751       s.defaultReadObject();
 752 
 753       Object keyOrNull;
 754       while(null != (keyOrNull = s.readObject())) {
 755         String key = ((String)keyOrNull).intern();
 756 
 757         if (actionListenerK == key)
 758           addActionListener((ActionListener)(s.readObject()));
 759 
 760         else // skip value for unrecognized key
 761           s.readObject();
 762       }
 763     }
 764 
 765     /**
 766      * Initialize JNI field and method IDs
 767      */
 768     private static native void initIDs();
 769 
 770 
 771 /////////////////
 772 // Accessibility support
 773 ////////////////
 774 
 775     /**
 776      * Gets the AccessibleContext associated with this MenuItem.
 777      * For menu items, the AccessibleContext takes the form of an
 778      * AccessibleAWTMenuItem.
 779      * A new AccessibleAWTMenuItem instance is created if necessary.
 780      *
 781      * @return an AccessibleAWTMenuItem that serves as the
 782      *         AccessibleContext of this MenuItem
 783      * @since 1.3
 784      */
 785     public AccessibleContext getAccessibleContext() {
 786         if (accessibleContext == null) {
 787             accessibleContext = new AccessibleAWTMenuItem();
 788         }
 789         return accessibleContext;
 790     }
 791 
 792     /**
 793      * Inner class of MenuItem used to provide default support for
 794      * accessibility.  This class is not meant to be used directly by
 795      * application developers, but is instead meant only to be
 796      * subclassed by menu component developers.
 797      * <p>
 798      * This class implements accessibility support for the
 799      * <code>MenuItem</code> class.  It provides an implementation of the
 800      * Java Accessibility API appropriate to menu item user-interface elements.
 801      * @since 1.3
 802      */
 803     protected class AccessibleAWTMenuItem extends AccessibleAWTMenuComponent
 804         implements AccessibleAction, AccessibleValue
 805     {
 806         /*
 807          * JDK 1.3 serialVersionUID
 808          */
 809         private static final long serialVersionUID = -217847831945965825L;
 810 
 811         /**
 812          * Get the accessible name of this object.
 813          *
 814          * @return the localized name of the object -- can be null if this
 815          * object does not have a name
 816          */
 817         public String getAccessibleName() {
 818             if (accessibleName != null) {
 819                 return accessibleName;
 820             } else {
 821                 if (getLabel() == null) {
 822                     return super.getAccessibleName();
 823                 } else {
 824                     return getLabel();
 825                 }
 826             }
 827         }
 828 
 829         /**
 830          * Get the role of this object.
 831          *
 832          * @return an instance of AccessibleRole describing the role of the
 833          * object
 834          */
 835         public AccessibleRole getAccessibleRole() {
 836             return AccessibleRole.MENU_ITEM;
 837         }
 838 
 839         /**
 840          * Get the AccessibleAction associated with this object.  In the
 841          * implementation of the Java Accessibility API for this class,
 842          * return this object, which is responsible for implementing the
 843          * AccessibleAction interface on behalf of itself.
 844          *
 845          * @return this object
 846          */
 847         public AccessibleAction getAccessibleAction() {
 848             return this;
 849         }
 850 
 851         /**
 852          * Get the AccessibleValue associated with this object.  In the
 853          * implementation of the Java Accessibility API for this class,
 854          * return this object, which is responsible for implementing the
 855          * AccessibleValue interface on behalf of itself.
 856          *
 857          * @return this object
 858          */
 859         public AccessibleValue getAccessibleValue() {
 860             return this;
 861         }
 862 
 863         /**
 864          * Returns the number of Actions available in this object.  The
 865          * default behavior of a menu item is to have one action.
 866          *
 867          * @return 1, the number of Actions in this object
 868          */
 869         public int getAccessibleActionCount() {
 870             return 1;
 871         }
 872 
 873         /**
 874          * Return a description of the specified action of the object.
 875          *
 876          * @param i zero-based index of the actions
 877          */
 878         public String getAccessibleActionDescription(int i) {
 879             if (i == 0) {
 880                 // [[[PENDING:  WDW -- need to provide a localized string]]]
 881                 return "click";
 882             } else {
 883                 return null;
 884             }
 885         }
 886 
 887         /**
 888          * Perform the specified Action on the object
 889          *
 890          * @param i zero-based index of actions
 891          * @return true if the action was performed; otherwise false.
 892          */
 893         public boolean doAccessibleAction(int i) {
 894             if (i == 0) {
 895                 // Simulate a button click
 896                 Toolkit.getEventQueue().postEvent(
 897                         new ActionEvent(MenuItem.this,
 898                                         ActionEvent.ACTION_PERFORMED,
 899                                         MenuItem.this.getActionCommand(),
 900                                         EventQueue.getMostRecentEventTime(),
 901                                         0));
 902                 return true;
 903             } else {
 904                 return false;
 905             }
 906         }
 907 
 908         /**
 909          * Get the value of this object as a Number.
 910          *
 911          * @return An Integer of 0 if this isn't selected or an Integer of 1 if
 912          * this is selected.
 913          * @see javax.swing.AbstractButton#isSelected()
 914          */
 915         public Number getCurrentAccessibleValue() {
 916             return Integer.valueOf(0);
 917         }
 918 
 919         /**
 920          * Set the value of this object as a Number.
 921          *
 922          * @return True if the value was set.
 923          */
 924         public boolean setCurrentAccessibleValue(Number n) {
 925             return false;
 926         }
 927 
 928         /**
 929          * Get the minimum value of this object as a Number.
 930          *
 931          * @return An Integer of 0.
 932          */
 933         public Number getMinimumAccessibleValue() {
 934             return Integer.valueOf(0);
 935         }
 936 
 937         /**
 938          * Get the maximum value of this object as a Number.
 939          *
 940          * @return An Integer of 0.
 941          */
 942         public Number getMaximumAccessibleValue() {
 943             return Integer.valueOf(0);
 944         }
 945 
 946     } // class AccessibleAWTMenuItem
 947 
 948 }