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