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