1 /*
2  * Copyright (c) 1995, 2017, 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 */
Toolkit.loadLibraries()85         Toolkit.loadLibraries();
86         if (!GraphicsEnvironment.isHeadless()) {
initIDs()87             initIDs();
88         }
89 
AWTAccessor.setMenuItemAccessor( new AWTAccessor.MenuItemAccessor() { public boolean isEnabled(MenuItem item) { return item.enabled; } public String getLabel(MenuItem item) { return item.label; } public MenuShortcut getShortcut(MenuItem item) { return item.shortcut; } public String getActionCommandImpl(MenuItem item) { return item.getActionCommandImpl(); } public boolean isItemEnabled(MenuItem item) { return item.isItemEnabled(); } })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      */
MenuItem()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      */
MenuItem(String label)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      */
MenuItem(String label, MenuShortcut s)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      */
constructComponentName()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      */
addNotify()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      */
getLabel()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      */
setLabel(String label)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      */
isEnabled()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      */
setEnabled(boolean b)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
enable()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
enable(boolean b)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
disable()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      */
getShortcut()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      */
setShortcut(MenuShortcut s)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      */
deleteShortcut()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      */
deleteShortcut(MenuShortcut s)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      */
doMenuEvent(long when, int modifiers)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      */
isItemEnabled()413     private 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")
handleShortcut(KeyEvent e)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 
getShortcutMenuItem(MenuShortcut s)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      */
enableEvents(long eventsToEnable)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      */
disableEvents(long eventsToDisable)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      */
setActionCommand(String command)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      */
getActionCommand()520     public String getActionCommand() {
521         return getActionCommandImpl();
522     }
523 
524     // This is final so it can be called on the Toolkit thread.
getActionCommandImpl()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      */
addActionListener(ActionListener l)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      */
removeActionListener(ActionListener l)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      */
getActionListeners()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      */
getListeners(Class<T> listenerType)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      */
processEvent(AWTEvent e)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
eventEnabled(AWTEvent e)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      */
processActionEvent(ActionEvent e)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      */
paramString()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      */
writeObject(ObjectOutputStream s)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      */
readObject(ObjectInputStream s)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      */
initIDs()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      */
getAccessibleContext()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          */
getAccessibleName()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          */
getAccessibleRole()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          */
getAccessibleAction()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          */
getAccessibleValue()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          */
getAccessibleActionCount()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          */
getAccessibleActionDescription(int i)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          */
doAccessibleAction(int i)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          */
getCurrentAccessibleValue()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          */
setCurrentAccessibleValue(Number n)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          */
getMinimumAccessibleValue()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          */
getMaximumAccessibleValue()957         public Number getMaximumAccessibleValue() {
958             return Integer.valueOf(0);
959         }
960 
961     } // class AccessibleAWTMenuItem
962 
963 }
964